<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Warren Jitsing</title>
    <description>The latest articles on DEV Community by Warren Jitsing (@warren_jitsing_dd1c1d6fc6).</description>
    <link>https://dev.to/warren_jitsing_dd1c1d6fc6</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3658537%2Fef9d7cee-36ac-45c5-9f18-457a9ea3465b.jpeg</url>
      <title>DEV Community: Warren Jitsing</title>
      <link>https://dev.to/warren_jitsing_dd1c1d6fc6</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/warren_jitsing_dd1c1d6fc6"/>
    <language>en</language>
    <item>
      <title>Pingora Guide - How To Make A Programmable API Gateway</title>
      <dc:creator>Warren Jitsing</dc:creator>
      <pubDate>Sun, 25 Jan 2026 06:07:06 +0000</pubDate>
      <link>https://dev.to/warren_jitsing_dd1c1d6fc6/pingora-guide-how-to-make-a-programmable-api-gateway-1oim</link>
      <guid>https://dev.to/warren_jitsing_dd1c1d6fc6/pingora-guide-how-to-make-a-programmable-api-gateway-1oim</guid>
      <description>&lt;p&gt;GitHub: &lt;a href="https://github.com/InfiniteConsult/pingora-guide" rel="noopener noreferrer"&gt;https://github.com/InfiniteConsult/pingora-guide&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reference architecture in src is work-in-progress. Check back next week&lt;/strong&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Pingora Guide
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Quick Start: The "Pingora City" Lab&lt;/li&gt;
&lt;li&gt;Lesson 0: The Raw Event Loop&lt;/li&gt;
&lt;li&gt;Lesson 1: Configuration &amp;amp; Lifecycle&lt;/li&gt;
&lt;li&gt;Lesson 2: Daemon Mode &amp;amp; Background Services&lt;/li&gt;
&lt;li&gt;Lesson 3: Graceful Shutdown&lt;/li&gt;
&lt;li&gt;Lesson 4: Threading Models&lt;/li&gt;
&lt;li&gt;Lesson 5: Background Services &amp;amp; Shared State&lt;/li&gt;
&lt;li&gt;Lesson 6: The Simple Forwarder&lt;/li&gt;
&lt;li&gt;Lesson 7: TLS Termination&lt;/li&gt;
&lt;li&gt;Lesson 8: Header Manipulation&lt;/li&gt;
&lt;li&gt;Lesson 9: Path Routing&lt;/li&gt;
&lt;li&gt;Lesson 10: Query Params&lt;/li&gt;
&lt;li&gt;Lesson 11: Response Modification&lt;/li&gt;
&lt;li&gt;Lesson 12: Body Inspection&lt;/li&gt;
&lt;li&gt;Lesson 13: Custom Errors&lt;/li&gt;
&lt;li&gt;Lesson 14: HTTP/2 Support&lt;/li&gt;
&lt;li&gt;Lesson 15: H2C (HTTP/2 Cleartext)&lt;/li&gt;
&lt;li&gt;Lesson 16: Static Peer&lt;/li&gt;
&lt;li&gt;Lesson 17: DNS Peer&lt;/li&gt;
&lt;li&gt;Lesson 18: Unix Domain Socket (UDS) Peer&lt;/li&gt;
&lt;li&gt;Lesson 19: Peer Options&lt;/li&gt;
&lt;li&gt;Lesson 20: SNI Routing&lt;/li&gt;
&lt;li&gt;Lesson 21: Mutual TLS (mTLS)&lt;/li&gt;
&lt;li&gt;Lesson 22: Proxy Connect (Tunneling)&lt;/li&gt;
&lt;li&gt;Lesson 23: WebSocket Upgrade&lt;/li&gt;
&lt;li&gt;Lesson 24: gRPC Proxy&lt;/li&gt;
&lt;li&gt;Lesson 25: Connection Reuse (Pooling)&lt;/li&gt;
&lt;li&gt;Lesson 26: Round Robin Load Balancing&lt;/li&gt;
&lt;li&gt;Lesson 27: Weighted Load Balancing&lt;/li&gt;
&lt;li&gt;Lesson 28: Consistent Hashing (Ketama)&lt;/li&gt;
&lt;li&gt;Lesson 29: Sticky Sessions (Cookie-Based)&lt;/li&gt;
&lt;li&gt;Lesson 30: TCP Health Checks&lt;/li&gt;
&lt;li&gt;Lesson 31: HTTP Health Checks&lt;/li&gt;
&lt;li&gt;Lesson 32: Custom Health Checks (Body Inspection)&lt;/li&gt;
&lt;li&gt;Lesson 33: Active-Passive Load Balancing (Failover)&lt;/li&gt;
&lt;li&gt;Lesson 34: Service Discovery (File-Based)&lt;/li&gt;
&lt;li&gt;Lesson 35: Dynamic Reconfiguration (Hot-Swap API)&lt;/li&gt;
&lt;li&gt;Lesson 36: Basic Rate Limiting (Fixed Window)&lt;/li&gt;
&lt;li&gt;Lesson 37: Sliding Window Rate Limiting&lt;/li&gt;
&lt;li&gt;Lesson 38: In-flight Concurrency Limiting&lt;/li&gt;
&lt;li&gt;Lesson 39: IP Filtering (Access Control)&lt;/li&gt;
&lt;li&gt;Lesson 40: Request Size Limiting&lt;/li&gt;
&lt;li&gt;Lesson 41: Authentication (Bearer Token)&lt;/li&gt;
&lt;li&gt;Lesson 42: Basic Authentication&lt;/li&gt;
&lt;li&gt;Lesson 43: In-Memory Caching&lt;/li&gt;
&lt;li&gt;Lesson 44: Respecting Cache-Control Headers&lt;/li&gt;
&lt;li&gt;Lesson 45: Cache Locking (Request Coalescing)&lt;/li&gt;
&lt;li&gt;Lesson 46: Cache Purging&lt;/li&gt;
&lt;li&gt;Lesson 47: Stale While Revalidate&lt;/li&gt;
&lt;li&gt;Lesson 48: Observability&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Quick Start: The "Pingora City" Lab
&lt;/h1&gt;

&lt;p&gt;To ensure a consistent environment for these tutorials, we have created a deterministic network topology using Docker Compose. This "Pingora City" simulates a real-world infrastructure with multiple clients, distinct upstreams (HTTP, HTTPS, gRPC, H2C), and a dedicated development station.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Setup &amp;amp; Installation
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites:&lt;/strong&gt; Docker and Docker Compose.&lt;/p&gt;

&lt;p&gt;First, generate the Certificate Authority and service certificates. This populates &lt;code&gt;conf/keys/&lt;/code&gt; with the TLS assets required for the advanced lessons.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Generate Certificates (Root CA, Server, Client, Upstream)&lt;/span&gt;
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x scripts/00-setup-certs.sh
./scripts/00-setup-certs.sh

&lt;span class="c"&gt;# 2. Build and Launch the City&lt;/span&gt;
docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--build&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. The Developer Environment
&lt;/h2&gt;

&lt;p&gt;You do not need Rust installed on your host machine. We have a dedicated &lt;code&gt;dev&lt;/code&gt; container (Debian Bookworm) with a pre-configured Rust toolchain, OpenSSL, and network utilities.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Enter the Dev Container:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; pingora_dev bash

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Verify Compilation (Run Example 05):&lt;/strong&gt;&lt;br&gt;
Once inside, run the "Background Services" example. This acts as a smoke test to ensure &lt;code&gt;cargo&lt;/code&gt; can compile the project and bind to the network.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Inside pingora_dev&lt;/span&gt;
&lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 05_background_services

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Wait for the "Server started" log message, then press &lt;code&gt;Ctrl+C&lt;/code&gt; to exit the process.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verify Internal Network Reachability:&lt;/strong&gt;&lt;br&gt;
We have provided a script to verify that the Dev station can reach all upstream services via both Static IP and DNS.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Inside pingora_dev&lt;/span&gt;
./scripts/validate-dev.sh

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;You should see green &lt;code&gt;OK&lt;/code&gt; statuses for Blue, Green, Advanced (Nginx), and gRPC upstreams.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Type &lt;code&gt;exit&lt;/code&gt; to return to your host terminal.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Verification &amp;amp; Connectivity Test
&lt;/h2&gt;

&lt;p&gt;From your &lt;strong&gt;host machine&lt;/strong&gt;, run the &lt;code&gt;validate-others.sh&lt;/code&gt; script. This automation script will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start &lt;code&gt;example 05_background_services&lt;/code&gt; in the background on the Dev container.&lt;/li&gt;
&lt;li&gt;Instruct &lt;strong&gt;Client 1&lt;/strong&gt; and &lt;strong&gt;Client 2&lt;/strong&gt; to connect to the server.&lt;/li&gt;
&lt;li&gt;Verify that both clients (with distinct IPs) successfully received a response.&lt;/li&gt;
&lt;li&gt;Verify the server logs to confirm traffic handling.&lt;/li&gt;
&lt;li&gt;Send a &lt;code&gt;SIGTERM&lt;/code&gt; to the server to test graceful shutdown.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# On Host&lt;/span&gt;
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x scripts/validate-others.sh
./scripts/validate-others.sh

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Network Topology &amp;amp; Services
&lt;/h2&gt;

&lt;p&gt;The lab runs on a fixed subnet &lt;code&gt;172.28.0.0/24&lt;/code&gt;. All containers mount the &lt;code&gt;conf/keys&lt;/code&gt; directory to trust the local Root CA.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;Hostname&lt;/th&gt;
&lt;th&gt;Static IP&lt;/th&gt;
&lt;th&gt;Role &amp;amp; Features&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dev Station&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;dev.pingora.local&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;172.28.0.10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Your Workstation.&lt;/strong&gt; Rust toolchain, code bind-mount. Runs your Proxy.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Upstream Blue&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;blue.pingora.local&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;172.28.0.20&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Basic HTTP.&lt;/strong&gt; Returns "Response from BLUE" on port 8080.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Upstream Green&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;green.pingora.local&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;172.28.0.21&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Basic HTTP.&lt;/strong&gt; Returns "Response from GREEN" on port 8080.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Advanced Upstream&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;advanced.pingora.local&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;172.28.0.22&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Nginx.&lt;/strong&gt; Supports: &lt;br&gt;&lt;br&gt;• Port 80: HTTP (Caching headers)&lt;br&gt;&lt;br&gt;• Port 443: HTTPS&lt;br&gt;&lt;br&gt;• Port 8443: Mutual TLS (mTLS)&lt;br&gt;&lt;br&gt;• Port 8081: HTTP/2 Cleartext (H2C)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;gRPC Upstream&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;grpc.pingora.local&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;172.28.0.23&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;gRPC.&lt;/strong&gt; &lt;code&gt;grpcbin&lt;/code&gt; server listening on TCP 9000.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Client 1&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;client1.pingora.local&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;172.28.0.30&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Traffic Generator.&lt;/strong&gt; Simulates User A.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Client 2&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;client2.pingora.local&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;172.28.0.31&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Traffic Generator.&lt;/strong&gt; Simulates User B (Useful for IP Rate Limiting).&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h1&gt;
  
  
  Lesson 0: The Raw Event Loop
&lt;/h1&gt;

&lt;p&gt;In this first lesson, we strip away the HTTP layer to understand the heart of Pingora: the &lt;strong&gt;Event Loop&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Pingora is not just an HTTP proxy; it is a generic network server framework. At its lowest level, it manages the lifecycle of a server process, handles configuration, daemonization, and graceful shutdowns. It then delegates the actual handling of traffic to &lt;strong&gt;Services&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We will build a raw TCP Echo Server. This requires implementing the &lt;code&gt;ServerApp&lt;/code&gt; trait, which gives us direct access to the underlying TCP stream before any protocol parsing occurs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Concepts
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Server&lt;/code&gt;&lt;/strong&gt;: The process manager. It owns the main thread, handles signals (like SIGTERM), and manages the worker threads.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Service&lt;/code&gt;&lt;/strong&gt;: A background worker or a listening endpoint. A &lt;code&gt;Server&lt;/code&gt; can run multiple &lt;code&gt;Services&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ServerApp&lt;/code&gt;&lt;/strong&gt;: The logic trait. You implement this to define &lt;em&gt;what&lt;/em&gt; happens when a new connection is established.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Stream&lt;/code&gt;&lt;/strong&gt;: A wrapper around the raw socket (TCP or Unix Domain Socket). It implements &lt;code&gt;AsyncRead&lt;/code&gt; and &lt;code&gt;AsyncWrite&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Code (&lt;code&gt;examples/00_basic_server.rs&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;We will implement a struct &lt;code&gt;EchoApp&lt;/code&gt;. When a client connects, &lt;code&gt;EchoApp&lt;/code&gt; will read bytes from the stream and immediately write them back until the client disconnects or the server shuts down.&lt;/p&gt;

&lt;p&gt;Notice the strict error handling. We avoid &lt;code&gt;unwrap()&lt;/code&gt;. If the server fails to initialize or a socket read fails, we log the error and exit the scope gracefully.&lt;/p&gt;

&lt;p&gt;We also import &lt;code&gt;GetSocketDigest&lt;/code&gt; to access metadata about the connection, such as the peer's IP address.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// examples/00_basic_server.rs&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;protocols&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Stream&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// We need this trait to access connection metadata (IPs, etc.) from the Stream&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;protocols&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;GetSocketDigest&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ShutdownWatch&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;services&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;listening&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;AsyncReadExt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AsyncWriteExt&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="cd"&gt;/// A custom application logic that implements `ServerApp`.&lt;/span&gt;
&lt;span class="cd"&gt;/// This is the lowest level of logic in Pingora, dealing with raw streams.&lt;/span&gt;
&lt;span class="nd"&gt;#[derive(Clone)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;EchoApp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;apps&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ServerApp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;EchoApp&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="cd"&gt;/// This method is called whenever a new TCP connection is established.&lt;/span&gt;
   &lt;span class="cd"&gt;///&lt;/span&gt;
   &lt;span class="cd"&gt;/// # Arguments&lt;/span&gt;
   &lt;span class="cd"&gt;/// * `stream` - The raw TCP/Unix stream (Box&amp;lt;dyn IO&amp;gt;).&lt;/span&gt;
   &lt;span class="cd"&gt;/// * `shutdown` - A watcher to check if the server is requested to stop.&lt;/span&gt;
   &lt;span class="cd"&gt;///&lt;/span&gt;
   &lt;span class="cd"&gt;/// # Returns&lt;/span&gt;
   &lt;span class="cd"&gt;/// * `Some(stream)`: The connection is reusable and should be kept alive.&lt;/span&gt;
   &lt;span class="cd"&gt;/// * `None`: The connection is finished or errored and should be closed.&lt;/span&gt;
   &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;process_new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;shutdown&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;ShutdownWatch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Access the socket digest to get the peer address.&lt;/span&gt;
      &lt;span class="c1"&gt;// We safely check if the digest exists and if the peer address is available.&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="nf"&gt;.get_socket_digest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer_addr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;digest&lt;/span&gt;&lt;span class="nf"&gt;.peer_addr&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"New connection from: {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;peer_addr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
         &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

      &lt;span class="k"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="c1"&gt;// 1. Graceful Shutdown Check&lt;/span&gt;
         &lt;span class="c1"&gt;// We check this every loop to ensure we don't hold connections hostage&lt;/span&gt;
         &lt;span class="c1"&gt;// during a server restart or shutdown.&lt;/span&gt;
         &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;shutdown&lt;/span&gt;&lt;span class="nf"&gt;.borrow&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Server shutting down, closing connection"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
         &lt;span class="p"&gt;}&lt;/span&gt;

         &lt;span class="c1"&gt;// 2. Read data from the stream safely&lt;/span&gt;
         &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;read_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="nf"&gt;.read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

         &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;read_result&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
               &lt;span class="c1"&gt;// 0 bytes read indicates the client closed the connection cleanly.&lt;/span&gt;
               &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Client closed connection"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
               &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
               &lt;span class="c1"&gt;// We successfully read n bytes. Now echo them back.&lt;/span&gt;
               &lt;span class="c1"&gt;// write_all ensures every byte in the buffer is transmitted.&lt;/span&gt;
               &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="nf"&gt;.write_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                  &lt;span class="nd"&gt;error!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to write to stream: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
               &lt;span class="p"&gt;}&lt;/span&gt;

               &lt;span class="c1"&gt;// Flush ensures the data is actually sent over the wire immediately.&lt;/span&gt;
               &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="nf"&gt;.flush&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                  &lt;span class="nd"&gt;error!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to flush stream: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
               &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
               &lt;span class="c1"&gt;// IO errors (broken pipe, reset, etc.) happen here.&lt;/span&gt;
               &lt;span class="nd"&gt;error!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Stream read error: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
               &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
         &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="c1"&gt;// 1. Initialize logging. Pingora uses `log`, so we need an implementation like `env_logger`.&lt;/span&gt;
   &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

   &lt;span class="c1"&gt;// 2. Parse command line options safely.&lt;/span&gt;
   &lt;span class="c1"&gt;// Pingora provides a built-in Clap parser for standard options (-c, -d, --upgrade).&lt;/span&gt;
   &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

   &lt;span class="c1"&gt;// 3. Create the Server instance.&lt;/span&gt;
   &lt;span class="c1"&gt;// This handles process lifecycle, PID files, and configuration loading.&lt;/span&gt;
   &lt;span class="c1"&gt;// We propagate the error up instead of unwrapping.&lt;/span&gt;
   &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

   &lt;span class="c1"&gt;// 4. Bootstrap initializes the environment (e.g., file descriptor inheritance for upgrades).&lt;/span&gt;
   &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

   &lt;span class="c1"&gt;// 5. Initialize our custom logic.&lt;/span&gt;
   &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;echo_logic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;EchoApp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

   &lt;span class="c1"&gt;// 6. Create a Listening Service.&lt;/span&gt;
   &lt;span class="c1"&gt;// We wrap our logic in a Service which binds it to a specific protocol/port.&lt;/span&gt;
   &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Echo Service"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;echo_logic&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

   &lt;span class="c1"&gt;// 7. Add a TCP listening endpoint.&lt;/span&gt;
   &lt;span class="c1"&gt;// This tells the service to listen on 0.0.0.0:6142.&lt;/span&gt;
   &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="nf"&gt;.add_tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:6142"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

   &lt;span class="c1"&gt;// 8. Register the service with the server.&lt;/span&gt;
   &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

   &lt;span class="c1"&gt;// 9. Run the server.&lt;/span&gt;
   &lt;span class="c1"&gt;// This enters the event loop and will not return until the process exits.&lt;/span&gt;
   &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Starting server on 0.0.0.0:6142..."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;To verify that your raw TCP server is working correctly, we will use &lt;code&gt;telnet&lt;/code&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Run the Server&lt;/strong&gt;:
Open your terminal in the project root and run the example. We enable info logs to see the connection events.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 00_basic_server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Connect with Telnet&lt;/strong&gt;:
Open a &lt;strong&gt;separate&lt;/strong&gt; terminal window and connect to the server.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   telnet localhost 6142
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Test Echo&lt;/strong&gt;:
Type &lt;code&gt;Hello Pingora&lt;/code&gt; and press Enter. You should see the text echo back immediately.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   Trying 127.0.0.1...
   Connected to localhost.
   Escape character is '^]'.
   Hello Pingora
   Hello Pingora
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Disconnect&lt;/strong&gt;:
To exit &lt;code&gt;telnet&lt;/code&gt;, press &lt;code&gt;Ctrl&lt;/code&gt; + &lt;code&gt;]&lt;/code&gt; (Control and right bracket), then type &lt;code&gt;close&lt;/code&gt; and press Enter.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   ^]
   telnet&amp;gt; close
   Connection closed.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Check Logs&lt;/strong&gt;:
In your first terminal, you should see logs indicating a new connection was established, and a closure log when you exited &lt;code&gt;telnet&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Lesson 1: Configuration &amp;amp; Lifecycle
&lt;/h1&gt;

&lt;p&gt;In Lesson 0, we built a server that ran with default settings. However, production services rarely run on defaults. They need to define how many worker threads to use, where to write error logs, where to store PID files, and how to handle process upgrades.&lt;/p&gt;

&lt;p&gt;Pingora handles this "infrastructure" configuration separately from your traffic handling logic. This separation allows the framework to manage the process lifecycle (daemonization, restarts, upgrades) standardly across all Pingora applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Concepts
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Opt&lt;/code&gt;&lt;/strong&gt;: This struct represents command-line arguments. Pingora provides a standard parser (via &lt;code&gt;clap&lt;/code&gt;) that handles flags like &lt;code&gt;-c&lt;/code&gt; (config file), &lt;code&gt;-d&lt;/code&gt; (daemon mode), and &lt;code&gt;-u&lt;/code&gt; (upgrade).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ServerConf&lt;/code&gt;&lt;/strong&gt;: This struct holds the runtime configuration for the server process. It includes settings for:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Threading&lt;/strong&gt;: &lt;code&gt;threads&lt;/code&gt; and &lt;code&gt;work_stealing&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Process Management&lt;/strong&gt;: &lt;code&gt;pid_file&lt;/code&gt;, &lt;code&gt;upgrade_sock&lt;/code&gt;, &lt;code&gt;user&lt;/code&gt;, &lt;code&gt;group&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logging&lt;/strong&gt;: &lt;code&gt;error_log&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSL/Network&lt;/strong&gt;: &lt;code&gt;ca_file&lt;/code&gt;, &lt;code&gt;upstream_keepalive_pool_size&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Server::new&lt;/code&gt;&lt;/strong&gt;: This constructor is the bridge. It takes the command-line options (&lt;code&gt;Opt&lt;/code&gt;), attempts to load the configuration file specified by &lt;code&gt;-c&lt;/code&gt;, merges it with defaults, and returns a fully initialized &lt;code&gt;Server&lt;/code&gt; instance.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Code (&lt;code&gt;examples/01_configuration.rs&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;In this example, we build a "dummy" server. Its only purpose is to load a configuration file and print the resulting settings to the console so we can verify that Pingora is correctly parsing our input.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ShutdownWatch&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;services&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;listening&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;protocols&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Stream&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[derive(Clone)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;ConfigDemoApp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;apps&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ServerApp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;ConfigDemoApp&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;process_new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_stream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_shutdown&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;ShutdownWatch&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// For this lesson, we don't process traffic.&lt;/span&gt;
        &lt;span class="c1"&gt;// We return None to close the connection immediately.&lt;/span&gt;
        &lt;span class="nb"&gt;None&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// 1. Initialize logging&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. Parse Command Line Arguments&lt;/span&gt;
    &lt;span class="c1"&gt;// This allows us to pass `-c conf/01_config.yaml` or `-d` (daemon mode)&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// 3. Initialize Server with Options&lt;/span&gt;
    &lt;span class="c1"&gt;// Server::new will attempt to load the config file specified in `opt.conf`.&lt;/span&gt;
    &lt;span class="c1"&gt;// If the file is missing or invalid, this will return an Error.&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;conf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// 4. Inspect the Loaded Configuration&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Configuration Loaded ---"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  version: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="py"&gt;.version&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  daemon: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="py"&gt;.daemon&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  error_log: {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="py"&gt;.error_log&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  pid_file: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="py"&gt;.pid_file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  upgrade_sock: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="py"&gt;.upgrade_sock&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  user: {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="py"&gt;.user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  group: {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="py"&gt;.group&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  threads: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="py"&gt;.threads&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  listener_tasks_per_fd: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="py"&gt;.listener_tasks_per_fd&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  work_stealing: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="py"&gt;.work_stealing&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  ca_file: {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="py"&gt;.ca_file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  grace_period_seconds: {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="py"&gt;.grace_period_seconds&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  graceful_shutdown_timeout_seconds: {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="py"&gt;.graceful_shutdown_timeout_seconds&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  client_bind_to_ipv4: {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="py"&gt;.client_bind_to_ipv4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  client_bind_to_ipv6: {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="py"&gt;.client_bind_to_ipv6&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  upstream_keepalive_pool_size: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="py"&gt;.upstream_keepalive_pool_size&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  upstream_connect_offload_threadpools: {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="py"&gt;.upstream_connect_offload_threadpools&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  upstream_connect_offload_thread_per_pool: {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="py"&gt;.upstream_connect_offload_thread_per_pool&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  upstream_debug_ssl_keylog: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="py"&gt;.upstream_debug_ssl_keylog&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  max_retries: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="py"&gt;.max_retries&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"----------------------------"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 5. Bootstrap the server&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// 6. Setup a dummy service (required to run the server)&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ConfigDemo"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;ConfigDemoApp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="nf"&gt;.add_tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:6143"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Starting server. Verify the thread count in the logs above matches your YAML."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Running the Lesson
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Define a Configuration File
&lt;/h3&gt;

&lt;p&gt;Create a file at &lt;code&gt;conf/01_config.yaml&lt;/code&gt; with the following content. We specifically set &lt;code&gt;threads&lt;/code&gt; to 2 to differentiate it from the default (which is usually 1 or the number of cores depending on environment).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="na"&gt;threads&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
&lt;span class="na"&gt;pid_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/tmp/pingora_lesson_01.pid"&lt;/span&gt;
&lt;span class="na"&gt;upgrade_sock&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/tmp/pingora_upgrade_01.sock"&lt;/span&gt;
&lt;span class="na"&gt;error_log&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/tmp/pingora_error.log"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Run with Defaults
&lt;/h3&gt;

&lt;p&gt;First, run without arguments. Pingora will use its internal defaults.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 01_configuration

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see &lt;code&gt;threads: 1&lt;/code&gt; and &lt;code&gt;pid_file: /tmp/pingora.pid&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Run with Configuration
&lt;/h3&gt;

&lt;p&gt;Now, pass the configuration file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 01_configuration &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; conf/01_config.yaml

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see the values change to match your YAML file:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;threads: 2&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pid_file: /tmp/pingora_lesson_01.pid&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;error_log: Some("/tmp/pingora_error.log")&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This confirms that the &lt;code&gt;Server&lt;/code&gt; has successfully bootstrapped itself using your external configuration.&lt;/p&gt;

&lt;h1&gt;
  
  
  Lesson 2: Daemon Mode &amp;amp; Background Services
&lt;/h1&gt;

&lt;p&gt;In production environments, servers are rarely run in the foreground attached to a terminal session. They run as &lt;strong&gt;daemons&lt;/strong&gt;—background processes that survive user logouts and system restarts.&lt;/p&gt;

&lt;p&gt;Pingora has built-in support for daemonization. It handles the low-level Unix operations required to detach from the terminal (forking, setsid), manages process ID (PID) files, and redirects standard output/error streams to log files.&lt;/p&gt;

&lt;p&gt;This lesson also introduces the &lt;strong&gt;&lt;code&gt;BackgroundService&lt;/code&gt;&lt;/strong&gt;. Unlike the &lt;code&gt;ListeningService&lt;/code&gt; from Lesson 0 (which accepts network connections), a &lt;code&gt;BackgroundService&lt;/code&gt; runs an arbitrary task loop. This is useful for sidecar processes, metric exporters, or health check runners that need to live alongside your proxy logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Concepts
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Daemonization Configuration&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;daemon: true&lt;/code&gt;&lt;/strong&gt;: Tells Pingora to fork into the background.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;pid_file&lt;/code&gt;&lt;/strong&gt;: The path where the server writes its Process ID. External tools (like &lt;code&gt;systemd&lt;/code&gt; or &lt;code&gt;monit&lt;/code&gt;) use this to track and stop the server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;error_log&lt;/code&gt;&lt;/strong&gt;: In daemon mode, &lt;code&gt;stdout&lt;/code&gt; and &lt;code&gt;stderr&lt;/code&gt; are closed. This setting redirects logs to a file so they aren't lost.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;BackgroundService&lt;/code&gt;&lt;/strong&gt;: A trait for tasks that run continuously until the server shuts down. It receives a &lt;code&gt;ShutdownWatch&lt;/code&gt; to know when to exit gracefully.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;background_service&lt;/code&gt; Helper&lt;/strong&gt;: A utility function in the prelude that wraps your custom logic into a generic service container, saving you from implementing boilerplate.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Code (&lt;code&gt;examples/02_daemon_mode.rs&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;This example defines a &lt;code&gt;HeartbeatService&lt;/code&gt; that logs a message every second. We check the &lt;code&gt;shutdown&lt;/code&gt; signal in the loop to ensure we stop immediately when the server receives a &lt;code&gt;SIGTERM&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ShutdownWatch&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;services&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;background&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;BackgroundService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;HeartbeatService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;BackgroundService&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;HeartbeatService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;shutdown&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ShutdownWatch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;period&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_secs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Heartbeat service started. PID: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;process&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

        &lt;span class="k"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;select!&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// Wait for shutdown signal&lt;/span&gt;
                &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;shutdown&lt;/span&gt;&lt;span class="nf"&gt;.changed&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Shutdown signal received. Stopping heartbeat."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="c1"&gt;// Or wait for the next tick&lt;/span&gt;
                &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;period&lt;/span&gt;&lt;span class="nf"&gt;.tick&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Beep... (PID: {})"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;process&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Inform the user if we are about to detach&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration.daemon&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Preparing to daemonize. Logs will be redirected to: {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration.error_log&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Check the PID file at: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration.pid_file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Running in foreground mode. Pass '-d' or use config file to daemonize."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;heartbeat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HeartbeatService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// Helper to wrap our logic in a Service container&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;background_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Heartbeat"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;heartbeat&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Running the Lesson
&lt;/h2&gt;

&lt;p&gt;To test daemonization, we must use a configuration file, as the behavior changes significantly from the default foreground mode.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Define the Daemon Configuration
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;conf/02_daemon.yaml&lt;/code&gt;. We set &lt;code&gt;daemon: true&lt;/code&gt; and define paths for logs and the PID file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="na"&gt;daemon&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="na"&gt;pid_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/tmp/pingora_02.pid"&lt;/span&gt;
&lt;span class="na"&gt;error_log&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/tmp/pingora_02.log"&lt;/span&gt;
&lt;span class="na"&gt;upgrade_sock&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/tmp/pingora_upgrade_02.sock"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Start the Daemon
&lt;/h3&gt;

&lt;p&gt;Run the server with the configuration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 02_daemon_mode &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; conf/02_daemon.yaml

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The program will print "Preparing to daemonize..." and then &lt;strong&gt;exit immediately&lt;/strong&gt;. This is expected; the parent process exits while the child process continues in the background.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Verify Background Execution
&lt;/h3&gt;

&lt;p&gt;The server is now running silently. You can verify this by checking the PID file or listing processes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Read the PID&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; /tmp/pingora_02.pid

&lt;span class="c"&gt;# Check if the process exists&lt;/span&gt;
ps &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /tmp/pingora_02.pid&lt;span class="si"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Check the Logs
&lt;/h3&gt;

&lt;p&gt;Since the process is detached, you won't see "Beep..." in your terminal. Tail the log file to see the output.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /tmp/pingora_02.log

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see the heartbeat messages appearing every second.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Stop the Daemon
&lt;/h3&gt;

&lt;p&gt;To stop the server gracefully, send a &lt;code&gt;SIGTERM&lt;/code&gt; to the process ID stored in the PID file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;kill&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /tmp/pingora_02.pid&lt;span class="si"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you check the log file again, you should see the "Shutdown signal received" message, confirming the &lt;code&gt;ShutdownWatch&lt;/code&gt; logic worked correctly.&lt;/p&gt;

&lt;h1&gt;
  
  
  Lesson 3: Graceful Shutdown
&lt;/h1&gt;

&lt;p&gt;In the previous lessons, stopping the server meant killing the process immediately. In a development environment, this is fine. In production, however, a hard stop is dangerous. You might interrupt a database write, corrupt a file, or drop a client connection in the middle of a request.&lt;/p&gt;

&lt;p&gt;Pingora provides a built-in &lt;strong&gt;Graceful Shutdown&lt;/strong&gt; mechanism to handle this. When the server receives a specific signal (usually &lt;code&gt;SIGTERM&lt;/code&gt;), it doesn't exit immediately. Instead:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It broadcasts a shutdown event to all services.&lt;/li&gt;
&lt;li&gt;It stops accepting &lt;em&gt;new&lt;/em&gt; connections (if using listeners).&lt;/li&gt;
&lt;li&gt;It waits for a configurable period (the &lt;code&gt;grace_period_seconds&lt;/code&gt;) for services to finish their current work.&lt;/li&gt;
&lt;li&gt;If the grace period expires and services are still running, it forces a shutdown.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Key Concepts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ShutdownWatch&lt;/code&gt;&lt;/strong&gt;: This is a Tokio &lt;code&gt;watch&lt;/code&gt; channel provided to every service's &lt;code&gt;start()&lt;/code&gt; method. Services must monitor this to know when to stop accepting new work and begin their cleanup.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;grace_period_seconds&lt;/code&gt;&lt;/strong&gt;: A setting in &lt;code&gt;ServerConf&lt;/code&gt;. It defines the maximum time the server will wait for services to finish after a shutdown signal is received.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Signal Handling&lt;/strong&gt;: Pingora distinguishes between two types of shutdown:&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fast Shutdown (&lt;code&gt;SIGINT&lt;/code&gt; / Ctrl+C)&lt;/strong&gt;: The server exits immediately. Use this during development or emergencies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Graceful Shutdown (&lt;code&gt;SIGTERM&lt;/code&gt;)&lt;/strong&gt;: The server enters the graceful shutdown phase described above. This is the standard signal used by deployment tools like Kubernetes or systemd.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Code (&lt;code&gt;examples/03_graceful_shutdown.rs&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;We will build a "Batch Job" service. It simulates processing long-running tasks that take 20 seconds to complete.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If we used &lt;strong&gt;Fast Shutdown&lt;/strong&gt; (Ctrl+C), the job would be cut off immediately.&lt;/li&gt;
&lt;li&gt;By using &lt;strong&gt;Graceful Shutdown&lt;/strong&gt; (SIGTERM) and checking &lt;code&gt;ShutdownWatch&lt;/code&gt;, the service detects the signal, stops starting &lt;em&gt;new&lt;/em&gt; jobs, but finishes the &lt;em&gt;current&lt;/em&gt; job before exiting.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ShutdownWatch&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;services&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;background&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;BackgroundService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;BatchJobService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;BackgroundService&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;BatchJobService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;shutdown&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ShutdownWatch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"BatchJob Service started. Waiting for jobs"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;job_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// 1. Check before starting work&lt;/span&gt;
            &lt;span class="c1"&gt;// If shutdown is requested, we break the loop immediately so no new jobs start.&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;shutdown&lt;/span&gt;&lt;span class="nf"&gt;.borrow&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Shutdown requested. No new jobs will be started."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="n"&gt;job_id&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Starting Job #{} (simulated 20s duration)..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;job_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// 2. Run the job with cancellation awareness&lt;/span&gt;
            &lt;span class="c1"&gt;// We use tokio::select! to listen for the shutdown signal WHILE the job is running.&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;job_duration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_secs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;select!&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// The "Happy Path": The job finishes normally&lt;/span&gt;
                &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;job_duration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Job #{} completed successfully."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;job_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;

                &lt;span class="c1"&gt;// The "Shutdown Path": Signal received mid-job&lt;/span&gt;
                &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;shutdown&lt;/span&gt;&lt;span class="nf"&gt;.changed&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nd"&gt;warn!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Shutdown signal received while Job #{} is running!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;job_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="nd"&gt;warn!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Finishing Job #{} before exiting..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;job_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                    &lt;span class="c1"&gt;// 3. Simulate wrapping up critical work (e.g., flushing buffers)&lt;/span&gt;
                    &lt;span class="c1"&gt;// In a real app, this ensures we don't leave data in a corrupt state.&lt;/span&gt;
                    &lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_secs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Job #{} completed gracefully during shutdown."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;job_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                    &lt;span class="c1"&gt;// Now we break the loop to allow the service to exit&lt;/span&gt;
                    &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="c1"&gt;// Brief pause between jobs&lt;/span&gt;
            &lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_secs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"BatchJob Service has stopped cleanly."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// 4. Configure the Grace Period&lt;/span&gt;
    &lt;span class="c1"&gt;// We set this to 10 seconds. Pingora will wait up to this long for&lt;/span&gt;
    &lt;span class="c1"&gt;// BatchJobService to exit. If we didn't set this, the server might exit&lt;/span&gt;
    &lt;span class="c1"&gt;// before our cleanup logic finishes.&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get_mut&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="py"&gt;.grace_period_seconds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;background_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"BatchJobService"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BatchJobService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Server running. Send SIGTERM to trigger graceful shutdown (e.g. 'pkill -TERM -f 03_graceful_shutdown')."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Running the Lesson
&lt;/h2&gt;

&lt;p&gt;To verify this lesson, we need to send specific signals to the process. We will test both the graceful path and the fast path.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Test Graceful Shutdown (The Happy Path)
&lt;/h3&gt;

&lt;p&gt;We want to confirm that if we stop the server while a job is running, it finishes that job.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Start the Server&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 03_graceful_shutdown
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Wait for a Job to Start&lt;/strong&gt;:
Watch the logs until you see &lt;code&gt;Starting Job #1...&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Send SIGTERM&lt;/strong&gt;:
Open a &lt;strong&gt;second terminal&lt;/strong&gt; window and run:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   pkill &lt;span class="nt"&gt;-TERM&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; 03_graceful_shutdown
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Observe the Logs&lt;/strong&gt;:
Back in the first terminal, you should see the shutdown sequence.

&lt;ul&gt;
&lt;li&gt;Pingora logs &lt;code&gt;SIGTERM received, gracefully exiting&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Our service logs &lt;code&gt;Shutdown signal received... Finishing Job #1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Crucially&lt;/strong&gt;, the server &lt;em&gt;waits&lt;/em&gt; for the job to finish (&lt;code&gt;Job #1 completed gracefully&lt;/code&gt;) before the process actually exits.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  2. Test Fast Shutdown (The Emergency Path)
&lt;/h3&gt;

&lt;p&gt;We want to confirm that we can still force-kill the server if needed.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Start the Server&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 03_graceful_shutdown
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Wait for a Job to Start&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Press&lt;/strong&gt; &lt;code&gt;Ctrl+C&lt;/code&gt;: This sends &lt;code&gt;SIGINT&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Observe the Logs&lt;/strong&gt;:
The server should exit &lt;strong&gt;immediately&lt;/strong&gt;. You will see &lt;code&gt;SIGINT received, exiting&lt;/code&gt;, but you will &lt;em&gt;not&lt;/em&gt; see the "Finishing Job" or "Job completed" messages. The work was abandoned instantly.&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Lesson 4: Threading Models
&lt;/h1&gt;

&lt;p&gt;Pingora offers two distinct threading models (runtimes) to execute your services. Choosing the right one is critical for performance tuning, as it dictates how your CPU cores are utilized and how tasks are scheduled.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Two Flavors
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Work Stealing (&lt;code&gt;Steal&lt;/code&gt;)&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What it is:&lt;/strong&gt; This is the standard Tokio multi-threaded runtime behavior. All worker threads share a global queue of tasks. If one thread finishes its work early, it "steals" tasks from other busy threads.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pros:&lt;/strong&gt; Excellent handling of uneven workloads. If one request takes 500ms and others take 1ms, the idle threads pick up the slack, preventing the system from stalling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cons:&lt;/strong&gt; Higher overhead due to synchronization (locking) between threads. This "chatter" can become a bottleneck at very high throughputs (e.g., 100k+ RPS).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Default:&lt;/strong&gt; &lt;code&gt;true&lt;/code&gt; in &lt;code&gt;ServerConf&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shared-Nothing (&lt;code&gt;NoSteal&lt;/code&gt;)&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What it is:&lt;/strong&gt; This is Pingora's specialized optimization. Instead of one large runtime, Pingora spawns a separate, single-threaded Tokio runtime for &lt;em&gt;each&lt;/em&gt; CPU core/thread configured. Incoming connections are sharded (distributed) to these threads. Once a connection belongs to a thread, it stays there.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pros:&lt;/strong&gt; Zero contention. Thread A never locks Thread B. This mimics the architecture of Nginx (one worker per core) and maximizes CPU cache locality.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cons:&lt;/strong&gt; Susceptible to "head-of-line blocking." If Thread A gets a heavy CPU-bound job, it cannot offload pending tasks to Thread B, even if Thread B is idle.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use Case:&lt;/strong&gt; High-throughput proxies where request latency is uniform (IO-bound).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Code (&lt;code&gt;examples/04_threading_model.rs&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;In this lesson, we programmatically toggle the threading model to &lt;code&gt;NoSteal&lt;/code&gt; (disabling work stealing) and set the thread count to 2.&lt;/p&gt;

&lt;p&gt;We then spawn two background services. Because we are in &lt;code&gt;NoSteal&lt;/code&gt; mode, Pingora will distribute these services across the available independent runtimes. We print the &lt;code&gt;ThreadId&lt;/code&gt; to verify that they are indeed running on different OS threads without moving between them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ShutdownWatch&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;services&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;background&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;BackgroundService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cd"&gt;/// A service that simply announces which thread it is running on.&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;ThreadReporterService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;BackgroundService&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;ThreadReporterService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;shutdown&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ShutdownWatch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// We set the interval to 1 second so logs don't flood the console&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;period&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_secs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ThreadReporter started."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;shutdown&lt;/span&gt;&lt;span class="nf"&gt;.borrow&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="c1"&gt;// This print will show us WHICH OS thread is executing this task.&lt;/span&gt;
            &lt;span class="c1"&gt;// In a 'Steal' runtime, this ID might change if the task moves (rare but possible).&lt;/span&gt;
            &lt;span class="c1"&gt;// In a 'NoSteal' runtime, this task is pinned to one specific thread forever.&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;thread_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;current&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.id&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;thread_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;current&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.name&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"unnamed"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"I am running on thread: {:?} ({})"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;thread_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;thread_name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="n"&gt;period&lt;/span&gt;&lt;span class="nf"&gt;.tick&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// 1. Configure the Threading Model&lt;/span&gt;
    &lt;span class="c1"&gt;// Access the configuration via Arc::get_mut to modify it before bootstrapping.&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get_mut&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Set the number of worker threads to 2 so we can see concurrency.&lt;/span&gt;
        &lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="py"&gt;.threads&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// Disable work stealing. This switches Pingora to the "Shared-Nothing" model.&lt;/span&gt;
        &lt;span class="c1"&gt;// Each of the 2 threads will run its own independent single-threaded runtime.&lt;/span&gt;
        &lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="py"&gt;.work_stealing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. Add multiple services&lt;/span&gt;
    &lt;span class="c1"&gt;// We add TWO instances of the same service. &lt;/span&gt;
    &lt;span class="c1"&gt;// In a NoSteal runtime with 2 threads, Pingora will attempt to distribute &lt;/span&gt;
    &lt;span class="c1"&gt;// these services across the available runtimes.&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;reporter_a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;background_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Reporter-A"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ThreadReporterService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;reporter_b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;background_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Reporter-B"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ThreadReporterService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reporter_a&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reporter_b&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Server starting with work_stealing = False."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;Run the server and observe the logs. You are looking for proof that two different Operating System threads are active.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Run the Example&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 04_threading_model
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Analyze the Output&lt;/strong&gt;:
You should see two different &lt;code&gt;ThreadId&lt;/code&gt; values appearing in the logs.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   INFO  04_threading_model &amp;gt; Server starting with work_stealing = False.
   INFO  04_threading_model &amp;gt; ThreadReporter started.
   INFO  04_threading_model &amp;gt; ThreadReporter started.
   INFO  04_threading_model &amp;gt; I am running on thread: ThreadId(2) (BG Reporter-B)
   INFO  04_threading_model &amp;gt; I am running on thread: ThreadId(3) (BG Reporter-A)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;work_stealing&lt;/code&gt; were enabled (default), you might see the same ThreadId for both, or the IDs swapping, depending on how Tokio schedules the tasks. With &lt;code&gt;work_stealing = false&lt;/code&gt;, these tasks are rigidly pinned to their respective threads.&lt;/p&gt;

&lt;h1&gt;
  
  
  Lesson 5: Background Services &amp;amp; Shared State
&lt;/h1&gt;

&lt;p&gt;Real-world proxies rarely run in isolation. They need to report metrics, fetch dynamic configurations, or perform health checks on upstream servers. These tasks must run continuously but independently of the request-handling logic.&lt;/p&gt;

&lt;p&gt;Pingora provides the &lt;strong&gt;&lt;code&gt;BackgroundService&lt;/code&gt;&lt;/strong&gt; trait for these scenarios. Unlike a &lt;code&gt;ListeningService&lt;/code&gt; (which waits for incoming network connections), a background service runs an arbitrary loop.&lt;/p&gt;

&lt;p&gt;In this lesson, we build a &lt;strong&gt;Traffic Monitor&lt;/strong&gt;. It consists of two parts running in parallel:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Traffic Service&lt;/strong&gt;: Accepts TCP connections and increments a shared counter.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Metric Exporter&lt;/strong&gt;: A background service that wakes up every 2 seconds to read and log the current connection count.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Key Concepts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Shared State with &lt;code&gt;Arc&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;To share data between the Traffic Service (which writes) and the Exporter (which reads), we wrap our state struct in an &lt;code&gt;Arc&lt;/code&gt; (Atomic Reference Counted smart pointer). This allows multiple threads to own a reference to the same memory location safely.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Atomic Operations &amp;amp; Memory Ordering
&lt;/h3&gt;

&lt;p&gt;Since multiple threads access the &lt;code&gt;connection_count&lt;/code&gt; simultaneously, we cannot use a simple &lt;code&gt;usize&lt;/code&gt;. We must use &lt;code&gt;AtomicUsize&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When reading or writing atomic variables, we must specify a &lt;strong&gt;Memory Ordering&lt;/strong&gt;. This tells the CPU and compiler how strictly they must synchronize this operation with other memory operations. In our example, we used &lt;code&gt;Ordering::Relaxed&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Ordering::Relaxed&lt;/code&gt;&lt;/strong&gt;: "I only care that this specific variable is updated atomically. I don't care about the order of &lt;em&gt;other&lt;/em&gt; memory operations around it."

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Why use it here?&lt;/em&gt; We are just counting numbers. If the "Exporter" sees the count update 5 nanoseconds later than it actually happened, or if it sees the updates out of perfect chronological order with unrelated variables, it doesn't matter. It is the fastest option.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;&lt;code&gt;Ordering::SeqCst&lt;/code&gt; (Sequential Consistency)&lt;/strong&gt;: "Every thread must see all operations in the exact same global order."

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Why avoid it here?&lt;/em&gt; It forces heavy synchronization barriers on the CPU, slowing down performance unnecessarily for a simple counter.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;&lt;code&gt;Ordering::Acquire&lt;/code&gt; / &lt;code&gt;Release&amp;lt;/strong&amp;gt;&lt;/code&gt;: Used for locks. "If I see this flag set (Acquire), I am guaranteed to see all the data you wrote before you set the flag (Release)."&lt;/strong&gt;
&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. &lt;code&gt;BackgroundService&lt;/code&gt; Lifecycle
&lt;/h3&gt;

&lt;p&gt;A background service receives a &lt;code&gt;ShutdownWatch&lt;/code&gt; in its &lt;code&gt;start()&lt;/code&gt; method. It is critical to check this watcher (usually via &lt;code&gt;tokio::select!&lt;/code&gt;). If you ignore it, your background loop will keep running forever, preventing the server from shutting down gracefully.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Code (&lt;code&gt;examples/05_background_services.rs&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;We define a shared &lt;code&gt;AppState&lt;/code&gt; and pass clones of it to both services. The &lt;code&gt;Traffic&lt;/code&gt; service simulates handling requests by incrementing the counter and writing a response. The &lt;code&gt;MetricExporter&lt;/code&gt; wakes up periodically to read that counter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ShutdownWatch&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;services&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;background&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;BackgroundService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;services&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;listening&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;protocols&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Stream&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;atomic&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;AtomicUsize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Ordering&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;AsyncWriteExt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cd"&gt;/// Shared state between the Traffic Service and the Background Service.&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;AppState&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;connection_count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AtomicUsize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// --- 1. Traffic Handling Service ---&lt;/span&gt;

&lt;span class="nd"&gt;#[derive(Clone)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;CounterApp&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;AppState&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;apps&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ServerApp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;CounterApp&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;process_new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_shutdown&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;ShutdownWatch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Increment the shared counter.&lt;/span&gt;
        &lt;span class="c1"&gt;// We use Relaxed because we don't rely on this value to synchronize other data.&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.state.connection_count&lt;/span&gt;&lt;span class="nf"&gt;.fetch_add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;Ordering&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Relaxed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Traffic: New connection handled. Count is now {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello! You are visitor #{}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="nf"&gt;.write_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="nf"&gt;.as_bytes&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// Return None to close the connection immediately&lt;/span&gt;
        &lt;span class="nb"&gt;None&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// --- 2. Background Metric Exporter ---&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;MetricExporter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;AppState&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;BackgroundService&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;MetricExporter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;shutdown&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ShutdownWatch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Run every 2 seconds&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;period&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_secs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Exporter: Service started."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;select!&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;shutdown&lt;/span&gt;&lt;span class="nf"&gt;.changed&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Exporter: Shutdown requested."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;period&lt;/span&gt;&lt;span class="nf"&gt;.tick&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="c1"&gt;// Read the shared state&lt;/span&gt;
                    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.state.connection_count&lt;/span&gt;&lt;span class="nf"&gt;.load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Ordering&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Relaxed&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Exporter: Current Total Connections: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Initialize shared state&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AppState&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="n"&gt;connection_count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;AtomicUsize&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// 1. Setup the Traffic Service (Port 6145)&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;traffic_logic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CounterApp&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;traffic_service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Traffic"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;traffic_logic&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;traffic_service&lt;/span&gt;&lt;span class="nf"&gt;.add_tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:6145"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. Setup the Background Service&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;exporter_logic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MetricExporter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="c1"&gt;// 'background_service' is a helper that wraps our struct into a Pingora Service&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;background_service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;background_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"MetricExporter"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exporter_logic&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 3. Add both to the server&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;traffic_service&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;background_service&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Server started. Traffic on port 6145. Metrics in logs."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;We will verify that both services are running and successfully communicating via the shared state. Since &lt;code&gt;nc&lt;/code&gt; (Netcat) is useful for testing network services, you may need to install it if you haven't already and are operating outside the dev container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;netcat-traditional
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;1. Run the Server&lt;/strong&gt;&lt;br&gt;
Start your server with info-level logging enabled.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 05_background_services
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Observe Initial Logs&lt;/strong&gt;&lt;br&gt;
You should see the "Exporter" logging &lt;code&gt;Current Total Connections: 0&lt;/code&gt; every 2 seconds. This confirms the background service is running.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INFO  05_background_services &amp;gt; Exporter: Service started.
INFO  05_background_services &amp;gt; Exporter: Current Total Connections: 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Generate Traffic&lt;/strong&gt;&lt;br&gt;
Open a &lt;strong&gt;second terminal&lt;/strong&gt; and connect to the traffic port a few times. Each connection will trigger the Traffic Service.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"hi"&lt;/span&gt; | nc localhost 6145
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"hi"&lt;/span&gt; | nc localhost 6145
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Observe State Update&lt;/strong&gt;&lt;br&gt;
Back in your first terminal, you should see the "Traffic" service log the new connections. Shortly after, the "Exporter" log should automatically reflect the new count, proving that the &lt;code&gt;Arc&amp;lt;AppState&amp;gt;&lt;/code&gt; is successfully sharing data between the two services.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INFO  05_background_services &amp;gt; Traffic: New connection handled. Count is now 1
INFO  05_background_services &amp;gt; Traffic: New connection handled. Count is now 2
INFO  05_background_services &amp;gt; Exporter: Current Total Connections: 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;5. Graceful Stop&lt;/strong&gt;&lt;br&gt;
Use &lt;code&gt;pkill -TERM -f 05_background_services&lt;/code&gt; (or &lt;code&gt;Ctrl+C&lt;/code&gt; if you don't mind the fast shutdown) to confirm the background service exits cleanly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pkill &lt;span class="nt"&gt;-TERM&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; 05_background_services
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Module 2: The Proxy Logic
&lt;/h1&gt;

&lt;p&gt;We have established the foundation of running a Pingora server. Now, we move to the core utility of the framework: &lt;strong&gt;HTTP Proxying&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important: The Lab Environment&lt;/strong&gt;&lt;br&gt;
From this module onwards, all examples must be run inside the &lt;code&gt;pingora_dev&lt;/code&gt; Docker container. The examples rely on the deterministic network topology of "Pingora City" to connect to upstream services (like &lt;code&gt;blue.pingora.local&lt;/code&gt;) and receive traffic from clients.&lt;/p&gt;

&lt;p&gt;If you are not inside the container yet, enter it now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; pingora_dev bash

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Lesson 6: The Simple Forwarder
&lt;/h1&gt;

&lt;p&gt;A "Simple Forwarder" or "Dumb Proxy" is the most basic proxy implementation. It accepts a request from a downstream client and forwards it to a single, hardcoded upstream server. It does not perform load balancing, authentication, or complex routing.&lt;/p&gt;

&lt;p&gt;This lesson introduces the &lt;strong&gt;&lt;code&gt;ProxyHttp&lt;/code&gt;&lt;/strong&gt; trait, which is the primary interface for building HTTP proxies in Pingora.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Concepts
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ProxyHttp&lt;/code&gt; Trait&lt;/strong&gt;: This is the heart of any HTTP proxy service. It provides hooks into the request lifecycle (request arrival, upstream selection, response filtering, etc.).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;upstream_peer()&lt;/code&gt;&lt;/strong&gt;: This is the only &lt;em&gt;mandatory&lt;/em&gt; hook you must implement (besides &lt;code&gt;new_ctx&lt;/code&gt;). It tells Pingora &lt;em&gt;where&lt;/em&gt; to send the current request.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;HttpPeer&lt;/code&gt;&lt;/strong&gt;: A struct defining the destination. It includes the IP/Port, whether to use TLS, and the SNI (Server Name Indication).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;upstream_request_filter()&lt;/code&gt;&lt;/strong&gt;: An optional hook that runs &lt;em&gt;after&lt;/em&gt; the peer is selected but &lt;em&gt;before&lt;/em&gt; the request is sent. This is the place to modify headers (e.g., setting the &lt;code&gt;Host&lt;/code&gt; header).&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Code (&lt;code&gt;examples/06_simple_forward.rs&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;We will build a proxy that listens on port &lt;code&gt;6146&lt;/code&gt;. It will forward every request it receives to our lab's &lt;strong&gt;Upstream Blue&lt;/strong&gt; (IP &lt;code&gt;172.28.0.20&lt;/code&gt;, port &lt;code&gt;8080&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;We also implement &lt;code&gt;upstream_request_filter&lt;/code&gt; to rewrite the &lt;code&gt;Host&lt;/code&gt; header. This is a best practice; many web servers (like Nginx) will reject requests if the &lt;code&gt;Host&lt;/code&gt; header does not match their configuration, even if the IP is correct.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// 1. Define the Proxy Logic&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;SimpleProxy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ProxyHttp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;SimpleProxy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Context (CTX) is per-request state. We don't need it for a simple forwarder.&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new_ctx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. Define the Upstream Peer&lt;/span&gt;
    &lt;span class="c1"&gt;// This hook is called for EVERY request.&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_peer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// In our lab, Upstream Blue is at this fixed IP.&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"172.28.0.20"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Forwarding request to Upstream Blue ({:?})"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Construct the peer. &lt;/span&gt;
        &lt;span class="c1"&gt;// - addr: The destination IP and Port.&lt;/span&gt;
        &lt;span class="c1"&gt;// - false: Do not use TLS (Blue is a plaintext HTTP server).&lt;/span&gt;
        &lt;span class="c1"&gt;// - "blue.pingora.local": The SNI (ignored for HTTP, but required by struct).&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"blue.pingora.local"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// 3. Modify headers before forwarding&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_request_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;upstream_request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;RequestHeader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Rewrite the Host header to match the destination.&lt;/span&gt;
        &lt;span class="c1"&gt;// Without this, the upstream sees the Host header sent by the client &lt;/span&gt;
        &lt;span class="c1"&gt;// (e.g., "172.28.0.10"), which might cause it to reject the request.&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;upstream_request&lt;/span&gt;&lt;span class="nf"&gt;.insert_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Host"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"blue.pingora.local"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// 4. Create the Service&lt;/span&gt;
    &lt;span class="c1"&gt;// `http_proxy_service` is a helper that wraps our logic in a ready-to-use Service.&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;http_proxy_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;SimpleProxy&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 5. Configure the Listener&lt;/span&gt;
    &lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="nf"&gt;.add_tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:6146"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Simple Proxy running on 0.0.0.0:6146 -&amp;gt; Forwarding to Upstream Blue"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;We verified this code by running the proxy in the &lt;code&gt;dev&lt;/code&gt; container and generating traffic from &lt;code&gt;client_1&lt;/code&gt; in a separate container.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Start the Proxy (in &lt;code&gt;pingora_dev&lt;/code&gt;)&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 06_simple_forward
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Output:&lt;/em&gt; &lt;code&gt;Simple Proxy running on 0.0.0.0:6146 -&amp;gt; Forwarding to Upstream Blue&lt;/code&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Generate Traffic (from Host)&lt;/strong&gt;:
We instructed &lt;code&gt;client_1&lt;/code&gt; to curl our proxy's IP (&lt;code&gt;172.28.0.10&lt;/code&gt;):
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; pingora_client_1 curl &lt;span class="nt"&gt;-v&lt;/span&gt; http://172.28.0.10:6146
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Result&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Client&lt;/strong&gt;: Received &lt;code&gt;200 OK&lt;/code&gt; and the body &lt;code&gt;'Response from BLUE'&lt;/code&gt;, confirming the traffic successfully traversed the proxy and reached the correct upstream.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proxy Logs&lt;/strong&gt;: Showed &lt;code&gt;Forwarding request to Upstream Blue&lt;/code&gt;, confirming the &lt;code&gt;upstream_peer&lt;/code&gt; hook was executed.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Lesson 7: TLS Termination
&lt;/h1&gt;

&lt;p&gt;In the previous lesson, we built a proxy that communicated over plain text (HTTP). In the modern web, this is insufficient for public-facing services. You need encryption (HTTPS) to protect data in transit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TLS Termination&lt;/strong&gt; (also known as SSL Offloading) is a pattern where the proxy handles the encrypted connection from the client, decrypts the traffic, and forwards it to the upstream service. Often, the connection to the upstream is kept as plain HTTP to save CPU cycles on the application servers, provided the internal network is secure (like our Docker bridge network).&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Concepts
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;TlsSettings&lt;/code&gt;&lt;/strong&gt;: This struct configures the SSL/TLS stack (OpenSSL or BoringSSL). Pingora provides an &lt;code&gt;intermediate()&lt;/code&gt; helper that configures a secure set of ciphers and protocols based on Mozilla's security guidelines, striking a balance between compatibility and security.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;enable_h2()&lt;/code&gt;&lt;/strong&gt;: Modern TLS listeners often negotiate the application protocol using ALPN (Application-Layer Protocol Negotiation). This setting enables &lt;strong&gt;HTTP/2&lt;/strong&gt;, which allows multiplexing multiple requests over a single TCP connection, significantly improving performance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;add_tls_with_settings&lt;/code&gt;&lt;/strong&gt;: Instead of &lt;code&gt;add_tcp&lt;/code&gt;, we use this method to bind the service to a port. It requires the certificate and private key.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;End-to-End vs. Termination&lt;/strong&gt;: In this lesson, we terminate TLS at the proxy. The client speaks HTTPS to us, but we speak HTTP to &lt;code&gt;blue.pingora.local&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Code (&lt;code&gt;examples/07_tls_termination.rs&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;We load the self-signed certificates generated by our lab environment (&lt;code&gt;server.crt&lt;/code&gt; and &lt;code&gt;server.key&lt;/code&gt;). We then configure the proxy to listen on port &lt;code&gt;6147&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Notice that inside &lt;code&gt;upstream_peer&lt;/code&gt;, we still pass &lt;code&gt;false&lt;/code&gt; to &lt;code&gt;HttpPeer::new&lt;/code&gt;. This confirms that while the &lt;em&gt;downstream&lt;/em&gt; (user) connection is secure, the &lt;em&gt;upstream&lt;/em&gt; (backend) connection remains plain text.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;listeners&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;tls&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;TlsSettings&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;path&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;TlsProxy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ProxyHttp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;TlsProxy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new_ctx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_peer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// We forward to Upstream Blue.&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"172.28.0.20"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Forwarding HTTPS request to Upstream Blue ({:?})"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// CRITICAL: We pass 'false' here. &lt;/span&gt;
        &lt;span class="c1"&gt;// This effectively "terminates" the TLS. We decrypted the traffic,&lt;/span&gt;
        &lt;span class="c1"&gt;// and now we are sending it as plain HTTP to the internal backend.&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"blue.pingora.local"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;http_proxy_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;TlsProxy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 1. Locate Certificates&lt;/span&gt;
    &lt;span class="c1"&gt;// These are mounted into the dev container at /keys/&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;cert_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"/keys/server.crt"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;key_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"/keys/server.key"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nn"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cert_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.exists&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nn"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.exists&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;error!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Certificates not found! Make sure you ran scripts/00-setup-certs.sh"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Missing keys at {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cert_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. Configure TLS&lt;/span&gt;
    &lt;span class="c1"&gt;// We use the 'intermediate' profile for best-practice security defaults.&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;tls_settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;TlsSettings&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;intermediate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cert_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Enable HTTP/2 support (ALPN)&lt;/span&gt;
    &lt;span class="n"&gt;tls_settings&lt;/span&gt;&lt;span class="nf"&gt;.enable_h2&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// 3. Bind the TLS Listener&lt;/span&gt;
    &lt;span class="c1"&gt;// We use port 6147 for this lesson.&lt;/span&gt;
    &lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="nf"&gt;.add_tls_with_settings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:6147"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tls_settings&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"HTTPS Proxy running on 0.0.0.0:6147 -&amp;gt; Forwarding to Upstream Blue"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="p"&gt;);;&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;To verify TLS termination, we need a client that trusts our lab's self-signed Certificate Authority (CA).&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Start the Proxy (in &lt;code&gt;pingora_dev&lt;/code&gt;)&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 07_tls_termination
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Output:&lt;/em&gt; &lt;code&gt;HTTPS Proxy running on 0.0.0.0:6147&lt;/code&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Test from Client (from Host Machine)&lt;/strong&gt;:
We use &lt;code&gt;curl&lt;/code&gt; with the CA certificate. We also map the hostname &lt;code&gt;dev.pingora.local&lt;/code&gt; to the container's IP to ensure the SSL certificate matches the domain name.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; pingora_client_1 curl &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;--cacert&lt;/span&gt; /keys/ca.crt &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;--resolve&lt;/span&gt; dev.pingora.local:6147:172.28.0.10 &lt;span class="se"&gt;\&lt;/span&gt;
     https://dev.pingora.local:6147
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Result Analysis&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;TLS Handshake&lt;/strong&gt;: You will see the handshake occur: &lt;code&gt;SSL connection using TLSv1.3&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ALPN&lt;/strong&gt;: If verified, you may see &lt;code&gt;ALPN: offers h2,http/1.1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Response&lt;/strong&gt;: &lt;code&gt;Response from BLUE&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proxy Logs&lt;/strong&gt;: &lt;code&gt;Forwarding HTTPS request to Upstream Blue&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Lesson 8: Header Manipulation
&lt;/h1&gt;

&lt;p&gt;One of the most common tasks for an API Gateway is to modify traffic as it passes through. You might need to sanitize requests (removing sensitive headers like internal tokens), tag traffic (adding request IDs for tracing), or modify responses (adding security headers like CORS or &lt;code&gt;Strict-Transport-Security&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Pingora provides specific hooks in the &lt;code&gt;ProxyHttp&lt;/code&gt; trait to inspect and mutate headers at different stages of the request lifecycle.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Concepts
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;upstream_request_filter&lt;/code&gt;&lt;/strong&gt;: This hook runs &lt;em&gt;after&lt;/em&gt; the upstream peer has been selected but &lt;em&gt;before&lt;/em&gt; the request is sent to the backend. It allows you to modify the &lt;code&gt;RequestHeader&lt;/code&gt;.

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Use cases:&lt;/em&gt; Adding authentication tokens, removing client-identifying info (scrubbing), or rewriting the &lt;code&gt;Host&lt;/code&gt; header.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;response_filter&lt;/code&gt;&lt;/strong&gt;: This hook runs &lt;em&gt;after&lt;/em&gt; the response receives the headers from the backend but &lt;em&gt;before&lt;/em&gt; the body is streamed to the client. It allows you to modify the &lt;code&gt;ResponseHeader&lt;/code&gt;.

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Use cases:&lt;/em&gt; Hiding backend server versions (&lt;code&gt;Server: nginx/1.18&lt;/code&gt;), adding custom watermarks, or fixing caching headers.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Code (&lt;code&gt;examples/08_header_manipulation.rs&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;In this lesson, we proxy traffic to &lt;strong&gt;Upstream Green&lt;/strong&gt; (172.28.0.21). We perform the following manipulations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Request Phase&lt;/strong&gt;: We inject &lt;code&gt;X-Pingora-Proxy: true&lt;/code&gt; so the backend knows the request came via our gateway. We also remove the &lt;code&gt;User-Agent&lt;/code&gt; header to anonymize the client.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Response Phase&lt;/strong&gt;: We inject &lt;code&gt;X-Edited-By: Pingora&lt;/code&gt; into the response headers so the client can verify the gateway handled the traffic.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;http&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ResponseHeader&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;HeaderModProxy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ProxyHttp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;HeaderModProxy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new_ctx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_peer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"172.28.0.21"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"green.pingora.local"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// 1. Modify the REQUEST (Client -&amp;gt; Proxy -&amp;gt; Upstream)&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_request_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;upstream_request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;RequestHeader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;where&lt;/span&gt;
        &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Send&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Sync&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Set the Host header to match the upstream&lt;/span&gt;
        &lt;span class="n"&gt;upstream_request&lt;/span&gt;&lt;span class="nf"&gt;.insert_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Host"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"green.pingora.local"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// Add a custom header for the backend to see&lt;/span&gt;
        &lt;span class="n"&gt;upstream_request&lt;/span&gt;&lt;span class="nf"&gt;.insert_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"X-Pingora-Proxy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// Remove the User-Agent header for privacy&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;upstream_request&lt;/span&gt;&lt;span class="nf"&gt;.remove_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"User-Agent"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Request headers modified: Added X-Pingora-Proxy, Removed User-Agent."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. Modify the RESPONSE (Upstream -&amp;gt; Proxy -&amp;gt; Client)&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;response_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;upstream_response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;ResponseHeader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;where&lt;/span&gt;
        &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Send&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Sync&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Add a custom header so the client knows who handled this&lt;/span&gt;
        &lt;span class="n"&gt;upstream_response&lt;/span&gt;&lt;span class="nf"&gt;.insert_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"X-Edited-By"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Pingora"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Response headers modified: Added X-Edited-By"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;http_proxy_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HeaderModProxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Bind to port 6148&lt;/span&gt;
    &lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="nf"&gt;.add_tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:6148"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Header Manipulation Proxy running on 0.0.0.0:6148 -&amp;gt; Forwarding to Upstream Green"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;To verify that the headers are being modified, we inspect the traffic from the client side.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Start the Proxy (in &lt;code&gt;pingora_dev&lt;/code&gt;)&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 08_header_manipulation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Output:&lt;/em&gt; &lt;code&gt;Header Manipulation Proxy running on 0.0.0.0:6148&lt;/code&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Test from Client (from Host)&lt;/strong&gt;:
Run &lt;code&gt;curl -v&lt;/code&gt; against port &lt;code&gt;6148&lt;/code&gt;. We use verbose mode to see the response headers.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; pingora_client_1 curl &lt;span class="nt"&gt;-v&lt;/span&gt; http://172.28.0.10:6148
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Result Analysis&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Body&lt;/strong&gt;: You should see &lt;code&gt;Response from GREEN&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Headers&lt;/strong&gt;: In the response section (lines starting with &lt;code&gt;&amp;lt;&lt;/code&gt;), you should see our custom header:
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   &amp;lt; X-Edited-By: Pingora
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Proxy Logs&lt;/strong&gt;: The console running the proxy will confirm the hooks executed:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   INFO  Request headers modified: Added X-Pingora-Proxy, Removed User-Agent.
   INFO  Response headers modified: Added X-Edited-By
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Lesson 9: Path Routing
&lt;/h1&gt;

&lt;p&gt;In previous lessons, we blindly forwarded every request to a single destination. In reality, proxies act as traffic routers, dispatching requests to different microservices based on the URL path, HTTP method, or headers.&lt;/p&gt;

&lt;p&gt;This lesson introduces two critical architectural concepts in Pingora:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Request Filter&lt;/strong&gt;: A hook that runs &lt;em&gt;early&lt;/em&gt; in the lifecycle to validate requests or make routing decisions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Context (&lt;code&gt;CTX&lt;/code&gt;)&lt;/strong&gt;: A mechanism to share state between different phases of a request (e.g., passing the routing decision from the "Filter" phase to the "Peer Selection" phase).&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Key Concepts
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;request_filter&lt;/code&gt;&lt;/strong&gt;: This hook runs immediately after the proxy receives the request headers from the client. It returns a &lt;code&gt;Result&amp;lt;bool&amp;gt;&lt;/code&gt;.

&lt;ul&gt;
&lt;li&gt;If it returns &lt;code&gt;Ok(false)&lt;/code&gt;: Pingora continues to the next phase (upstream peer selection).&lt;/li&gt;
&lt;li&gt;If it returns &lt;code&gt;Ok(true)&lt;/code&gt;: Pingora assumes the request has been fully handled (e.g., you sent a 404 error response manually) and stops processing.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context (&lt;code&gt;CTX&lt;/code&gt;)&lt;/strong&gt;: The &lt;code&gt;ProxyHttp&lt;/code&gt; trait has an associated type &lt;code&gt;CTX&lt;/code&gt;. This is your custom state object created via &lt;code&gt;new_ctx()&lt;/code&gt; for every new request.

&lt;ul&gt;
&lt;li&gt;In simple proxies, this is &lt;code&gt;()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;In routing proxies, we use it to store decisions (like &lt;code&gt;Option&amp;lt;Target&amp;gt;&lt;/code&gt;) so subsequent hooks (like &lt;code&gt;upstream_peer&lt;/code&gt; or &lt;code&gt;upstream_request_filter&lt;/code&gt;) know what to do.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Code (&lt;code&gt;examples/09_path_routing.rs&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;We define a simple &lt;code&gt;Target&lt;/code&gt; enum to represent our microservices.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;/blue&lt;/code&gt;&lt;/strong&gt; -&amp;gt; Routes to Upstream Blue.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;/green&lt;/code&gt;&lt;/strong&gt; -&amp;gt; Routes to Upstream Green.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Other&lt;/strong&gt; -&amp;gt; Returns a 404 error immediately.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// 1. Define an Enum to track our routing decision&lt;/span&gt;
&lt;span class="c1"&gt;// This will be stored in the Request Context (CTX)&lt;/span&gt;
&lt;span class="nd"&gt;#[derive(Debug,&lt;/span&gt; &lt;span class="nd"&gt;Clone,&lt;/span&gt; &lt;span class="nd"&gt;Copy)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;Target&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Blue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Green&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;PathRouter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ProxyHttp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;PathRouter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// 2. Define the Context Type&lt;/span&gt;
    &lt;span class="c1"&gt;// Instead of (), we now use Option&amp;lt;Target&amp;gt; to store our decision.&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Target&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new_ctx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;None&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// 3. Request Filter: The Gatekeeper&lt;/span&gt;
    &lt;span class="c1"&gt;// We check the path *before* picking a peer.&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;request_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="nf"&gt;.req_header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="py"&gt;.uri&lt;/span&gt;&lt;span class="nf"&gt;.path&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="nf"&gt;.starts_with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/blue"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Target&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Blue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="nf"&gt;.starts_with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/green"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Target&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Green&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Unknown path: Return 404 immediately&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="nf"&gt;.respond_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="c1"&gt;// Return true to tell Pingora "we handled this, stop processing".&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Return false to continue to the next phase (upstream_peer)&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// 4. Upstream Peer: The Router&lt;/span&gt;
    &lt;span class="c1"&gt;// We read the decision made in request_filter to pick the IP.&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_peer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Context should be set by request_filter"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sni&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nn"&gt;Target&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Blue&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="s"&gt;"172.28.0.20"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"blue.pingora.local"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nn"&gt;Target&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Green&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="s"&gt;"172.28.0.21"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"green.pingora.local"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Routing request to {:?} based on path"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sni&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// 5. Upstream Request Filter: The Modifier&lt;/span&gt;
    &lt;span class="c1"&gt;// We rewrite the Host header to match the chosen upstream.&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_request_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;upstream_request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;RequestHeader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;where&lt;/span&gt;
        &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Send&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Sync&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Context should be set"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nn"&gt;Target&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Blue&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"blue.pingora.local"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nn"&gt;Target&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Green&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"green.pingora.local"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="n"&gt;upstream_request&lt;/span&gt;&lt;span class="nf"&gt;.insert_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Host"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;http_proxy_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PathRouter&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="nf"&gt;.add_tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:6149"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Path Router running on 0.0.0.0:6149"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Try: curl http://127.0.0.1:6149/blue or /green"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;We verified this routing logic by sending requests to different paths and observing the responses.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Start the Proxy (in &lt;code&gt;pingora_dev&lt;/code&gt;)&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 09_path_routing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Output:&lt;/em&gt; &lt;code&gt;Path Router running on 0.0.0.0:6149&lt;/code&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Test Blue Route&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; pingora_client_1 curl &lt;span class="nt"&gt;-v&lt;/span&gt; http://172.28.0.10:6149/blue
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Result:&lt;/em&gt; &lt;code&gt;200 OK&lt;/code&gt; and &lt;code&gt;'Response from BLUE'&lt;/code&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Test Green Route&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; pingora_client_1 curl &lt;span class="nt"&gt;-v&lt;/span&gt; http://172.28.0.10:6149/green
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Result:&lt;/em&gt; &lt;code&gt;200 OK&lt;/code&gt; and &lt;code&gt;'Response from GREEN'&lt;/code&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Test Invalid Route (404)&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; pingora_client_1 curl &lt;span class="nt"&gt;-v&lt;/span&gt; http://172.28.0.10:6149/invalid
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Result:&lt;/em&gt; &lt;code&gt;404 Not Found&lt;/code&gt; (Pingora Default Error Page).&lt;/p&gt;

&lt;h1&gt;
  
  
  Lesson 10: Query Params
&lt;/h1&gt;

&lt;p&gt;Modifying the request URI—specifically the query string—is a frequent requirement for edge proxies. Common use cases include:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Cache Normalization&lt;/strong&gt;: Reordering parameters or removing volatile ones (like &lt;code&gt;utm_source&lt;/code&gt; or &lt;code&gt;fbclid&lt;/code&gt;) so that requests map to the same cache key.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security&lt;/strong&gt;: Stripping internal debug flags or administrative parameters before they reach the backend.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Analytics Tagging&lt;/strong&gt;: Injecting a source identifier (e.g., &lt;code&gt;ref=gateway&lt;/code&gt;) so the upstream knows the request passed through the proxy.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Key Concepts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;upstream_request.uri&lt;/code&gt;&lt;/strong&gt;: Accessing the URI within the &lt;code&gt;upstream_request_filter&lt;/code&gt; hook allows us to inspect the path and query string.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;URI Immutability&lt;/strong&gt;: The &lt;code&gt;http::Uri&lt;/code&gt; type is immutable. To modify it, we typically extract the string components, manipulate them, parse a new &lt;code&gt;Uri&lt;/code&gt; object, and then use &lt;code&gt;upstream_request.set_uri()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Robust Parsing&lt;/strong&gt;: In production code, it is recommended to use the &lt;code&gt;url&lt;/code&gt; crate for complex parsing (decoding percent-encoding, handling edge cases). For simple string replacement, standard string manipulation works fine.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Code (&lt;code&gt;examples/10_query_params.rs&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;In this lesson, we manipulate the URI string directly in &lt;code&gt;upstream_request_filter&lt;/code&gt;. We perform two actions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Security&lt;/strong&gt;: Remove any parameter starting with &lt;code&gt;debug=&lt;/code&gt; (e.g., preventing &lt;code&gt;debug=true&lt;/code&gt; from triggering verbose backend logs).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tagging&lt;/strong&gt;: Append &lt;code&gt;ref=pingora&lt;/code&gt; to every request.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;http&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Uri&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;QueryModeProxy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ProxyHttp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;QueryModeProxy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new_ctx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_peer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"172.28.0.20"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"blue.pingora.local"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_request_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;upstream_request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;RequestHeader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;where&lt;/span&gt;
        &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Send&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Sync&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// 1. Access the URI parts&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;upstream_request&lt;/span&gt;&lt;span class="py"&gt;.uri&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="nf"&gt;.path&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="nf"&gt;.query&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Original Query: '{}'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// 2. Manipulate the Query String&lt;/span&gt;
        &lt;span class="c1"&gt;// We filter OUT "debug=..." and append "ref=pingora"&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="nf"&gt;.split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&amp;amp;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;.filter&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;part&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;part&lt;/span&gt;&lt;span class="nf"&gt;.is_empty&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;part&lt;/span&gt;&lt;span class="nf"&gt;.starts_with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"debug="&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="nf"&gt;.collect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="nf"&gt;.push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ref=pingora"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;new_query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="nf"&gt;.join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&amp;amp;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// 3. Construct and parse the new URI&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;new_uri_string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}?{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_query&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;new_uri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new_uri_string&lt;/span&gt;&lt;span class="nf"&gt;.parse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to parse new URI"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Rewritten URI: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_uri&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// 4. Update the request&lt;/span&gt;
        &lt;span class="n"&gt;upstream_request&lt;/span&gt;&lt;span class="nf"&gt;.set_uri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_uri&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;upstream_request&lt;/span&gt;&lt;span class="nf"&gt;.insert_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Host"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"blue.pingora.local"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;http_proxy_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;QueryModeProxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="nf"&gt;.add_tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:6150"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Query Param Proxy running on 0.0.0.0:6150"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;We will verify this by sending a request containing the forbidden &lt;code&gt;debug&lt;/code&gt; parameter and observing the logs to confirm it was removed and replaced.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Start the Proxy (in &lt;code&gt;pingora_dev&lt;/code&gt;)&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 10_query_params
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Output:&lt;/em&gt; &lt;code&gt;Query Param Proxy running on 0.0.0.0:6150&lt;/code&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Send a Request (from Host)&lt;/strong&gt;:
We construct a URL with a mix of parameters:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; pingora_client_1 curl &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"http://172.28.0.10:6150/search?q=rust&amp;amp;debug=true&amp;amp;sort=asc"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Result Analysis&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Proxy Logs&lt;/strong&gt;: You should see the transformation happening. The &lt;code&gt;debug=true&lt;/code&gt; segment is dropped, and &lt;code&gt;ref=pingora&lt;/code&gt; is appended.
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   INFO  Original Query: 'q=rust&amp;amp;debug=true&amp;amp;sort=asc'
   INFO  Rewritten URI: /search?q=rust&amp;amp;sort=asc&amp;amp;ref=pingora
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Upstream Behavior&lt;/strong&gt;: If you inspect the &lt;code&gt;blue&lt;/code&gt; container logs (or if the echo server reflected the query string), you would see it received the sanitized version.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Lesson 11: Response Modification
&lt;/h1&gt;

&lt;p&gt;Just as we can modify requests before they reach the upstream, we can intercept and modify the response coming back from the backend before it reaches the client.&lt;/p&gt;

&lt;p&gt;This is critical for:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Security&lt;/strong&gt;: Adding headers like &lt;code&gt;HSTS&lt;/code&gt;, &lt;code&gt;X-Frame-Options&lt;/code&gt;, or &lt;code&gt;Content-Security-Policy&lt;/code&gt; (CSP) centrally, rather than configuring them on every backend service.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Privacy&lt;/strong&gt;: Stripping headers that leak internal implementation details (e.g., removing &lt;code&gt;X-Powered-By&lt;/code&gt; or internal version numbers).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Legacy Compatibility&lt;/strong&gt;: Renaming or duplicating headers to satisfy old client applications.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Key Concepts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;response_filter&lt;/code&gt;&lt;/strong&gt;: The &lt;code&gt;ProxyHttp&lt;/code&gt; hook that runs after the upstream has responded with headers, but before the body is streamed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ResponseHeader&lt;/code&gt;&lt;/strong&gt;: The struct representing the response. It behaves similarly to &lt;code&gt;RequestHeader&lt;/code&gt;, allowing you to &lt;code&gt;insert&lt;/code&gt;, &lt;code&gt;remove&lt;/code&gt;, or &lt;code&gt;get&lt;/code&gt; headers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Header Handling&lt;/strong&gt;: Headers in HTTP are technically multi-valued. When using &lt;code&gt;.get()&lt;/code&gt;, you receive the first value. If you need to handle multiple values (like multiple &lt;code&gt;Set-Cookie&lt;/code&gt; headers), you would iterate over them, though simple insertion/removal is the most common use case.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Code (&lt;code&gt;examples/11_response_modification.rs&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;We configure the proxy to forward traffic to &lt;strong&gt;Upstream Blue&lt;/strong&gt;. On the return trip, we perform three operations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Strip&lt;/strong&gt; the &lt;code&gt;X-App-Version&lt;/code&gt; header to hide the backend version.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inject&lt;/strong&gt; &lt;code&gt;X-Content-Type-Options: nosniff&lt;/code&gt; to prevent browsers from MIME-sniffing the response.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Duplicate&lt;/strong&gt; the &lt;code&gt;Date&lt;/code&gt; header into &lt;code&gt;X-Legacy-Date&lt;/code&gt; to simulate supporting a legacy client that expects this specific header name.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;http&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ResponseHeader&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;ResponseModProxy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ProxyHttp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;ResponseModProxy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new_ctx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_peer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"172.28.0.20"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"blue.pingora.local"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;response_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;upstream_response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;ResponseHeader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;where&lt;/span&gt;
        &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Send&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Sync&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// 1. Remove a header to hide backend details&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;upstream_response&lt;/span&gt;&lt;span class="nf"&gt;.remove_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"X-App-Version"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// 2. Add a security header&lt;/span&gt;
        &lt;span class="n"&gt;upstream_response&lt;/span&gt;&lt;span class="nf"&gt;.insert_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"X-Content-Type-Options"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"nosniff"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// 3. Copy/Rename a header&lt;/span&gt;
        &lt;span class="c1"&gt;// We retrieve the 'Date' header and insert it as 'X-Legacy-Date'.&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;date_val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;upstream_response&lt;/span&gt;&lt;span class="py"&gt;.headers&lt;/span&gt;&lt;span class="nf"&gt;.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Date"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// We clone the bytes because insert_header takes ownership&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;val_bytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;date_val&lt;/span&gt;&lt;span class="nf"&gt;.as_bytes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.to_vec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="n"&gt;upstream_response&lt;/span&gt;&lt;span class="nf"&gt;.insert_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"X-Legacy-Date"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;val_bytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Response filtered. Stripped Version. Added Security Headers."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;http_proxy_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ResponseModProxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="nf"&gt;.add_tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:6151"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Response Mod Proxy running on 0.0.0.0:6151"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;We verified the logic by inspecting the HTTP headers using &lt;code&gt;curl&lt;/code&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Start the Proxy (in &lt;code&gt;pingora_dev&lt;/code&gt;)&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 11_response_modification
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Test from Client (from Host)&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; pingora_client_1 curl &lt;span class="nt"&gt;-v&lt;/span&gt; http://172.28.0.10:6151
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Result Analysis&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Removed&lt;/strong&gt;: The output showed &lt;code&gt;&amp;lt; X-App-Name: http-echo&lt;/code&gt;, but &lt;code&gt;X-App-Version&lt;/code&gt; was successfully absent (it is normally present in the echo server response).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Added&lt;/strong&gt;: The line &lt;code&gt;&amp;lt; X-Content-Type-Options: nosniff&lt;/code&gt; appeared.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Copied&lt;/strong&gt;: The line &lt;code&gt;&amp;lt; X-Legacy-Date: ...&lt;/code&gt; appeared with the exact timestamp as the standard &lt;code&gt;Date&lt;/code&gt; header.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Lesson 12: Body Inspection
&lt;/h1&gt;

&lt;p&gt;Validating the request &lt;em&gt;body&lt;/em&gt; is a powerful capability for an edge proxy. While standard Load Balancers often just route based on headers, a sophisticated proxy can act as a &lt;strong&gt;WAF&lt;/strong&gt; (Web Application Firewall), inspecting payloads for SQL injection, malware signatures, or prohibited keywords.&lt;/p&gt;

&lt;p&gt;However, inspecting bodies in a proxy is challenging because proxies are typically &lt;strong&gt;streaming&lt;/strong&gt; by default to maintain high performance and low memory usage. To inspect the body, we often need to buffer it (hold it in memory), which creates trade-offs between security and resource consumption.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Concepts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;request_body_filter&lt;/code&gt;&lt;/strong&gt;: This hook is called iteratively for every chunk of data the client uploads. It provides a &lt;code&gt;&amp;amp;mut Option&amp;lt;Bytes&amp;gt;&lt;/code&gt;, which allows you to inspect, modify, or reject the chunk before it is passed to the upstream.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Streaming vs. Buffering&lt;/strong&gt;:&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Streaming&lt;/strong&gt;: Data flows &lt;code&gt;Client -&amp;gt; Proxy -&amp;gt; Upstream&lt;/code&gt; immediately. Good for speed, bad for inspection (you might send half a malicious payload before detecting it).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Buffering&lt;/strong&gt;: Data is held in the Proxy's &lt;code&gt;CTX&lt;/code&gt; until a condition is met. In this lesson, we buffer chunks into a &lt;code&gt;Vec&amp;lt;u8&amp;gt;&lt;/code&gt; to perform a string check.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Safety&lt;/strong&gt;: When inspecting bodies, you &lt;strong&gt;must&lt;/strong&gt; enforce size limits (e.g., stopping after 1MB). Otherwise, a client could exhaust your proxy's RAM by sending an infinite stream of data.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Code (&lt;code&gt;examples/12_body_inspection.rs&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;We define a &lt;code&gt;BodyCtx&lt;/code&gt; struct to hold our inspection buffer. We then implement &lt;code&gt;request_body_filter&lt;/code&gt; to accumulate incoming bytes and scan for the forbidden keyword &lt;code&gt;"rogue"&lt;/code&gt;. If detected, we return a custom error, which immediately aborts the connection.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Bytes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;BodyInspector&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// 1. Define the Context&lt;/span&gt;
&lt;span class="c1"&gt;// We use a simple buffer (Vec&amp;lt;u8&amp;gt;) to accumulate the body for inspection.&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;BodyCtx&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ProxyHttp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;BodyInspector&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BodyCtx&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Initialize the empty buffer for each request&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new_ctx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;BodyCtx&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;Vec&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_peer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"172.28.0.20"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"blue.pingora.local"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. Request Body Filter&lt;/span&gt;
    &lt;span class="c1"&gt;// This runs for EVERY chunk of the request body.&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;request_body_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Bytes&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_end_of_stream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;where&lt;/span&gt;
        &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Send&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Sync&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// If there is data in this chunk...&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// ...append it to our inspection buffer.&lt;/span&gt;
            &lt;span class="c1"&gt;// Note: In production, enforce a size limit (e.g. 1MB) to prevent memory DoS.&lt;/span&gt;
            &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.buffer&lt;/span&gt;&lt;span class="nf"&gt;.extend_from_slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// Check for the forbidden pattern&lt;/span&gt;
            &lt;span class="c1"&gt;// We use String::from_utf8_lossy to handle potential binary data safely.&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_utf8_lossy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.buffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="nf"&gt;.contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"rogue"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nd"&gt;warn!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Security Alert: Forbidden content 'rogue' detected in body!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="c1"&gt;// Returning an error here immediately aborts the proxy session.&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ErrorType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Custom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SecurityPolicyViolation"&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// If we didn't find the keyword, we allow the chunk to proceed.&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;http_proxy_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BodyInspector&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="nf"&gt;.add_tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:6152"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Body Inspector running on 0.0.0.0:6152"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;To verify the "WAF" functionality, we send both compliant and non-compliant POST requests using &lt;code&gt;curl&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Start the Proxy
&lt;/h3&gt;

&lt;p&gt;Run the example inside the &lt;code&gt;pingora_dev&lt;/code&gt; container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 12_body_inspection
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Test Clean Request
&lt;/h3&gt;

&lt;p&gt;From the host machine, send a harmless payload:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; pingora_client_1 curl &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"Hello World"&lt;/span&gt; http://172.28.0.10:6152
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Result:&lt;/em&gt; &lt;code&gt;200 OK&lt;/code&gt; with body &lt;code&gt;'Response from BLUE'&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Test Forbidden Request
&lt;/h3&gt;

&lt;p&gt;From the host machine, send a payload containing the trigger word:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; pingora_client_1 curl &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"I am a rogue agent"&lt;/span&gt; http://172.28.0.10:6152
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Result:&lt;/em&gt; &lt;code&gt;500 Internal Server Error&lt;/code&gt; (or Connection Closed).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Proxy Logs:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WARN  Security Alert: Forbidden content 'rogue' detected in body!
ERROR Fail to proxy: SecurityPolicyViolation ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Lesson 13: Custom Errors
&lt;/h1&gt;

&lt;p&gt;In production environments, returning generic error pages (like &lt;code&gt;502 Bad Gateway&lt;/code&gt; or &lt;code&gt;500 Internal Server Error&lt;/code&gt;) provides a poor user experience. Modern APIs and web applications expect structured error responses—typically JSON for APIs (&lt;code&gt;{"error": "message"}&lt;/code&gt;) or branded HTML pages for browsers.&lt;/p&gt;

&lt;p&gt;Pingora provides a dedicated hook, &lt;strong&gt;&lt;code&gt;fail_to_proxy&lt;/code&gt;&lt;/strong&gt;, which acts as a global catch-all for any error that occurs during the request lifecycle. This allows you to inspect the error cause and generate a custom response before closing the connection.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Concepts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;fail_to_proxy&lt;/code&gt;&lt;/strong&gt;: This hook is triggered if any previous phase (e.g., &lt;code&gt;request_filter&lt;/code&gt;, &lt;code&gt;upstream_peer&lt;/code&gt;, &lt;code&gt;upstream_request_filter&lt;/code&gt;) returns an &lt;code&gt;Err&lt;/code&gt;. It replaces the default error handling logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ErrorType::Custom&lt;/code&gt;&lt;/strong&gt;: You can generate your own errors using &lt;code&gt;pingora::Error::new(ErrorType::Custom("MyReason"))&lt;/code&gt;. This allows you to "throw" specific exceptions (like "BlockedByWAF" or "MaintenanceMode") and "catch" them in the error handler to serve specific status codes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;FailToProxy&lt;/code&gt; Struct&lt;/strong&gt;: The return type of this hook. It instructs the server on two things:

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;error_code&lt;/code&gt;: The HTTP status code to log internally.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;can_reuse_downstream&lt;/code&gt;: Whether the TCP connection to the client is safe to reuse for another request (Keep-Alive). Usually, this is &lt;code&gt;false&lt;/code&gt; for fatal errors.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Code (&lt;code&gt;examples/13_custom_errors.rs&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;We implement a proxy that normally forwards to &lt;strong&gt;Upstream Blue&lt;/strong&gt;. However, if the user requests the path &lt;code&gt;/oops&lt;/code&gt;, we intentionally raise a custom error. We then catch this error in &lt;code&gt;fail_to_proxy&lt;/code&gt; and return a structured JSON response instead of a default error page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;http&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ResponseHeader&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;FailToProxy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;CustomErrorProxy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ProxyHttp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;CustomErrorProxy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new_ctx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_peer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"172.28.0.20"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"blue.pingora.local"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// 1. Trigger an error intentionally&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;request_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;where&lt;/span&gt;
        &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Send&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Sync&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="nf"&gt;.req_header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="py"&gt;.uri&lt;/span&gt;&lt;span class="nf"&gt;.path&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"/oops"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Raise a custom error. This immediately jumps to fail_to_proxy.&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ErrorType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Custom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SimulatedFailure"&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. Handle the error (The "Catch" block)&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;fail_to_proxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;FailToProxy&lt;/span&gt;
    &lt;span class="k"&gt;where&lt;/span&gt;
        &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Send&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Sync&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;error!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Entered fail_to_proxy with error: {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Map the internal error to an HTTP Status Code&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nn"&gt;ErrorType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Custom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SimulatedFailure"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="py"&gt;.etype&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="mi"&gt;400&lt;/span&gt; &lt;span class="c1"&gt;// Bad Request&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="mi"&gt;500&lt;/span&gt; &lt;span class="c1"&gt;// Internal Server Error&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="c1"&gt;// Construct the Custom JSON Response&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;r#"{{"status": "error", "code": {}, "message": "We caught a custom error!"}}"#&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;code&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;content_length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;ResponseHeader&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="nf"&gt;.insert_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Content-Type"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="nf"&gt;.insert_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Content-Length"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content_length&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// Write the response manually&lt;/span&gt;
        &lt;span class="c1"&gt;// - false: end_of_stream is false because body follows&lt;/span&gt;
        &lt;span class="c1"&gt;// - true: end_of_stream is true because this is the end&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="nf"&gt;.write_response_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="nf"&gt;.write_response_body&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// Return instruction to Pingora core&lt;/span&gt;
        &lt;span class="n"&gt;FailToProxy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;error_code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;can_reuse_downstream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Close connection for safety&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;http_proxy_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CustomErrorProxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="nf"&gt;.add_tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:6153"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Custom Error Proxy running on 0.0.0.0:6153"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Try: curl http://127.0.0.1:6153/oops"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;We will verify both the standard success path and the custom error path.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Start the Proxy
&lt;/h3&gt;

&lt;p&gt;Run the example inside the &lt;code&gt;pingora_dev&lt;/code&gt; container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 13_custom_errors
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Test Normal Request
&lt;/h3&gt;

&lt;p&gt;From the host machine, request the root path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; pingora_client_1 curl &lt;span class="nt"&gt;-v&lt;/span&gt; http://172.28.0.10:6153
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Result:&lt;/em&gt; &lt;code&gt;200 OK&lt;/code&gt; from Upstream Blue.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Test Custom Error
&lt;/h3&gt;

&lt;p&gt;From the host machine, request the trigger path &lt;code&gt;/oops&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; pingora_client_1 curl &lt;span class="nt"&gt;-v&lt;/span&gt; http://172.28.0.10:6153/oops
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Result:&lt;/em&gt; &lt;code&gt;400 Bad Request&lt;/code&gt;.&lt;br&gt;
The body should be our custom JSON:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"We caught a custom error!"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Lesson 14: HTTP/2 Support
&lt;/h1&gt;

&lt;p&gt;HTTP/2 (H2) is a major upgrade to the HTTP protocol, introducing binary framing, header compression (HPACK), and multiplexing (multiple requests over one TCP connection).&lt;/p&gt;

&lt;p&gt;Pingora supports HTTP/2 on both sides of the proxy:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Downstream (Client → Proxy)&lt;/strong&gt;: Negotiated via TLS ALPN (Application-Layer Protocol Negotiation).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Upstream (Proxy → Backend)&lt;/strong&gt;: Configured explicitly in the &lt;code&gt;HttpPeer&lt;/code&gt; options.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In this lesson, we configure &lt;strong&gt;End-to-End HTTP/2&lt;/strong&gt;. We will proxy traffic from an H2 client to our "Advanced" Nginx upstream, which is also listening on H2.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Concepts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ALPN (Application-Layer Protocol Negotiation)&lt;/strong&gt;: An extension to TLS where the client sends a list of supported protocols (e.g., &lt;code&gt;h2&lt;/code&gt;, &lt;code&gt;http/1.1&lt;/code&gt;) during the handshake. The server selects one. To enable this in Pingora, we call &lt;code&gt;tls_settings.enable_h2()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ALPN&lt;/code&gt; Enum&lt;/strong&gt;: When connecting to an upstream, we must tell Pingora which protocols to offer.

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ALPN::H2H1&lt;/code&gt;: Prefer HTTP/2, but fallback to HTTP/1.1 (safest).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ALPN::H2&lt;/code&gt;: Force HTTP/2.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;&lt;code&gt;SSL_CERT_FILE&lt;/code&gt;&lt;/strong&gt;: Pingora uses OpenSSL (via &lt;code&gt;boringssl&lt;/code&gt;). It respects standard environment variables. Because our Docker container has &lt;code&gt;SSL_CERT_FILE=/keys/ca.crt&lt;/code&gt; set, Pingora automatically trusts our lab's local Certificate Authority. We do &lt;em&gt;not&lt;/em&gt; need to disable certificate verification manually.&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Code (&lt;code&gt;examples/14_http2_support.rs&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;We configure the listener on port &lt;code&gt;6154&lt;/code&gt; to accept H2. We configure the upstream peer to target &lt;code&gt;advanced.pingora.local:443&lt;/code&gt; (our Nginx container) and offer H2.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;listeners&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;tls&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;TlsSettings&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;ALPN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;path&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Http2Proxy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ProxyHttp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;Http2Proxy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new_ctx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_peer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Target the "Advanced" Nginx upstream on port 443 (HTTPS)&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"172.28.0.22"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;443&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// true = Enable TLS for the upstream connection&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"advanced.pingora.local"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="p"&gt;));&lt;/span&gt;

        &lt;span class="c1"&gt;// Offer HTTP/2 to the upstream, fallback to HTTP/1.1&lt;/span&gt;
        &lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="py"&gt;.options.alpn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;ALPN&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;H2H1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Forwarding to Upstream Advanced via HTTPS (ALPN: H2/H1)"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_request_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;upstream_request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;RequestHeader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;where&lt;/span&gt;
        &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Send&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Sync&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Nginx requires the Host header to match the server_name&lt;/span&gt;
        &lt;span class="n"&gt;upstream_request&lt;/span&gt;&lt;span class="nf"&gt;.insert_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Host"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"advanced.pingora.local"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;http_proxy_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Http2Proxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 1. Configure Downstream TLS&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;cert_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"/keys/server.crt"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;key_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"/keys/server.key"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nn"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cert_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.exists&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Missing keys at {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cert_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;tls_settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;TlsSettings&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;intermediate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cert_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// CRITICAL: This enables H2 negotiation with the Client&lt;/span&gt;
    &lt;span class="n"&gt;tls_settings&lt;/span&gt;&lt;span class="nf"&gt;.enable_h2&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="nf"&gt;.add_tls_with_settings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:6154"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tls_settings&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"HTTP/2 Proxy running on 0.0.0.0:6154"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;We verify that the connection uses HTTP/2 using &lt;code&gt;curl&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Start the Proxy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;debug cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 14_http2_support
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Note: We use &lt;code&gt;debug&lt;/code&gt; logs to see the ALPN handshake details.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Test with Curl
&lt;/h3&gt;

&lt;p&gt;From the host machine, we run &lt;code&gt;curl&lt;/code&gt; inside the client container. We use the &lt;code&gt;--http2&lt;/code&gt; flag to encourage H2 usage.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; pingora_client_1 curl &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nt"&gt;--http2&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cacert&lt;/span&gt; /keys/ca.crt &lt;span class="se"&gt;\&lt;/span&gt;
  https://dev.pingora.local:6154
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Result Analysis
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Handshake&lt;/strong&gt;: You should see &lt;code&gt;ALPN: server accepted h2&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Protocol&lt;/strong&gt;: &lt;code&gt;using HTTP/2&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Certificate&lt;/strong&gt;: &lt;code&gt;SSL certificate verify ok&lt;/code&gt;. This confirms that our &lt;code&gt;SSL_CERT_FILE&lt;/code&gt; environment variable correctly pointed to the CA, allowing the client to trust the proxy, and the proxy to trust the upstream.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Response&lt;/strong&gt;: &lt;code&gt;Response from Advanced Upstream (HTTPS + HTTP/2)&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Lesson 15: H2C (HTTP/2 Cleartext)
&lt;/h1&gt;

&lt;p&gt;While HTTP/2 is almost exclusively used over HTTPS (TLS) on the public internet, the specification also defines a cleartext version known as &lt;strong&gt;h2c&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;h2c&lt;/strong&gt; is widely used in internal infrastructure, particularly for &lt;strong&gt;gRPC&lt;/strong&gt; microservices running inside a secure cluster (like Kubernetes). It allows services to benefit from HTTP/2's multiplexing and binary framing without the CPU overhead of encryption/decryption at every hop.&lt;/p&gt;

&lt;p&gt;In this lesson, we demonstrate &lt;strong&gt;Protocol Translation&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Downstream (Client → Proxy)&lt;/strong&gt;: Standard HTTP/1.1.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Upstream (Proxy → Backend)&lt;/strong&gt;: HTTP/2 Cleartext (h2c).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key Concepts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Forcing HTTP/2&lt;/strong&gt;: When using TLS, the protocol is negotiated via ALPN. With Cleartext, there is no handshake to negotiate the protocol. Therefore, we must explicitly tell Pingora to treat the connection as HTTP/2 immediately upon connecting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ALPN::H2&lt;/code&gt;&lt;/strong&gt;: By setting &lt;code&gt;peer.options.alpn = ALPN::H2&lt;/code&gt; combined with &lt;code&gt;tls: false&lt;/code&gt;, we instruct the connection pool to start the HTTP/2 "preface" sequence immediately.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No Fallback&lt;/strong&gt;: Unlike &lt;code&gt;ALPN::H2H1&lt;/code&gt;, there is no fallback mechanism here. If the upstream server does not speak H2C, the connection will fail instantly with a protocol error.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Code (&lt;code&gt;examples/15_h2c_support.rs&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;We configure the proxy to listen for standard HTTP traffic on port &lt;code&gt;6155&lt;/code&gt;. It forwards requests to the lab's &lt;strong&gt;Advanced Upstream&lt;/strong&gt; on port &lt;strong&gt;8081&lt;/strong&gt;, which is configured to accept H2C traffic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;ALPN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;H2cProxy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ProxyHttp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;H2cProxy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new_ctx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_peer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Target the "Advanced" Nginx upstream on port 8081 (H2C)&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"172.28.0.22"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8081&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// TLS is false (Cleartext)&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="s"&gt;"advanced.pingora.local"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="p"&gt;));&lt;/span&gt;

        &lt;span class="c1"&gt;// We must Force H2.&lt;/span&gt;
        &lt;span class="c1"&gt;// There is no negotiation (ALPN) in Cleartext; we just send H2 frames.&lt;/span&gt;
        &lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="py"&gt;.options.alpn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;ALPN&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;H2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Forwarding to Upstream Advanced via H2C (Port 8081)"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_request_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;upstream_request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;RequestHeader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 
    &lt;span class="k"&gt;where&lt;/span&gt;
        &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Send&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Sync&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Nginx requires the Host header to match the server block&lt;/span&gt;
        &lt;span class="n"&gt;upstream_request&lt;/span&gt;&lt;span class="nf"&gt;.insert_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Host"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"advanced.pingora.local"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;http_proxy_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;H2cProxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// We accept standard HTTP/1.1 on the front end for simplicity&lt;/span&gt;
    &lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="nf"&gt;.add_tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:6155"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Proxy running on 0.0.0.0:6155 (HTTP/1.1) -&amp;gt; Forwarding to Upstream (H2C)"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;We verify that the proxy accepts HTTP/1.1 but receives a response from the H2C-only upstream port.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Start the Proxy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 15_h2c_support
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Test with Curl
&lt;/h3&gt;

&lt;p&gt;We use a standard &lt;code&gt;curl&lt;/code&gt; command (which defaults to HTTP/1.1).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; pingora_client_1 curl &lt;span class="nt"&gt;-v&lt;/span&gt; http://172.28.0.10:6155
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Result Analysis
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Client Side&lt;/strong&gt;: &lt;code&gt;HTTP/1.1 200 OK&lt;/code&gt;. The client (curl) spoke HTTP/1.1 to the proxy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proxy Logs&lt;/strong&gt;: &lt;code&gt;Forwarding to Upstream Advanced via H2C&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Body&lt;/strong&gt;: &lt;code&gt;Response from Advanced Upstream (H2C - Cleartext HTTP/2)&lt;/code&gt;. This specific body text confirms that we successfully hit the Nginx server block listening on port 8081.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Module 3: Upstream Management
&lt;/h1&gt;

&lt;p&gt;In the previous modules, we focused on the request lifecycle—how the proxy accepts, routes, and modifies traffic from the client. Now, we turn our attention to the &lt;strong&gt;Upstream&lt;/strong&gt;: the backend services your proxy protects and serves.&lt;/p&gt;

&lt;p&gt;A production proxy rarely talks to a single, static IP address. It must navigate dynamic environments where services scale up and down, reside behind secure TLS layers, or communicate over specialized protocols like gRPC and WebSockets.&lt;/p&gt;

&lt;p&gt;In this module, we will explore the mechanics of connectivity. You will learn how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Discover Peers&lt;/strong&gt;: Switch from hardcoded IPs to dynamic DNS resolution and Unix Domain Sockets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secure Connections&lt;/strong&gt;: Manage TLS handshakes, SNI routing, and Mutual TLS (mTLS) authentication.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Handle Advanced Protocols&lt;/strong&gt;: Tunnel traffic via &lt;code&gt;CONNECT&lt;/code&gt;, upgrade connections for WebSockets, and proxy gRPC streams.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tune Performance&lt;/strong&gt;: optimize connection reuse (Keep-Alive) and configure granular timeouts to ensure resilience.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Lesson 16: Static Peer
&lt;/h1&gt;

&lt;p&gt;The simplest way to connect to an upstream service is by using a &lt;strong&gt;Static Peer&lt;/strong&gt;. In this configuration, the IP address and port of the backend server are known ahead of time and do not change (or change very rarely).&lt;/p&gt;

&lt;p&gt;While modern cloud environments often rely on dynamic service discovery, static definitions are still widely used for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Connecting to legacy infrastructure with fixed IPs.&lt;/li&gt;
&lt;li&gt;Routing traffic to local sidecars (e.g., sending to &lt;code&gt;127.0.0.1:8080&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Simple, high-performance setups where DNS overhead is undesirable.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key Concepts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;HttpPeer&lt;/code&gt;&lt;/strong&gt;: This is the fundamental struct Pingora uses to represent a backend connection. It encapsulates three critical pieces of information:

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Address&lt;/strong&gt;: A &lt;code&gt;SocketAddr&lt;/code&gt; (IP + Port).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TLS Config&lt;/strong&gt;: A boolean flag (&lt;code&gt;true&lt;/code&gt; for HTTPS, &lt;code&gt;false&lt;/code&gt; for HTTP).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SNI (Server Name Indication)&lt;/strong&gt;: The domain name associated with the backend.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Role of SNI&lt;/strong&gt;: Even when &lt;code&gt;use_tls&lt;/code&gt; is false, providing a valid SNI string is important. Pingora often uses this string to populate the default &lt;code&gt;Host&lt;/code&gt; header if the request doesn't explicitly provide one.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Code (&lt;code&gt;examples/16_static_peer.rs&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;We configure the proxy to route all traffic to &lt;strong&gt;Upstream Blue&lt;/strong&gt; at the hardcoded address &lt;code&gt;172.28.0.20:8080&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;StaticPeerProxy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ProxyHttp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;StaticPeerProxy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new_ctx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_peer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// 1. Define the Socket Address&lt;/span&gt;
        &lt;span class="c1"&gt;// In a static setup, this is hardcoded or loaded from a config file.&lt;/span&gt;
        &lt;span class="c1"&gt;// It accepts any type that implements ToSocketAddrs (e.g., tuple or string)&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"172.28.0.20"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// 2. Configure TLS&lt;/span&gt;
        &lt;span class="c1"&gt;// false = Plaintext (HTTP)&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;use_tls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// 3. Define SNI&lt;/span&gt;
        &lt;span class="c1"&gt;// Used for TLS handshake and Host header generation&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;sni&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"blue.pingora.local"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;use_tls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sni&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Connecting to static peer: {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;http_proxy_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;StaticPeerProxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="nf"&gt;.add_tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:6160"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Static Peer Proxy running on 0.0.0.0:6160"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;We verify that the proxy successfully connects to the specific static IP provided.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Start the Proxy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 16_static_peer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Test Connection
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; pingora_client_1 curl &lt;span class="nt"&gt;-v&lt;/span&gt; http://172.28.0.10:6160
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Result Analysis
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Response&lt;/strong&gt;: &lt;code&gt;200 OK&lt;/code&gt; containing &lt;code&gt;'Response from BLUE'&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logs&lt;/strong&gt;: You should see the log entry &lt;code&gt;Connecting to static peer: ("172.28.0.20", 8080)&lt;/code&gt;. This confirms the &lt;code&gt;upstream_peer&lt;/code&gt; hook executed and selected the correct hardcoded address.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Lesson 17: DNS Peer
&lt;/h1&gt;

&lt;p&gt;In dynamic environments like Kubernetes, AWS, or Docker, backend IP addresses change frequently. Hardcoding IPs is brittle; instead, we rely on DNS to resolve service names (e.g., &lt;code&gt;blue.pingora.local&lt;/code&gt;) to their current IP addresses at runtime.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Concepts
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Async Resolution&lt;/strong&gt;: Standard Rust DNS resolution (&lt;code&gt;std::net::ToSocketAddrs&lt;/code&gt;) is &lt;strong&gt;blocking&lt;/strong&gt;. Using it inside Pingora's async runtime will freeze the worker thread, causing massive latency spikes. You &lt;strong&gt;must&lt;/strong&gt; use an async resolver like &lt;code&gt;tokio::net::lookup_host&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic &lt;code&gt;HttpPeer&lt;/code&gt;&lt;/strong&gt;: Instead of a static constant, we create the &lt;code&gt;HttpPeer&lt;/code&gt; struct dynamically inside &lt;code&gt;upstream_peer&lt;/code&gt; based on the result of the DNS lookup.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error Handling&lt;/strong&gt;: DNS lookups can fail (NXDOMAIN, timeouts). Robust proxies must catch these errors and return appropriate internal error codes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Code (&lt;code&gt;examples/17_dns_peer.rs&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;We perform an asynchronous DNS lookup for &lt;code&gt;blue.pingora.local&lt;/code&gt; and use the resolved IP to construct the peer connection.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;net&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;lookup_host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;DnsPeerProxy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ProxyHttp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;DnsPeerProxy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new_ctx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_peer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;hostname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"blue.pingora.local"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}:{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Resolving host: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// 1. Perform Async DNS Lookup&lt;/span&gt;
        &lt;span class="c1"&gt;// We use tokio::net::lookup_host to avoid blocking the runtime.&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;addrs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;lookup_host&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;
            &lt;span class="nf"&gt;.map_err&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;_e&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ErrorType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Custom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"DNSResolutionFailed"&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// 2. Select an Address&lt;/span&gt;
        &lt;span class="c1"&gt;// A hostname might resolve to multiple IPs. We pick the first one.&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;addrs&lt;/span&gt;&lt;span class="nf"&gt;.next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Resolved {} -&amp;gt; {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hostname&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
            &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nd"&gt;error!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"DNS lookup returned no records for {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ErrorType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Custom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"DNSNoRecords"&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;http_proxy_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DnsPeerProxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="nf"&gt;.add_tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:6161"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"DNS Peer Proxy running on 0.0.0.0:6161"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;We verify that the proxy correctly resolves the internal Docker DNS name to an IP address.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Start the Proxy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 17_dns_peer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Test Connection
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; pingora_client_1 curl &lt;span class="nt"&gt;-v&lt;/span&gt; http://172.28.0.10:6161
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Result Analysis
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Response&lt;/strong&gt;: &lt;code&gt;200 OK&lt;/code&gt; from Upstream Blue.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logs&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   INFO  Resolving host: blue.pingora.local:8080
   INFO  Resolved blue.pingora.local -&amp;gt; 172.28.0.20:8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The logs confirm that &lt;code&gt;lookup_host&lt;/code&gt; successfully returned the internal IP address &lt;code&gt;172.28.0.20&lt;/code&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Lesson 18: Unix Domain Socket (UDS) Peer
&lt;/h1&gt;

&lt;p&gt;In high-performance local environments—such as communicating with a sidecar proxy (e.g., Envoy, Linkerd) or a local PHP-FPM process—using TCP/IP can introduce unnecessary overhead. &lt;strong&gt;Unix Domain Sockets (UDS)&lt;/strong&gt; allow processes on the same machine to communicate via the kernel without touching the network stack.&lt;/p&gt;

&lt;p&gt;Pingora supports proxying traffic to UDS endpoints natively. This is often used to offload TLS termination or WAF duties to Pingora while the application logic runs locally on a socket.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Concepts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;HttpPeer::new_uds()&lt;/code&gt;&lt;/strong&gt;: This constructor is distinct from the standard &lt;code&gt;new()&lt;/code&gt;. It takes a file system path (e.g., &lt;code&gt;/tmp/upstream.sock&lt;/code&gt;) instead of an IP address. Note that it returns a &lt;code&gt;Result&lt;/code&gt;, so it must be unwrapped with &lt;code&gt;?&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The "Host" in UDS&lt;/strong&gt;: Even though we are connecting to a file, the HTTP protocol still requires a &lt;code&gt;Host&lt;/code&gt; header. You must still provide a valid SNI/Host string (e.g., &lt;code&gt;"uds.local"&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mocking in Rust&lt;/strong&gt;: Since our lab environment lacks a real UDS upstream (like a local Python server), we implement a mock upstream using &lt;code&gt;tokio::net::UnixListener&lt;/code&gt;. Because Pingora controls the main thread, we spawn this mock server in a background thread with its own Tokio runtime.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Code (&lt;code&gt;examples/18_uds_peer.rs&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;We launch a background thread to act as the "Upstream Server," listening on &lt;code&gt;/tmp/upstream.sock&lt;/code&gt;. The Proxy then accepts traffic on TCP port &lt;code&gt;6162&lt;/code&gt; and tunnels it to that socket file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;AsyncReadExt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AsyncWriteExt&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;net&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;UnixListener&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;UdsProxy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ProxyHttp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;UdsProxy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new_ctx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_peer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Connect to the Unix Domain Socket file&lt;/span&gt;
        &lt;span class="c1"&gt;// Note: new_uds returns a Result, so we use '?'&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new_uds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"/tmp/upstream.sock"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// TLS is rarely used over UDS&lt;/span&gt;
            &lt;span class="s"&gt;"uds.local"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Forwarding to UDS: /tmp/upstream.sock"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// A simple Mock Server that listens on a Unix Socket&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;run_mock_uds_server&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;'static&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// 1. Clean up old socket file if it exists&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;remove_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. Bind to the path&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;listener&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;UnixListener&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to bind UDS"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Mock Upstream running at {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 3. Accept loop&lt;/span&gt;
    &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;move&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_addr&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;listener&lt;/span&gt;&lt;span class="nf"&gt;.accept&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;move&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="c1"&gt;// Simple Read (drain request)&lt;/span&gt;
                    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0u8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;4096&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
                    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="nf"&gt;.read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

                    &lt;span class="c1"&gt;// Simple Write (static response)&lt;/span&gt;
                    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"HTTP/1.1 200 OK&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s"&gt;Content-Length: 13&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s"&gt;Connection: close&lt;/span&gt;&lt;span class="se"&gt;\r\n\r\n&lt;/span&gt;&lt;span class="s"&gt;Hello via UDS"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="nf"&gt;.write_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="nf"&gt;.as_bytes&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="p"&gt;});&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Spawn our mock upstream server in the background&lt;/span&gt;
    &lt;span class="c1"&gt;// We use a separate thread + runtime to avoid conflicting with Pingora's main loop&lt;/span&gt;
    &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;move&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;rt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Runtime&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;rt&lt;/span&gt;&lt;span class="nf"&gt;.block_on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;run_mock_uds_server&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/tmp/upstream.sock"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;park&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Keep the thread alive&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;http_proxy_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UdsProxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="nf"&gt;.add_tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:6162"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"UDS Proxy running on 0.0.0.0:6162"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;We verify that the proxy can bridge a standard TCP HTTP request to a local Unix Domain Socket.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Start the Proxy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 18_uds_peer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Test Connection
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; pingora_client_1 curl &lt;span class="nt"&gt;-v&lt;/span&gt; http://172.28.0.10:6162
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Result Analysis
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Logs&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   INFO  Mock Upstream running at /tmp/upstream.sock
   INFO  Forwarding to UDS: /tmp/upstream.sock
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Response&lt;/strong&gt;: &lt;code&gt;200 OK&lt;/code&gt; with body &lt;code&gt;Hello via UDS&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Lesson 19: Peer Options
&lt;/h1&gt;

&lt;p&gt;In a complex microservices architecture, not all upstreams are created equal. A real-time bidding server might need to fail fast (e.g., 50ms), while a legacy reporting service might need a generous 30-second window.&lt;/p&gt;

&lt;p&gt;Pingora allows granular control over connection parameters via the &lt;code&gt;peer.options&lt;/code&gt; struct. This allows you to apply different Service Level Agreements (SLAs) dynamically per request.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Concepts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;connection_timeout&lt;/code&gt;&lt;/strong&gt;: The maximum time allowed to establish the TCP (and TLS) handshake.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;read_timeout&lt;/code&gt;&lt;/strong&gt;: The maximum time allowed between bytes when reading the response body.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The "Blackhole" Pattern&lt;/strong&gt;: To test connection timeouts reliably in a local lab, we route traffic to &lt;code&gt;192.0.2.1&lt;/code&gt;. This is a reserved "TEST-NET" IP address that is guaranteed not to route, causing the connection attempt to hang until the timeout fires.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;fail_to_proxy&lt;/code&gt;&lt;/strong&gt;: Timeouts in Pingora generate specific error types (&lt;code&gt;ConnectTimedout&lt;/code&gt;, &lt;code&gt;ReadTimedout&lt;/code&gt;). We catch these here to return a specific &lt;code&gt;504 Gateway Timeout&lt;/code&gt; status instead of a generic 502.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Code (&lt;code&gt;examples/19_peer_options.rs&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;We configure the proxy to switch behaviors based on the URL path:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;/normal&lt;/code&gt;&lt;/strong&gt;: Connects to the standard upstream with a 2-second timeout.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;/timeout&lt;/code&gt;&lt;/strong&gt;: Attempts to connect to the "Blackhole" IP with a &lt;strong&gt;100ms&lt;/strong&gt; timeout. This guarantees a &lt;code&gt;ConnectTimedout&lt;/code&gt; error.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;FailToProxy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;TimeoutProxy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ProxyHttp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;TimeoutProxy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new_ctx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_peer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="nf"&gt;.req_header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="py"&gt;.uri&lt;/span&gt;&lt;span class="nf"&gt;.path&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// 1. Determine Target based on path&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sni&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"/timeout"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Case A: The "Blackhole"&lt;/span&gt;
            &lt;span class="c1"&gt;// 192.0.2.1 is a reserved Test-Net IP. It will not respond.&lt;/span&gt;
            &lt;span class="c1"&gt;// We set a 100ms timeout to fail fast.&lt;/span&gt;
            &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Routing to Blackhole IP (192.0.2.1) to force timeout..."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="s"&gt;"192.0.2.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"blackhole.local"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_millis&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Case B: The Happy Path&lt;/span&gt;
            &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Routing to Blue (Standard Timeout)"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="s"&gt;"172.28.0.20"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"blue.pingora.local"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_secs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sni&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;

        &lt;span class="c1"&gt;// 2. Apply the specific timeout config&lt;/span&gt;
        &lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="py"&gt;.options.connection_timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="c1"&gt;// We set generous read/write timeouts to ensure only connection time is tested&lt;/span&gt;
        &lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="py"&gt;.options.read_timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_secs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; 
        &lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="py"&gt;.options.write_timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_secs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;fail_to_proxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;FailToProxy&lt;/span&gt;
    &lt;span class="k"&gt;where&lt;/span&gt;
        &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Send&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Sync&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// 3. Handle the Timeout Error&lt;/span&gt;
        &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="py"&gt;.etype&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nn"&gt;ErrorType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ConnectTimedout&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nn"&gt;ErrorType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ReadTimedout&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nd"&gt;warn!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Custom Error Handler: Connection Timed Out!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="nf"&gt;.respond_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;504&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;FailToProxy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;error_code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;504&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;can_reuse_downstream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;};&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// Fallback for other errors&lt;/span&gt;
                &lt;span class="nd"&gt;warn!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Fail to proxy: {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="nf"&gt;.respond_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;502&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="n"&gt;FailToProxy&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;error_code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;502&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;can_reuse_downstream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;http_proxy_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TimeoutProxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="nf"&gt;.add_tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:6163"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Timeout Config Proxy running on 0.0.0.0:6163"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Start the Proxy&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 19_peer_options
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Test Normal Request&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; pingora_client_1 curl &lt;span class="nt"&gt;-v&lt;/span&gt; http://172.28.0.10:6163/normal
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Result:&lt;/em&gt; &lt;code&gt;200 OK&lt;/code&gt; from Blue.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Test Timeout Request&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; pingora_client_1 curl &lt;span class="nt"&gt;-v&lt;/span&gt; http://172.28.0.10:6163/timeout
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Result:&lt;/em&gt; After exactly 100ms, you will receive &lt;code&gt;504 Gateway Timeout&lt;/code&gt;.&lt;br&gt;
&lt;em&gt;Logs:&lt;/em&gt; &lt;code&gt;Routing to Blackhole IP...&lt;/code&gt; followed by &lt;code&gt;Custom Error Handler: Connection Timed Out!&lt;/code&gt;.&lt;/p&gt;
&lt;h1&gt;
  
  
  Lesson 20: SNI Routing
&lt;/h1&gt;

&lt;p&gt;In modern multi-tenant architectures (like CDNs or SaaS platforms), a single backend IP address often serves thousands of different domains. To route traffic correctly, the proxy must tell the upstream server &lt;em&gt;which&lt;/em&gt; domain it is trying to reach during the TLS handshake. This is done via the &lt;strong&gt;SNI (Server Name Indication)&lt;/strong&gt; extension.&lt;/p&gt;

&lt;p&gt;In this lesson, we will build a proxy that dynamically selects the SNI based on the request path.&lt;/p&gt;
&lt;h2&gt;
  
  
  Key Concepts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SNI (Server Name Indication):&lt;/strong&gt; A TLS extension where the client (Pingora) sends the hostname it wants to connect to in the initial &lt;code&gt;ClientHello&lt;/code&gt; packet. This allows the backend (Nginx) to select the correct certificate and server block.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context (&lt;code&gt;CTX&lt;/code&gt;)&lt;/strong&gt;: We use the request context to store the decision made in &lt;code&gt;request_filter&lt;/code&gt; (the routing logic) so it can be retrieved later in &lt;code&gt;upstream_peer&lt;/code&gt; (where the TLS connection is created).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Host Header vs. SNI&lt;/strong&gt;: While they often match, they are distinct. SNI is Layer 4 (TLS), while the Host header is Layer 7 (HTTP). To avoid confusion or rejection by the upstream, it is best practice to keep them in sync.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wildcard Certificates&lt;/strong&gt;: In our lab, the upstream Nginx server (&lt;code&gt;advanced.pingora.local&lt;/code&gt;) holds a wildcard certificate for &lt;code&gt;*.api.pingora.local&lt;/code&gt;. This allows it to accept connections for both &lt;code&gt;v1.api.pingora.local&lt;/code&gt; and &lt;code&gt;v2.api.pingora.local&lt;/code&gt; using the same certificate.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  The Code (&lt;code&gt;examples/20_sni_routing.rs&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;We map incoming paths to different subdomains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/v1/...&lt;/code&gt; → &lt;code&gt;v1.api.pingora.local&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Other → &lt;code&gt;v2.api.pingora.local&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We use the &lt;code&gt;SniCtx&lt;/code&gt; struct to pass this decision from the filter phase to the peer selection phase.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;SniRouter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// 1. Define Context to share data between hooks&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;SniCtx&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;target_host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ProxyHttp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;SniRouter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SniCtx&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Initialize the context for each new request&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new_ctx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;SniCtx&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;target_host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. Determine Routing Logic early (Layer 7 Filter)&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;request_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;where&lt;/span&gt;
        &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Send&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Sync&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="nf"&gt;.req_header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="py"&gt;.uri&lt;/span&gt;&lt;span class="nf"&gt;.path&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// Calculate the target hostname based on path prefix&lt;/span&gt;
        &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.target_host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="nf"&gt;.starts_with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/v1"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;"v1.api.pingora.local"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;"v2.api.pingora.local"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// 3. Configure the Connection (Layer 4 Peer Selection)&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_peer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"172.28.0.22"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;443&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Retrieve the SNI we decided on earlier&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;sni&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.target_host&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// Create the peer with TLS enabled (true)&lt;/span&gt;
        &lt;span class="c1"&gt;// Because the upstream cert covers *.api.pingora.local, &lt;/span&gt;
        &lt;span class="c1"&gt;// both "v1" and "v2" SNIs will be accepted and validated.&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sni&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;

        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Connecting to IP: {:?} with SNI: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sni&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// 4. Sync the Host Header (Layer 7 Request Modification)&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_request_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;upstream_request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;RequestHeader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;where&lt;/span&gt;
        &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Send&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Sync&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Ensure the Host header matches the SNI to satisfy the web server&lt;/span&gt;
        &lt;span class="n"&gt;upstream_request&lt;/span&gt;&lt;span class="nf"&gt;.insert_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Host"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.target_host&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;http_proxy_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SniRouter&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="nf"&gt;.add_tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:6164"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SNI Routing Proxy running on 0.0.0.0:6164"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;We will verify that Pingora connects to the same upstream IP but presents different identities (SNI) depending on the URL.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Start the Proxy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 20_sni_routing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Test V1 Path
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; pingora_client_1 curl &lt;span class="nt"&gt;-v&lt;/span&gt; http://172.28.0.10:6164/v1/test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Result:&lt;/em&gt; &lt;code&gt;200 OK&lt;/code&gt;.&lt;br&gt;
&lt;em&gt;Log:&lt;/em&gt; &lt;code&gt;Connecting to ... SNI: v1.api.pingora.local&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Test V2 Path
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; pingora_client_1 curl &lt;span class="nt"&gt;-v&lt;/span&gt; http://172.28.0.10:6164/v2/test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;em&gt;Result:&lt;/em&gt; &lt;code&gt;200 OK&lt;/code&gt;.&lt;br&gt;
&lt;em&gt;Log:&lt;/em&gt; &lt;code&gt;Connecting to ... SNI: v2.api.pingora.local&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This works seamlessly because we updated our certificate infrastructure to include &lt;code&gt;*.api.pingora.local&lt;/code&gt; in the upstream certificate. Pingora successfully negotiates a secure TLS connection for both subdomains using the same backend IP.&lt;/p&gt;
&lt;h1&gt;
  
  
  Lesson 21: Mutual TLS (mTLS)
&lt;/h1&gt;

&lt;p&gt;Standard TLS (HTTPS) involves one-way authentication: the client validates the server's identity. &lt;strong&gt;Mutual TLS (mTLS)&lt;/strong&gt; adds a second layer of security where the server also validates the client's identity. This is commonly used in Zero Trust architectures to ensure only authorized proxies or microservices can talk to sensitive backends.&lt;/p&gt;

&lt;p&gt;In this lesson, we will configure Pingora to present a &lt;strong&gt;Client Certificate&lt;/strong&gt; when connecting to a secured upstream.&lt;/p&gt;
&lt;h2&gt;
  
  
  Key Concepts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Client Certificate (&lt;code&gt;client.crt&lt;/code&gt;)&lt;/strong&gt;: A digital identity card for the proxy. It is signed by a Root CA trusted by the upstream.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Private Key (&lt;code&gt;client.key&lt;/code&gt;)&lt;/strong&gt;: The secret key used to prove ownership of the certificate during the handshake.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;CertKey&lt;/code&gt; Struct&lt;/strong&gt;: Pingora's internal wrapper for an &lt;code&gt;X509&lt;/code&gt; certificate chain and a &lt;code&gt;PKey&lt;/code&gt; private key.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;upstream_peer&lt;/code&gt; Hook&lt;/strong&gt;: We attach the credentials to the &lt;code&gt;HttpPeer&lt;/code&gt; struct dynamically. We can choose to send them for some requests (e.g., &lt;code&gt;/auth&lt;/code&gt;) and omit them for others (e.g., &lt;code&gt;/anon&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance&lt;/strong&gt;: Parsing certificates from PEM files is CPU-intensive. We must load them into memory &lt;strong&gt;once&lt;/strong&gt; during startup and wrap them in an &lt;code&gt;Arc&lt;/code&gt; (Atomic Reference Counted) pointer to share them efficiently across thousands of requests.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  The Code (&lt;code&gt;examples/21_mutual_tls.rs&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;We create a proxy that connects to the &lt;strong&gt;Advanced Upstream&lt;/strong&gt; on port &lt;code&gt;8443&lt;/code&gt;, which enforces mTLS.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the path is &lt;code&gt;/auth&lt;/code&gt;, we attach the client certificate.&lt;/li&gt;
&lt;li&gt;If the path is &lt;code&gt;/anon&lt;/code&gt;, we attempt to connect without one.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;utils&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;tls&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CertKey&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;tls&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;x509&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;X509&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;tls&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;pkey&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;PKey&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// The struct holds the parsed Certificate/Key pair in an Arc.&lt;/span&gt;
&lt;span class="c1"&gt;// This is thread-safe and cheap to clone for every request.&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;MtlsProxy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;client_cert&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CertKey&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ProxyHttp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;MtlsProxy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new_ctx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_peer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="nf"&gt;.req_header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="py"&gt;.uri&lt;/span&gt;&lt;span class="nf"&gt;.path&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// Target the mTLS port (8443) on the Advanced Upstream&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"172.28.0.22"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8443&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;sni&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"advanced.pingora.local"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sni&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;

        &lt;span class="c1"&gt;// Decision Logic: To Auth or Not to Auth?&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"/auth"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Case 1: Authenticated&lt;/span&gt;
            &lt;span class="c1"&gt;// We attach the Arc&amp;lt;CertKey&amp;gt;. Pingora will use this in the TLS handshake.&lt;/span&gt;
            &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Attaching Client Certificate for /auth request..."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="py"&gt;.client_cert_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.client_cert&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Case 2: Anonymous&lt;/span&gt;
            &lt;span class="c1"&gt;// We explicitly leave client_cert_key as None.&lt;/span&gt;
            &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Connecting anonymously (no cert) for {}..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// 1. Load Credentials at Startup (Critical for Performance)&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;cert_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"/keys/client.crt"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;key_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"/keys/client.key"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;path&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cert_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.exists&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Client keys missing at {}. Run 00-setup-certs.sh"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cert_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;cert_bytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cert_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;key_bytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. Parse PEM into OpenSSL Objects&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;x509&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;X509&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_pem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;cert_bytes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="nf"&gt;.map_err&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to parse certificate: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;PKey&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;private_key_from_pem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;key_bytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.map_err&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to parse private key: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// 3. Wrap in CertKey and Arc&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;cert_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;CertKey&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x509&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;client_cert&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cert_key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;my_proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MtlsProxy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;client_cert&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;http_proxy_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_service&lt;/span&gt;&lt;span class="nf"&gt;.add_tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:6165"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"mTLS Proxy running on 0.0.0.0:6165"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_service&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;We will verify that the upstream server rejects anonymous connections but accepts authenticated ones.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Start the Proxy
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 21_mutual_tls
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  2. Test Anonymous Access (Expected Failure)
&lt;/h3&gt;

&lt;p&gt;The upstream Nginx is configured with &lt;code&gt;ssl_verify_client on&lt;/code&gt;. It should reject connections that lack a certificate.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; pingora_client_1 curl &lt;span class="nt"&gt;-v&lt;/span&gt; http://172.28.0.10:6165/anon
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Result:&lt;/em&gt; &lt;code&gt;400 Bad Request&lt;/code&gt;.&lt;br&gt;
&lt;em&gt;Body:&lt;/em&gt; The HTML error page contains &lt;code&gt;&amp;lt;center&amp;gt;No required SSL certificate was sent&amp;lt;/center&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Test Authenticated Access (Expected Success)
&lt;/h3&gt;

&lt;p&gt;We hit the &lt;code&gt;/auth&lt;/code&gt; path, causing Pingora to attach the client certificate.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; pingora_client_1 curl &lt;span class="nt"&gt;-v&lt;/span&gt; http://172.28.0.10:6165/auth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Result:&lt;/em&gt; &lt;code&gt;200 OK&lt;/code&gt;.&lt;br&gt;
&lt;em&gt;Body:&lt;/em&gt; &lt;code&gt;Response from mTLS Protected Upstream. Hello, Authenticated Client!&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  Lesson 22: Proxy Connect (Tunneling)
&lt;/h1&gt;
&lt;h2&gt;
  
  
  1. Proxy Tunnels
&lt;/h2&gt;

&lt;p&gt;In enterprise environments, direct internet access is rarely granted to backend servers. Instead, all outbound traffic must pass through a designated &lt;strong&gt;Forward Proxy&lt;/strong&gt; (like Squid, Zscaler, or an AWS NAT Gateway).&lt;/p&gt;

&lt;p&gt;If your Pingora instance is running in such a restricted environment, it cannot simply open a TCP connection to &lt;code&gt;api.stripe.com&lt;/code&gt;. It must ask the corporate proxy to open that connection on its behalf. This is achieved using the HTTP &lt;strong&gt;&lt;code&gt;CONNECT&lt;/code&gt;&lt;/strong&gt; method.&lt;/p&gt;

&lt;p&gt;In this lesson, we will implement a custom connector that allows Pingora to "tunnel" through an intermediate proxy to reach a secure upstream.&lt;/p&gt;


&lt;h2&gt;
  
  
  2. Understanding HTTP &lt;code&gt;CONNECT&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;CONNECT&lt;/code&gt; method is unique because it asks the proxy to stop acting like an HTTP parser and start acting like a blind pipe. This is essential for secure traffic (HTTPS), because the intermediate proxy cannot read the encrypted data—it just shovels bytes back and forth.&lt;/p&gt;
&lt;h3&gt;
  
  
  The "Double Handshake"
&lt;/h3&gt;

&lt;p&gt;Tunneling involves two distinct connection phases:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Setup (Plaintext):&lt;/strong&gt;
The client (Pingora) connects to the &lt;strong&gt;Proxy&lt;/strong&gt; and sends a &lt;code&gt;CONNECT&lt;/code&gt; request specifying the ultimate destination.
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;   CONNECT advanced.pingora.local:443 HTTP/1.1
   Host: advanced.pingora.local:443
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Tunnel (Encrypted):&lt;/strong&gt;
If allowed, the Proxy connects to the destination and replies &lt;code&gt;200 Connection Established&lt;/code&gt;.
At this precise moment, the connection transforms. The HTTP layer vanishes, and it becomes a raw TCP stream. Pingora then initiates the TLS handshake &lt;em&gt;through&lt;/em&gt; this stream, effectively talking directly to the destination.&lt;/li&gt;
&lt;/ol&gt;


&lt;h2&gt;
  
  
  3. The Challenge: Pingora's Limitation
&lt;/h2&gt;

&lt;p&gt;As of today, Pingora's high-level API (&lt;code&gt;peer.proxy&lt;/code&gt;) is optimized for Unix Domain Sockets (UDS). It does &lt;strong&gt;not&lt;/strong&gt; natively support configuring a standard TCP/IP address (like &lt;code&gt;10.0.0.5:3128&lt;/code&gt;) as a forward proxy.&lt;/p&gt;

&lt;p&gt;If you attempt to set &lt;code&gt;peer.proxy&lt;/code&gt; to a standard HTTP proxy address, Pingora will struggle to establish the connection because it expects a local socket file path.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Consequence:&lt;/strong&gt; We cannot rely on simple configuration flags. To support this common enterprise requirement, we must extend Pingora's networking capabilities manually.&lt;/p&gt;


&lt;h2&gt;
  
  
  4. The Solution: Transport Layer Hijacking
&lt;/h2&gt;

&lt;p&gt;To solve this, we will use Pingora's &lt;code&gt;L4Connect&lt;/code&gt; trait. This powerful interface allows us to intercept the "Dialing" phase of the connection lifecycle.&lt;/p&gt;

&lt;p&gt;We will implement a custom connector that performs a "Bait and Switch" operation:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Intercept the Dial:&lt;/strong&gt; When Pingora asks to connect to a peer, our custom code takes over.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dial the Proxy:&lt;/strong&gt; Instead of connecting to the Upstream, we physically connect to the &lt;strong&gt;Forward Proxy IP&lt;/strong&gt; (e.g., &lt;code&gt;127.0.0.1:3128&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Perform the Handshake:&lt;/strong&gt; We manually write the &lt;code&gt;CONNECT&lt;/code&gt; headers to the socket and wait for the &lt;code&gt;200 OK&lt;/code&gt; response.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Return the Socket:&lt;/strong&gt; We hand this established tunnel back to Pingora.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Pingora's core doesn't know (or care) that the socket is tunneled. It simply sees a valid TCP stream and proceeds to perform the TLS handshake on top of it.&lt;/p&gt;
&lt;h3&gt;
  
  
  The "FD Mismatch" Trap
&lt;/h3&gt;

&lt;p&gt;There is one critical safety mechanism we must navigate: &lt;strong&gt;File Descriptor (FD) Reuse Checks&lt;/strong&gt;.&lt;br&gt;
Pingora checks if the connected socket's remote address matches the configured &lt;code&gt;HttpPeer&lt;/code&gt; address.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;If we configure:&lt;/strong&gt; Peer = &lt;code&gt;advanced.pingora.local&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;But we connect to:&lt;/strong&gt; Socket = &lt;code&gt;127.0.0.1&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pingora will detect this mismatch ("I asked for X but you gave me Y!") and close the connection to prevent security risks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Fix:&lt;/strong&gt; We must align the physical and logical layers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Physical Layer:&lt;/strong&gt; We configure the &lt;code&gt;HttpPeer&lt;/code&gt; address to be the &lt;strong&gt;Proxy's IP&lt;/strong&gt;. This satisfies the safety check.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logical Layer:&lt;/strong&gt; We manually set the &lt;strong&gt;SNI&lt;/strong&gt; and &lt;strong&gt;Host Header&lt;/strong&gt; to the &lt;strong&gt;Upstream's Domain&lt;/strong&gt;. This ensures the TLS handshake and HTTP request are valid for the destination.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  5. The Code (&lt;code&gt;examples/22_proxy_connect.rs&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;This implementation is advanced because it requires us to touch three different layers of the networking stack simultaneously:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Layer 4 (Transport):&lt;/strong&gt; We physically connect to the Proxy IP.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Layer 5 (Session):&lt;/strong&gt; We manually negotiate the HTTP &lt;code&gt;CONNECT&lt;/code&gt; tunnel.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Layer 7 (Application):&lt;/strong&gt; We lie to the application layer, telling it we are connecting to the Upstream Host so that SNI and Host headers are generated correctly.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  The Components
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ProxyTunnelConnector&lt;/code&gt;&lt;/strong&gt;: This struct implements &lt;code&gt;L4Connect&lt;/code&gt;. It is the "driver" that Pingora uses when it needs to establish a TCP connection. Instead of dialing the destination, it dials the proxy, negotiates the tunnel, and returns the active socket.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;TunnelProxy&lt;/code&gt;&lt;/strong&gt;: The main proxy logic. Its job is to configure the &lt;code&gt;HttpPeer&lt;/code&gt; with the &lt;strong&gt;Physical Address&lt;/strong&gt; (Proxy IP) to satisfy safety checks, while injecting our custom connector to handle the &lt;strong&gt;Logical Connection&lt;/strong&gt; (Target Host).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;run_mock_forward_proxy&lt;/code&gt;&lt;/strong&gt;: A minimal TCP proxy running in the background to simulate a corporate firewall like Zscaler or Squid.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;connectors&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;L4Connect&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// We alias the specific Stream type Pingora expects for L4 connections&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;protocols&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;l4&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Stream&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;L4Stream&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;protocols&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;l4&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;SocketAddr&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;PingoraSocketAddr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;net&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;SocketAddr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;AsyncReadExt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AsyncWriteExt&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;net&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;TcpListener&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TcpStream&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// --- COMPONENT 1: The Custom Connector ---&lt;/span&gt;
&lt;span class="c1"&gt;// This acts as the "Client" in the CONNECT handshake.&lt;/span&gt;
&lt;span class="nd"&gt;#[derive(Debug)]&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;ProxyTunnelConnector&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;proxy_addr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SocketAddr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Physical: Where packets go (127.0.0.1:3128)&lt;/span&gt;
    &lt;span class="n"&gt;remote_host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="c1"&gt;// Logical: Who we want to reach (advanced.pingora.local)&lt;/span&gt;
    &lt;span class="n"&gt;remote_port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;       &lt;span class="c1"&gt;// Logical: Port (443)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;L4Connect&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;ProxyTunnelConnector&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Pingora calls this method when it wants to open a connection.&lt;/span&gt;
    &lt;span class="c1"&gt;// We ignore the `_addr` argument provided by Pingora because we are hijacking the destination.&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_addr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;PingoraSocketAddr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;L4Stream&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Connector: Dialing Proxy at {:?}..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.proxy_addr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// 1. Establish Physical TCP Connection&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;TcpStream&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.proxy_addr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="nf"&gt;.map_err&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nn"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;explain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ErrorType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ConnectError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to connect to proxy: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// 2. Perform the HTTP CONNECT Handshake&lt;/span&gt;
        &lt;span class="c1"&gt;// We construct a raw HTTP request asking the proxy to open a tunnel.&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;connect_req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"CONNECT {}:{} HTTP/1.1&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s"&gt;Host: {}:{}&lt;/span&gt;&lt;span class="se"&gt;\r\n\r\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.remote_host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.remote_port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.remote_host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.remote_port&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Connector: Sending CONNECT request for {}:{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.remote_host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.remote_port&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="nf"&gt;.write_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connect_req&lt;/span&gt;&lt;span class="nf"&gt;.as_bytes&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="nf"&gt;.map_err&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nn"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;explain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ErrorType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;WriteError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to write CONNECT req: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// 3. Verify the Tunnel&lt;/span&gt;
        &lt;span class="c1"&gt;// We must read the response headers to ensure we got a "200 Connection Established".&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0u8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;4096&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="nf"&gt;.read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="nf"&gt;.map_err&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nn"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;explain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ErrorType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ReadError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to read proxy resp: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_utf8_lossy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="nf"&gt;.contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;" 200 "&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
             &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;explain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nn"&gt;ErrorType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ConnectProxyFailure&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Proxy refused tunnel: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="nf"&gt;.lines&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Unknown"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Connector: Tunnel established! Handing socket to Pingora core."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// 4. Handover&lt;/span&gt;
        &lt;span class="c1"&gt;// We wrap the standard Tokio TcpStream into Pingora's L4Stream wrapper.&lt;/span&gt;
        &lt;span class="c1"&gt;// Pingora's TLS layer will now take this socket and perform the SSL Handshake *over* it.&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;L4Stream&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// --- COMPONENT 2: The Main Proxy Logic ---&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;TunnelProxy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ProxyHttp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;TunnelProxy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new_ctx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_peer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;upstream_host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"advanced.pingora.local"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;upstream_port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;443&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// The Proxy's physical address (The "Next Hop")&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;proxy_addr_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"127.0.0.1:3128"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;proxy_socket_addr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SocketAddr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;proxy_addr_str&lt;/span&gt;&lt;span class="nf"&gt;.parse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// THE CRITICAL CONFIGURATION:&lt;/span&gt;
        &lt;span class="c1"&gt;// 1. Peer Address: Set to the PROXY IP. &lt;/span&gt;
        &lt;span class="c1"&gt;//    This prevents "FD Mismatch" errors. Pingora checks: "Is the socket connected to &lt;/span&gt;
        &lt;span class="c1"&gt;//    the address in the peer struct?" Since our connector dials 127.0.0.1, this must match.&lt;/span&gt;
        &lt;span class="c1"&gt;// 2. SNI: Set to the UPSTREAM HOST.&lt;/span&gt;
        &lt;span class="c1"&gt;//    This ensures the ClientHello generated by Pingora asks for the correct certificate.&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;proxy_socket_addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;        &lt;span class="c1"&gt;// Address matches physical socket&lt;/span&gt;
            &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                     &lt;span class="c1"&gt;// TLS matches final destination&lt;/span&gt;
            &lt;span class="n"&gt;upstream_host&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// SNI matches final destination&lt;/span&gt;
        &lt;span class="p"&gt;));&lt;/span&gt;

        &lt;span class="c1"&gt;// 3. Inject the Custom Connector&lt;/span&gt;
        &lt;span class="c1"&gt;// This overrides the default logic of "Just dial the Peer Address".&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;connector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ProxyTunnelConnector&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;proxy_addr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;proxy_socket_addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;remote_host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;upstream_host&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;remote_port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;upstream_port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="py"&gt;.options.custom_l4&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connector&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// 4. Override the Host Header&lt;/span&gt;
    &lt;span class="c1"&gt;// Even though we are physically talking to 127.0.0.1, the HTTP request &lt;/span&gt;
    &lt;span class="c1"&gt;// inside the encrypted tunnel must look like it's going to the upstream.&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_request_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;upstream_request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;RequestHeader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;upstream_request&lt;/span&gt;&lt;span class="nf"&gt;.insert_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Host"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"advanced.pingora.local"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// --- COMPONENT 3: Mock Forward Proxy (Background Task) ---&lt;/span&gt;
&lt;span class="c1"&gt;// Simulates a corporate proxy that accepts CONNECT requests.&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;run_mock_forward_proxy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.0.0.0:3128"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;listener&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;TcpListener&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to bind Mock Proxy"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Mock Proxy listening on {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;client_socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;listener&lt;/span&gt;&lt;span class="nf"&gt;.accept&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;move&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0u8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client_socket&lt;/span&gt;&lt;span class="nf"&gt;.read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="nf"&gt;.unwrap_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_utf8_lossy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="nf"&gt;.starts_with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"CONNECT"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Mock Proxy: Connecting to upstream_advanced..."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="c1"&gt;// In a real proxy, we would parse the requested host.&lt;/span&gt;
                    &lt;span class="c1"&gt;// Here we hardcode the destination for the lab.&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;upstream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;TcpStream&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"172.28.0.22:443"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="c1"&gt;// Reply "Success" to the client&lt;/span&gt;
                        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client_socket&lt;/span&gt;&lt;span class="nf"&gt;.write_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;b"HTTP/1.1 200 Connection Established&lt;/span&gt;&lt;span class="se"&gt;\r\n\r\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                        &lt;span class="c1"&gt;// Enter "Blind Pipe" mode&lt;/span&gt;
                        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;copy_bidirectional&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;client_socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;upstream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Start the mock proxy in a background thread&lt;/span&gt;
    &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;move&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;rt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Runtime&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;rt&lt;/span&gt;&lt;span class="nf"&gt;.block_on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;run_mock_forward_proxy&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;http_proxy_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TunnelProxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="nf"&gt;.add_tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:6166"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Pingora Tunnel Service running on 0.0.0.0:6166"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  6. Verification
&lt;/h2&gt;

&lt;p&gt;We will confirm that traffic is actually flowing through the proxy by observing the logs from our Mock Proxy component.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Start the Proxy
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 22_proxy_connect
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  2. Make the Request
&lt;/h3&gt;

&lt;p&gt;We send a request to the Pingora listener (port 6166). We expect Pingora to transparently handle the complex tunneling.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; pingora_client_1 curl &lt;span class="nt"&gt;-v&lt;/span&gt; http://172.28.0.10:6166/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Log Analysis
&lt;/h3&gt;

&lt;p&gt;Observe the &lt;code&gt;stdout&lt;/code&gt; of your running Rust program. You should see the following sequence of events:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Connector: Dialing Proxy...&lt;/code&gt;&lt;/strong&gt;: The &lt;code&gt;upstream_peer&lt;/code&gt; hook executed, and our custom connector was invoked. It is connecting to &lt;code&gt;127.0.0.1:3128&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Mock Proxy: Connecting to upstream_advanced...&lt;/code&gt;&lt;/strong&gt;: The background thread received the connection and the &lt;code&gt;CONNECT&lt;/code&gt; header.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Connector: Tunnel established!&lt;/code&gt;&lt;/strong&gt;: The Mock Proxy replied with &lt;code&gt;200 OK&lt;/code&gt;, and our connector handed the socket back to Pingora.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Response from Advanced Upstream...&lt;/code&gt;&lt;/strong&gt;: Pingora successfully performed the TLS handshake &lt;em&gt;inside&lt;/em&gt; the tunnel and retrieved the response.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  4. Why this matters
&lt;/h3&gt;

&lt;p&gt;If you attempted to use &lt;code&gt;HttpPeer::new_proxy&lt;/code&gt; with a TCP address, you would have seen an error like &lt;code&gt;No such file or directory&lt;/code&gt; (OS Error 2). This confirms that native support is lacking for TCP, and that our "Hijack" method is currently the correct way to implement TCP Tunneling in Pingora.&lt;/p&gt;

&lt;h1&gt;
  
  
  Lesson 23: WebSocket Upgrade
&lt;/h1&gt;

&lt;h2&gt;
  
  
  1. WebSockets
&lt;/h2&gt;

&lt;p&gt;In modern real-time applications (chat apps, live sports feeds, stock tickers), the request-response model of HTTP is often insufficient. These applications rely on &lt;strong&gt;WebSockets&lt;/strong&gt;, which allow for persistent, bidirectional communication between the client and the server.&lt;/p&gt;

&lt;p&gt;A WebSocket connection begins its life as a standard HTTP request but quickly "upgrades" into a raw TCP stream. In this lesson, we will configure Pingora to handle this upgrade process and maintain these long-lived connections.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. How the Upgrade Works
&lt;/h2&gt;

&lt;p&gt;The WebSocket protocol (RFC 6455) uses a specific "handshake" to establish the connection:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Client Request:&lt;/strong&gt; The client sends an HTTP GET request with two special headers:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Connection: Upgrade&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Upgrade: websocket&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proxy Behavior:&lt;/strong&gt; Pingora forwards this request to the upstream.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Upstream Response:&lt;/strong&gt; If the upstream accepts the upgrade, it replies with &lt;code&gt;101 Switching Protocols&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Switch:&lt;/strong&gt; Upon receiving the 101 status, both the client and the upstream (and the proxy in the middle) stop speaking HTTP. They switch to the WebSocket binary protocol over the same underlying TCP connection.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  3. The Critical Challenge: Timeouts
&lt;/h2&gt;

&lt;p&gt;The most common issue when proxying WebSockets is premature disconnection.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;HTTP Mindset:&lt;/strong&gt; HTTP proxies are designed for short bursts of activity. If a connection is idle for 60 seconds, it's usually considered dead or "stuck," and the proxy kills it to free up resources.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WebSocket Reality:&lt;/strong&gt; WebSockets are often idle for long periods (e.g., waiting for a chat message).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The Solution:&lt;/strong&gt;&lt;br&gt;
When configuring the &lt;code&gt;HttpPeer&lt;/code&gt; for WebSocket traffic, we must explicitly &lt;strong&gt;disable or extend the read/write timeouts&lt;/strong&gt;. If we leave the defaults (e.g., 60s), Pingora will aggressively terminate perfectly healthy WebSocket connections during quiet periods.&lt;/p&gt;
&lt;h2&gt;
  
  
  4. Setup for this Lesson
&lt;/h2&gt;

&lt;p&gt;To demonstrate a complete WebSocket lifecycle without external dependencies, we will build a self-contained system in a single file:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Echo Server (Background Thread):&lt;/strong&gt; A simple server using &lt;code&gt;tokio-tungstenite&lt;/code&gt; that accepts WebSocket connections and echoes back any text it receives with a timestamp.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Test Client (Background Thread):&lt;/strong&gt; A script that connects to our proxy, sends a "Ping" every second, and logs the "Pong" it receives.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Pingora Proxy:&lt;/strong&gt; The intermediary that routes the traffic between them.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This "Self-Driving" example allows you to verify the entire flow—handshake, message passing, and termination—just by watching the logs.&lt;/p&gt;
&lt;h2&gt;
  
  
  5. The Code (&lt;code&gt;examples/23_websocket_upgrade.rs&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;First, ensure your &lt;code&gt;Cargo.toml&lt;/code&gt; includes the necessary dependencies for handling WebSockets and time formatting.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[dependencies]&lt;/span&gt;
&lt;span class="c"&gt;# ... previous dependencies ...&lt;/span&gt;
&lt;span class="py"&gt;tokio-tungstenite&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.28.0"&lt;/span&gt;
&lt;span class="py"&gt;futures-util&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.3.31"&lt;/span&gt;
&lt;span class="py"&gt;chrono&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.4.42"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The following code sets up a complete ecosystem: a Proxy, a Mock Server, and a Test Client.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;net&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;TcpListener&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// WebSocket Dependencies&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;futures_util&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;SinkExt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;StreamExt&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;tokio_tungstenite&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;accept_async&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;connect_async&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;tungstenite&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;protocol&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;WebSocketProxy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ProxyHttp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;WebSocketProxy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new_ctx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_peer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Forward to the Mock Server running in the background&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"127.0.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9091&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;

        &lt;span class="c1"&gt;// CRITICAL: WebSocket Configuration&lt;/span&gt;
        &lt;span class="c1"&gt;// 1. read_timeout: Set to None (infinity). WebSockets are often idle.&lt;/span&gt;
        &lt;span class="c1"&gt;//    If we leave the default (e.g., 60s), Pingora will kill the connection.&lt;/span&gt;
        &lt;span class="c1"&gt;// 2. connection_timeout: Standard TCP connect timeout.&lt;/span&gt;
        &lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="py"&gt;.options.read_timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="py"&gt;.options.write_timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="py"&gt;.options.connection_timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_secs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_request_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;upstream_request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;RequestHeader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;where&lt;/span&gt;
        &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Send&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Sync&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Pingora automatically preserves the "Connection" and "Upgrade" headers&lt;/span&gt;
        &lt;span class="c1"&gt;// required for the handshake. We log here purely for visibility.&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;upgrade&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;upstream_request&lt;/span&gt;&lt;span class="py"&gt;.headers&lt;/span&gt;&lt;span class="nf"&gt;.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Upgrade"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Proxy: Detected Upgrade Header: {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;upgrade&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// --- Background Task: The Upstream Server ---&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;run_echo_server&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"127.0.0.1:9091"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;listener&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;TcpListener&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to bind Echo Server."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Echo Server listening on {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;listener&lt;/span&gt;&lt;span class="nf"&gt;.accept&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;move&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;ws_stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;accept_async&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to accept WS"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ws_stream&lt;/span&gt;&lt;span class="nf"&gt;.next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error reading message."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="nf"&gt;.is_text&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="nf"&gt;.to_text&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                    &lt;span class="c1"&gt;// Reply with a timestamped echo&lt;/span&gt;
                    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;response_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Echo [{}]: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;chrono&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Local&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%H:%M:%S"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Server: Received '{}', Replying..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="n"&gt;ws_stream&lt;/span&gt;&lt;span class="nf"&gt;.send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response_text&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// --- Background Task: The Test Client ---&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;run_test_client&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Wait for Proxy to bind port&lt;/span&gt;
    &lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_secs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"ws://127.0.0.1:6167"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Client: Connection to Proxy at {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;ws_stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;connect_async&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to connect to proxy."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Client: Connected! Starting message loop..."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Send 3 Pings with a 1-second delay&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Ping #{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;ws_stream&lt;/span&gt;&lt;span class="nf"&gt;.send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to send"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ws_stream&lt;/span&gt;&lt;span class="nf"&gt;.next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;resp_msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to read response"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Client: Received '{}'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resp_msg&lt;/span&gt;&lt;span class="nf"&gt;.to_text&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_secs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Client: Test Complete. Closing connection."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;ws_stream&lt;/span&gt;&lt;span class="nf"&gt;.close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Spawn Background Services&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_server_handle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(||&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;rt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Runtime&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;rt&lt;/span&gt;&lt;span class="nf"&gt;.block_on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;run_echo_server&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_client_handle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(||&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;rt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Runtime&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;rt&lt;/span&gt;&lt;span class="nf"&gt;.block_on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;run_test_client&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Start Proxy Service&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;http_proxy_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;WebSocketProxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="nf"&gt;.add_tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:6167"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"WebSocket Proxy running on 0.0.0.0:6167"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  6. Verification
&lt;/h2&gt;

&lt;p&gt;This script verifies itself automatically. When you run it, you will see the logs from all three components (Client, Proxy, Server) interleaved, showing the flow of data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Run the Example&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 23_websocket_upgrade
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Analyze the Output&lt;/strong&gt;&lt;br&gt;
You should see the following sequence:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Setup:&lt;/strong&gt; The Proxy and Echo Server start.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Handshake:&lt;/strong&gt; The Client connects (&lt;code&gt;ws://127.0.0.1:6167&lt;/code&gt;). The Proxy detects the &lt;code&gt;Upgrade&lt;/code&gt; header.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Traffic:&lt;/strong&gt; The Client sends "Ping #1". The Server receives it and replies with "Echo [Time]: Ping #1".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repeat:&lt;/strong&gt; This happens 3 times over the same persistent connection.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Sample Log Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[INFO] Echo Server listening on 127.0.0.1:9091
[INFO] WebSocket Proxy running on 0.0.0.0:6167
[INFO] Client: Connection to Proxy at ws://127.0.0.1:6167
[INFO] Proxy: Detected Upgrade Header: "websocket"
[INFO] Client: Connected! Starting message loop...
[INFO] Client: Sending 'Ping #1'
[INFO] Server: Received 'Ping #1', Replying...
[INFO] Client: Received 'Echo [11:00:19]: Ping #1'
...
[INFO] Client: Test Complete. Closing connection.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Lesson 24: gRPC Proxy
&lt;/h1&gt;

&lt;p&gt;gRPC is a high-performance RPC framework that runs on top of &lt;strong&gt;HTTP/2&lt;/strong&gt;. Unlike standard REST APIs, gRPC relies heavily on specific HTTP/2 features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Binary Framing:&lt;/strong&gt; It uses Protocol Buffers (protobufs) instead of JSON text.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trailers:&lt;/strong&gt; It sends status codes (like &lt;code&gt;grpc-status&lt;/code&gt;) in a separate header block &lt;em&gt;after&lt;/em&gt; the response body has finished.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multiplexing:&lt;/strong&gt; It allows multiple requests to be interleaved over a single TCP connection.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To proxy gRPC traffic effectively, Pingora must be configured to establish an &lt;strong&gt;HTTP/2 connection&lt;/strong&gt; to the upstream server. If the proxy falls back to HTTP/1.1, standard gRPC backends will often reject the connection immediately.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Concepts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. ALPN (Application-Layer Protocol Negotiation)
&lt;/h3&gt;

&lt;p&gt;When establishing a TLS connection, the client and server perform a handshake to decide which protocol to speak (e.g., "http/1.1" or "h2").&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Problem:&lt;/strong&gt; By default, connections might settle on HTTP/1.1 for compatibility.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Fix:&lt;/strong&gt; We must explicitly configure Pingora's upstream peer to request &lt;code&gt;h2&lt;/code&gt; during the TLS handshake.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Trailers
&lt;/h3&gt;

&lt;p&gt;In REST, an HTTP 200 OK means success. In gRPC, an HTTP 200 OK just means "I received your message." The actual success or failure is communicated in the &lt;strong&gt;Trailers&lt;/strong&gt; (headers sent &lt;em&gt;after&lt;/em&gt; the body). Pingora supports HTTP trailers natively, ensuring this critical status information is preserved.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. End-to-End Encryption (and Trust)
&lt;/h3&gt;

&lt;p&gt;In previous steps, we generated a specific certificate for our gRPC container (&lt;code&gt;grpc.pingora.local&lt;/code&gt;). Because we set the &lt;code&gt;SSL_CERT_FILE&lt;/code&gt; environment variable in our docker-compose, Pingora automatically trusts our Root CA. This allows us to connect securely to the upstream without disabling certificate verification—a best practice for production gRPC services.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Code (&lt;code&gt;examples/24_grpc_proxy.rs&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;In this example, we configure Pingora to target the secure port (9001) of our &lt;code&gt;grpcbin&lt;/code&gt; container. The critical line is forcing the ALPN negotiation to &lt;code&gt;H2&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// We import ALPN from the peer module to control protocol negotiation&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ALPN&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;GrpcProxy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ProxyHttp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;GrpcProxy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new_ctx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_peer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Target the gRPC server (Secure Port 9001)&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"172.28.0.23"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9001&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;sni&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"grpc.pingora.local"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// 1. Enable TLS&lt;/span&gt;
        &lt;span class="c1"&gt;// Pingora will use the system trust store (defined by SSL_CERT_FILE)&lt;/span&gt;
        &lt;span class="c1"&gt;// to verify the upstream's certificate.&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sni&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;

        &lt;span class="c1"&gt;// 2. Force HTTP/2 via ALPN&lt;/span&gt;
        &lt;span class="c1"&gt;// This is mandatory. If we negotiate HTTP/1.1, most gRPC servers will&lt;/span&gt;
        &lt;span class="c1"&gt;// close the connection or return a protocol error.&lt;/span&gt;
        &lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="py"&gt;.options.alpn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;ALPN&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;H2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_request_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;upstream_request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;RequestHeader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Optional: Log when we see gRPC traffic&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctype&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;upstream_request&lt;/span&gt;&lt;span class="py"&gt;.headers&lt;/span&gt;&lt;span class="nf"&gt;.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"content-type"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ctype&lt;/span&gt;&lt;span class="nf"&gt;.to_str&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.starts_with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"application/grpc"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Proxying gRPC request..."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;http_proxy_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GrpcProxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// We listen on TLS so we can support secure gRPC clients.&lt;/span&gt;
    &lt;span class="c1"&gt;// This allows the client to negotiate H2 with Pingora as well.&lt;/span&gt;
    &lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="nf"&gt;.add_tls&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;"0.0.0.0:6168"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"conf/keys/server.crt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"conf/keys/server.key"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to add TLS listener"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"gRPC Proxy running on 0.0.0.0:6168"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;We will use &lt;code&gt;curl&lt;/code&gt; to simulate a gRPC client. Since we don't have a full gRPC client (like &lt;code&gt;grpcurl&lt;/code&gt;) installed in our environment, we will manually construct a compliant HTTP/2 request.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Start the Proxy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 24_grpc_proxy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Send the Request
&lt;/h3&gt;

&lt;p&gt;We target the Pingora proxy (&lt;code&gt;172.28.0.10:6168&lt;/code&gt;). We must provide the Root CA so &lt;code&gt;curl&lt;/code&gt; trusts Pingora's TLS certificate.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; pingora_client_1 curl &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cacert&lt;/span&gt; /keys/ca.crt &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/grpc"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"TE: trailers"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://172.28.0.10:6168/grpc.health.v1.Health/Check
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Analyze the Output
&lt;/h3&gt;

&lt;p&gt;You should observe the following in the &lt;code&gt;curl&lt;/code&gt; output:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Status 200 OK:&lt;/strong&gt; &lt;code&gt;&amp;lt; HTTP/1.1 200 OK&lt;/code&gt; (or &lt;code&gt;HTTP/2&lt;/code&gt;). This indicates the upstream accepted the connection.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;gRPC Headers:&lt;/strong&gt; You will see headers like &lt;code&gt;Content-Type: application/grpc&lt;/code&gt; and trailers like &lt;code&gt;Grpc-Status&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logs:&lt;/strong&gt; The proxy logs will show &lt;code&gt;Proxying gRPC request...&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Note on Protocol Versions:&lt;/strong&gt;&lt;br&gt;
Even if your &lt;code&gt;curl&lt;/code&gt; client negotiates HTTP/1.1 with Pingora (as seen in some test environments), Pingora is actively translating that traffic and speaking &lt;strong&gt;HTTP/2&lt;/strong&gt; to the upstream &lt;code&gt;grpcbin&lt;/code&gt;. This confirms that Pingora is correctly acting as a gateway, bridging the protocols as defined in our configuration.&lt;/p&gt;
&lt;h1&gt;
  
  
  Lesson 25: Connection Reuse (Pooling)
&lt;/h1&gt;

&lt;p&gt;Establishing a new TCP connection (and potentially a TLS handshake) for every single request is expensive. It adds significant latency and consumes CPU on both the proxy and the upstream.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Connection Reuse&lt;/strong&gt; (or "Keep-Alive") allows Pingora to keep a connection to an upstream open after a request finishes, putting it into a "Pool." When a new request arrives for that same upstream, Pingora checks the pool first. If a healthy connection exists, it reuses it—skipping the handshake entirely.&lt;/p&gt;

&lt;p&gt;In this lesson, we will configure connection pooling and, critically, &lt;strong&gt;prove&lt;/strong&gt; it works by capturing the reuse flag using Pingora's lifecycle hooks.&lt;/p&gt;
&lt;h2&gt;
  
  
  Key Concepts
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. The &lt;code&gt;CTX&lt;/code&gt; (Context) Pattern
&lt;/h3&gt;

&lt;p&gt;Since Pingora's request lifecycle is split across multiple asynchronous phases, variables don't persist automatically between them. To track whether a connection was reused, we must:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Define a custom &lt;code&gt;CTX&lt;/code&gt; struct.&lt;/li&gt;
&lt;li&gt;Capture the &lt;code&gt;reused&lt;/code&gt; boolean in the &lt;code&gt;connected_to_upstream&lt;/code&gt; hook.&lt;/li&gt;
&lt;li&gt;Store it in our &lt;code&gt;CTX&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Read it back in the &lt;code&gt;logging&lt;/code&gt; hook.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  2. Tuning the Pool
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;idle_timeout&lt;/code&gt;:&lt;/strong&gt; How long a connection can sit in the pool doing nothing before Pingora closes it. If this is too short (e.g., 0), reuse will never happen.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;pool_max_idle&lt;/code&gt;&lt;/strong&gt;: (Global setting, not shown here) Limits how many idle connections we keep per peer.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  The Code (&lt;code&gt;examples/25_connection_reuse.rs&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;We define a &lt;code&gt;ReusingCtx&lt;/code&gt; to hold our state, and implement the &lt;code&gt;connected_to_upstream&lt;/code&gt; hook to intercept the connection event.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;protocols&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Digest&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;ReusingProxy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// 1. Define a Context to hold state across the request lifecycle&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;ReusingCtx&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;is_reused&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ProxyHttp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;ReusingProxy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// 2. Use the custom Context type&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ReusingCtx&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new_ctx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ReusingCtx&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;is_reused&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_peer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Target Upstream Blue&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"172.28.0.20"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"blue.pingora.local"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;

        &lt;span class="c1"&gt;// --- CONNECTION REUSE CONFIGURATION ---&lt;/span&gt;

        &lt;span class="c1"&gt;// idle_timeout: Controls how long an idle connection stays in the pool.&lt;/span&gt;
        &lt;span class="c1"&gt;// We set this to 30s. If we set this to 0, the connection would likely &lt;/span&gt;
        &lt;span class="c1"&gt;// close immediately after use, preventing reuse.&lt;/span&gt;
        &lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="py"&gt;.options.idle_timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_secs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

        &lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="py"&gt;.options.connection_timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_secs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// 3. Intercept the connection step to capture the 'reused' flag&lt;/span&gt;
    &lt;span class="c1"&gt;// This hook runs immediately after Pingora gets a connection (either new or from pool).&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;connected_to_upstream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;reused&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                 &lt;span class="c1"&gt;// &amp;lt;--- The flag we want!&lt;/span&gt;
        &lt;span class="n"&gt;_peer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nd"&gt;#[cfg(unix)]&lt;/span&gt; &lt;span class="n"&gt;_fd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;os&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;unix&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;RawFd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nd"&gt;#[cfg(windows)]&lt;/span&gt; &lt;span class="n"&gt;_sock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;os&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;windows&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;RawSocket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_digest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Digest&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;          &lt;span class="c1"&gt;// &amp;lt;--- Our mutable context&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Store the status in our context for logging later&lt;/span&gt;
        &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.is_reused&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reused&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;          &lt;span class="c1"&gt;// &amp;lt;--- Access the stored context&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// 4. Log the state to prove reuse happened&lt;/span&gt;
        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Request Complete. Reused Connection: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.is_reused&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;http_proxy_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ReusingProxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="nf"&gt;.add_tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:6169"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Connection Reuse Demo running on 0.0.0.0:6169"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;To verify reuse, we need to send requests fast enough that the previous connection hasn't timed out yet. Using &lt;code&gt;curl&lt;/code&gt; with two URLs in a single command is a great way to do this.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Start the Proxy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 25_connection_reuse
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Send Sequential Requests
&lt;/h3&gt;

&lt;p&gt;We ask &lt;code&gt;curl&lt;/code&gt; to fetch the same URL twice in a row.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; pingora_client_1 curl &lt;span class="nt"&gt;-v&lt;/span&gt; http://172.28.0.10:6169/ http://172.28.0.10:6169/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Analyze the Logs
&lt;/h3&gt;

&lt;p&gt;You will see exactly how the pooling mechanism works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[INFO] Request Complete. Reused Connection: false
[INFO] Request Complete. Reused Connection: true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Request 1 (&lt;code&gt;false&lt;/code&gt;):&lt;/strong&gt; The pool was empty. Pingora had to perform a TCP handshake with &lt;code&gt;172.28.0.20&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Request 2 (&lt;code&gt;true&lt;/code&gt;):&lt;/strong&gt; Pingora checked the pool, found the live connection from Request 1, and reused it immediately.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This "0-RTT" (Zero Round Trip Time) connection setup is vital for high-performance proxies.&lt;/p&gt;

&lt;h1&gt;
  
  
  Module 4: Load Balancing
&lt;/h1&gt;

&lt;p&gt;Up to this point, our proxy has been a &lt;strong&gt;1-to-1&lt;/strong&gt; conduit. We accepted a request and forwarded it to a single, hardcoded destination (e.g., &lt;code&gt;172.28.0.20&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Module 4&lt;/strong&gt;, we graduate to &lt;strong&gt;1-to-N&lt;/strong&gt; routing. We are no longer just "proxying"; we are &lt;strong&gt;distributing&lt;/strong&gt; traffic. This module focuses on the &lt;code&gt;pingora-load-balancing&lt;/code&gt; crate, which provides the logic to manage pools of upstream servers, ensuring high availability, scalability, and efficiency.&lt;/p&gt;

&lt;p&gt;We will move away from defining a single &lt;code&gt;HttpPeer&lt;/code&gt; and start defining &lt;strong&gt;Clusters&lt;/strong&gt; of backends. We will explore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Selection Algorithms:&lt;/strong&gt; How does Pingora decide which server gets the next request? (Round Robin, Weighted, Hashing).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Health Checking:&lt;/strong&gt; How does Pingora automatically detect and remove dead servers from rotation &lt;em&gt;before&lt;/em&gt; they cause errors?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Session Persistence:&lt;/strong&gt; How do we ensure a user always returns to the same backend for stateful applications?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic Discovery:&lt;/strong&gt; How do we update our list of backends without restarting the server?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is where Pingora transforms from a simple pipe into a robust Edge Gateway capable of handling production-scale traffic.&lt;/p&gt;

&lt;h1&gt;
  
  
  Lesson 26: Round Robin Load Balancing
&lt;/h1&gt;

&lt;p&gt;In previous modules, our proxy acted as a simple pipe: one listener mapped to one upstream. In &lt;strong&gt;Module 4&lt;/strong&gt;, we unlock the power of &lt;strong&gt;Load Balancing&lt;/strong&gt;, transforming Pingora into a gateway that distributes traffic across a cluster of servers.&lt;/p&gt;

&lt;p&gt;We start with the most fundamental algorithm: &lt;strong&gt;Round Robin&lt;/strong&gt;. This strategy rotates requests sequentially through the list of available backends (&lt;code&gt;Blue -&amp;gt; Green -&amp;gt; Blue -&amp;gt; Green&lt;/code&gt;). It is ideal for stateless services where all backends have roughly equal capacity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Concepts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. The &lt;code&gt;LoadBalancer&lt;/code&gt; Struct
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;pingora_load_balancing::LoadBalancer&amp;lt;S&amp;gt;&lt;/code&gt; struct is the "brain" of this module.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Inventory:&lt;/strong&gt; It holds the list of all backend servers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State:&lt;/strong&gt; It tracks which servers are healthy (and eligible for traffic) and which are not.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strategy (&lt;code&gt;S&lt;/code&gt;):&lt;/strong&gt; It is generic over a selection algorithm. In this lesson, we specify &lt;code&gt;LoadBalancer&amp;lt;RoundRobin&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. The Background Service
&lt;/h3&gt;

&lt;p&gt;Load balancing involves more than just picking a random IP. It often requires active maintenance tasks like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pinging servers to check if they are alive (Health Checks).&lt;/li&gt;
&lt;li&gt;Querying DNS or APIs to find new servers (Service Discovery).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To perform these tasks without blocking the main proxy thread, Pingora runs the &lt;code&gt;LoadBalancer&lt;/code&gt; as a separate &lt;strong&gt;Background Service&lt;/strong&gt;. Even if we provide a static list of IPs (as we do here), using this architecture from the start prepares us for dynamic features later.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Code (&lt;code&gt;examples/26_round_robin.rs&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;This code sets up a Load Balancer with two upstreams (&lt;code&gt;Blue&lt;/code&gt; and &lt;code&gt;Green&lt;/code&gt;) and routes traffic between them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;services&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;background&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;background_service&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// 1. The Struct holding our Load Balancer state&lt;/span&gt;
&lt;span class="c1"&gt;// We wrap it in Arc so it can be shared between the Background Service (which updates it)&lt;/span&gt;
&lt;span class="c1"&gt;// and the Proxy Service (which reads from it).&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nf"&gt;LB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LoadBalancer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RoundRobin&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ProxyHttp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;LB&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new_ctx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_peer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// 2. Load Balancing Selection&lt;/span&gt;
        &lt;span class="c1"&gt;// select() takes a key (for hashing) and a max_iterations limit.&lt;/span&gt;
        &lt;span class="c1"&gt;// Since we are using RoundRobin, the key (b"") is ignored.&lt;/span&gt;
        &lt;span class="c1"&gt;// We look up to 256 times to find a healthy backend.&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;upstream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="na"&gt;.0&lt;/span&gt;
            &lt;span class="nf"&gt;.select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;b""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
            &lt;span class="nf"&gt;.ok_or_else&lt;/span&gt;&lt;span class="p"&gt;(||&lt;/span&gt; &lt;span class="nn"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;explain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ErrorType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Custom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"NoUpstreamAvailable"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"Empty upstream pool"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Selected upstream: {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;upstream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// 3. Construct the Peer&lt;/span&gt;
        &lt;span class="c1"&gt;// We use the address provided by the Load Balancer.&lt;/span&gt;
        &lt;span class="c1"&gt;// "upstream" is a generic SNI/Host since we are hitting simple echo servers.&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;upstream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// No TLS for these specific echo containers&lt;/span&gt;
            &lt;span class="s"&gt;"upstream"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;));&lt;/span&gt;

        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_request_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;upstream_request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;RequestHeader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Just for clarity in the logs&lt;/span&gt;
        &lt;span class="n"&gt;upstream_request&lt;/span&gt;&lt;span class="nf"&gt;.insert_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Host"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"round-robin-cluster"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// 4. Initialize the Load Balancer&lt;/span&gt;
    &lt;span class="c1"&gt;// We create a static list. In later modules, this can be dynamic.&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;upstreams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;LoadBalancer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;try_from_iter&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="s"&gt;"172.28.0.20:8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Blue&lt;/span&gt;
        &lt;span class="s"&gt;"172.28.0.21:8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Green&lt;/span&gt;
    &lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// 5. Create the Background Service&lt;/span&gt;
    &lt;span class="c1"&gt;// This allows the LoadBalancer to run tasks (like health checks) in the background.&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;background&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;background_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"round_robin_lb"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Get the Arc pointer to the LB to pass to our proxy&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;lb_ref&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;background&lt;/span&gt;&lt;span class="nf"&gt;.task&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// 6. Create the Proxy Service&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;http_proxy_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;LB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lb_ref&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="nf"&gt;.add_tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:6170"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Round Robin Load Balancer running on 0.0.0.0:6170"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 7. Register both services&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;background&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Start the Proxy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 26_round_robin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Observe the "Service Exited" Log
&lt;/h3&gt;

&lt;p&gt;You might see a log line saying &lt;code&gt;[INFO pingora_core::server] service exited&lt;/code&gt;.&lt;br&gt;
&lt;strong&gt;Do not panic.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This refers to the &lt;code&gt;round_robin_lb&lt;/code&gt; background service.&lt;/li&gt;
&lt;li&gt;Because we haven't configured any &lt;strong&gt;Health Checks&lt;/strong&gt; or &lt;strong&gt;Dynamic Discovery&lt;/strong&gt; yet, the background service realized it had no periodic work to do and exited to save resources.&lt;/li&gt;
&lt;li&gt;The Load Balancer state (the list of IPs) remains safely in memory, held by the Proxy Service.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  3. Send Traffic
&lt;/h3&gt;

&lt;p&gt;From another terminal, send two requests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://172.28.0.10:6170/
curl http://172.28.0.10:6170/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Analyze Output
&lt;/h3&gt;

&lt;p&gt;You will see the responses alternate perfectly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;'Response from BLUE'
'Response from GREEN'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the proxy logs, you will see the selection logic at work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[INFO] Selected upstream: Backend { addr: Inet(172.28.0.20:8080), ... }
[INFO] Selected upstream: Backend { addr: Inet(172.28.0.21:8080), ... }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Lesson 27: Weighted Load Balancing
&lt;/h1&gt;

&lt;p&gt;In a real-world cluster, not all servers are created equal. You might have a beefy bare-metal server ("Blue") alongside a smaller virtual machine ("Green"). If you use standard Round Robin, you will overload the small server or underutilize the big one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Weighted Load Balancing&lt;/strong&gt; solves this by assigning a "weight" to each backend. A server with Weight 3 receives 3x more requests than a server with Weight 1.&lt;/p&gt;

&lt;p&gt;In this lesson, we will configure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Upstream Blue (172.28.0.20):&lt;/strong&gt; Weight &lt;strong&gt;3&lt;/strong&gt; (High Capacity).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Upstream Green (172.28.0.21):&lt;/strong&gt; Weight &lt;strong&gt;1&lt;/strong&gt; (Low Capacity).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key Concepts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. The Initialization "Trap"
&lt;/h3&gt;

&lt;p&gt;In the previous lesson, we used &lt;code&gt;LoadBalancer::try_from_iter(["ip1", "ip2"])&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Problem:&lt;/strong&gt; This convenience method takes raw strings and converts them into &lt;code&gt;Backend&lt;/code&gt; objects with a &lt;strong&gt;default weight of 1&lt;/strong&gt;. If we used it here, it would wipe out our custom weights.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Fix:&lt;/strong&gt; We must manually create &lt;code&gt;Backend&lt;/code&gt; objects using &lt;code&gt;Backend::new_with_weight&lt;/code&gt;. Then, we manually build the discovery pipeline:
&lt;code&gt;Backend&lt;/code&gt; → &lt;code&gt;BTreeSet&lt;/code&gt; → &lt;code&gt;Static Discovery&lt;/code&gt; → &lt;code&gt;Backends&lt;/code&gt; → &lt;code&gt;LoadBalancer&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. The &lt;code&gt;RoundRobin&lt;/code&gt; Type Alias
&lt;/h3&gt;

&lt;p&gt;You might think we need to define our balancer as &lt;code&gt;LoadBalancer&amp;lt;Weighted&amp;lt;RoundRobin&amp;gt;&amp;gt;&lt;/code&gt;.&lt;br&gt;
&lt;strong&gt;Do not do this.&lt;/strong&gt;&lt;br&gt;
In Pingora, the &lt;code&gt;RoundRobin&lt;/code&gt; type you import is actually a type alias for &lt;code&gt;Weighted&amp;lt;algorithms::RoundRobin&amp;gt;&lt;/code&gt;. It supports weights natively. If you try to wrap it manually, you will get complex compilation errors about unsatisfied trait bounds.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Code (&lt;code&gt;examples/27_weighted_lb.rs&lt;/code&gt;)
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;services&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;background&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;background_service&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Key Imports for Load Balancing&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora_load_balancing&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;LoadBalancer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Backend&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Backends&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora_load_balancing&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;discovery&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Static&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora_load_balancing&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;selection&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;RoundRobin&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// This alias includes Weighted logic&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;collections&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;BTreeSet&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// The struct wraps the LoadBalancer.&lt;/span&gt;
&lt;span class="c1"&gt;// Note: We use 'RoundRobin' as the generic type. In Pingora, the 'RoundRobin' type alias&lt;/span&gt;
&lt;span class="c1"&gt;// is actually defined as 'Weighted&amp;lt;algorithms::RoundRobin&amp;gt;', so it supports weights natively.&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nf"&gt;LB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LoadBalancer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RoundRobin&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ProxyHttp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;LB&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new_ctx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_peer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Selection: b"" key is ignored by RoundRobin.&lt;/span&gt;
        &lt;span class="c1"&gt;// The balancer automatically handles the 3:1 distribution logic.&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;upstream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="na"&gt;.0&lt;/span&gt;
            &lt;span class="nf"&gt;.select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;b""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;.ok_or_else&lt;/span&gt;&lt;span class="p"&gt;(||&lt;/span&gt; &lt;span class="nn"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;explain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ErrorType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Custom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"NoUpstreamAvailable"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"Empty upstream pool"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Selected upstream: {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;upstream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;upstream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"weighted.upstream"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;));&lt;/span&gt;

        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_request_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;upstream_request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;RequestHeader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;upstream_request&lt;/span&gt;&lt;span class="nf"&gt;.insert_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Host"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"weighted-cluster"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// 1. Create Backends with specific Weights&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;blue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Backend&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new_with_weight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"172.28.0.20:8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;green&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Backend&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new_with_weight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"172.28.0.21:8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. Construct the Upstream Set Manually&lt;/span&gt;
    &lt;span class="c1"&gt;// We CANNOT use LoadBalancer::try_from_iter() here, because that helper method&lt;/span&gt;
    &lt;span class="c1"&gt;// extracts IPs and creates *new* Backend objects with default weight (1).&lt;/span&gt;
    &lt;span class="c1"&gt;// We must pass our pre-weighted Backend objects into a Static discovery service.&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;BTreeSet&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="nf"&gt;.insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="nf"&gt;.insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;green&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;discovery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Static&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;backends&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Backends&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;discovery&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;upstreams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;LoadBalancer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_backends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;backends&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 3. Create Background Service&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;background&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;background_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"weighted_lb"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;lb_ref&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;background&lt;/span&gt;&lt;span class="nf"&gt;.task&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// 4. Create Proxy Service&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;http_proxy_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;LB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lb_ref&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="nf"&gt;.add_tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:6171"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Weighted Load Balancer running on 0.0.0.0:6171"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Configuration: Blue (Weight 3) vs Green (Weight 1)"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;background&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. Start the Proxy
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 27_weighted_lb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  2. Run the Traffic Test
&lt;/h3&gt;

&lt;p&gt;We will run &lt;code&gt;curl&lt;/code&gt; 4 times. Based on our 3:1 configuration, we expect 3 responses from Blue and 1 from Green.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; pingora_client_1 bash &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"for i in {1..4}; do curl -s http://172.28.0.10:6171/; echo; done"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Analyze Output
&lt;/h3&gt;

&lt;p&gt;You should see a pattern similar to this (order may vary slightly, but the ratio holds):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;'Response from BLUE'
'Response from BLUE'
'Response from BLUE'
'Response from GREEN'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the proxy logs, you can confirm the selection details:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[INFO] Selected upstream: Backend { addr: Inet(172.28.0.20:8080), weight: 3, ... }
[INFO] Selected upstream: Backend { addr: Inet(172.28.0.20:8080), weight: 3, ... }
[INFO] Selected upstream: Backend { addr: Inet(172.28.0.20:8080), weight: 3, ... }
[INFO] Selected upstream: Backend { addr: Inet(172.28.0.21:8080), weight: 1, ... }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Lesson 28: Consistent Hashing (Ketama)
&lt;/h1&gt;

&lt;p&gt;In a typical load balancing setup (like Round Robin), requests are distributed evenly. However, for applications relying on &lt;strong&gt;local caching&lt;/strong&gt; or &lt;strong&gt;user sessions&lt;/strong&gt;, this is inefficient. If User A logs in on Server 1, but their next request goes to Server 2, they might be logged out or experience a "cold" cache.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Consistent Hashing&lt;/strong&gt; ensures that requests with the same "Key" (e.g., URL path, User ID, or IP address) are &lt;strong&gt;always&lt;/strong&gt; routed to the same upstream server.&lt;/p&gt;

&lt;p&gt;In this lesson, we will implement the &lt;strong&gt;Ketama&lt;/strong&gt; algorithm. We will route requests based on their &lt;strong&gt;URL Path&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/user/123&lt;/code&gt; → Always hits Server A.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/user/456&lt;/code&gt; → Always hits Server B.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key Concepts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Hash Ring
&lt;/h3&gt;

&lt;p&gt;Imagine a clock face (a ring).&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Nodes:&lt;/strong&gt; We hash our servers (Blue, Green) to specific points on this ring.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keys:&lt;/strong&gt; We hash the incoming request path (e.g., &lt;code&gt;/user/123&lt;/code&gt;) to a point on the ring.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Routing:&lt;/strong&gt; To find the correct server, we move &lt;strong&gt;clockwise&lt;/strong&gt; from the request's point until we hit a server.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Why Ketama?&lt;/strong&gt;&lt;br&gt;
It minimizes disruption. If you add or remove a server, only the keys immediately "behind" that server on the ring are reassigned. In standard modulo hashing (&lt;code&gt;hash % N&lt;/code&gt;), changing &lt;code&gt;N&lt;/code&gt; (the number of servers) would reshuffle nearly 100% of your traffic.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Code (&lt;code&gt;examples/28_consistent_hashing.rs&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;We configure the &lt;code&gt;LoadBalancer&lt;/code&gt; to use &lt;code&gt;KetamaHashing&lt;/code&gt; as its selection strategy. The critical change happens in &lt;code&gt;upstream_peer&lt;/code&gt;, where we extract the path and pass it as the hash key.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;services&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;background&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;background_service&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora_load_balancing&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;LoadBalancer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// Import the specific Ketama algorithm&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora_load_balancing&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;selection&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;consistent&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;KetamaHashing&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// 1. The Struct: Wraps LoadBalancer with the KetamaHashing strategy&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nf"&gt;LB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LoadBalancer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;KetamaHashing&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ProxyHttp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;LB&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new_ctx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_peer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// 2. The Key: Extract the path from the request URI&lt;/span&gt;
        &lt;span class="c1"&gt;// This is what makes the routing "sticky" to the URL.&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="nf"&gt;.req_header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="py"&gt;.uri&lt;/span&gt;&lt;span class="nf"&gt;.path&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="nf"&gt;.as_bytes&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// 3. Selection: Pass the path as the hash key.&lt;/span&gt;
        &lt;span class="c1"&gt;// This ensures Deterministic Routing: Same Key -&amp;gt; Same Server.&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;upstream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="na"&gt;.0&lt;/span&gt;
            &lt;span class="nf"&gt;.select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;.ok_or_else&lt;/span&gt;&lt;span class="p"&gt;(||&lt;/span&gt; &lt;span class="nn"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;explain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ErrorType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Custom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"NoUpstreamAvailable"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"Empty upstream pool"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Path '{}' hashed to upstream: {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;upstream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;upstream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"consistent.cluster"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;));&lt;/span&gt;

        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_request_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;upstream_request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;RequestHeader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;upstream_request&lt;/span&gt;&lt;span class="nf"&gt;.insert_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Host"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"consistent-hash-cluster"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// 4. Initialization: Create the pool.&lt;/span&gt;
    &lt;span class="c1"&gt;// Ketama handles distribution via the ring, so 'try_from_iter' is sufficient.&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;upstreams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;LoadBalancer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;try_from_iter&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="s"&gt;"172.28.0.20:8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Blue&lt;/span&gt;
        &lt;span class="s"&gt;"172.28.0.21:8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Green&lt;/span&gt;
    &lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// 5. Background Service: Essential for LB maintenance&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;background&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;background_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"consistent_lb"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;lb_ref&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;background&lt;/span&gt;&lt;span class="nf"&gt;.task&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;http_proxy_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;LB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lb_ref&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="nf"&gt;.add_tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:6172"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Consistent Hashing LB running on 0.0.0.0:6172"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Try: curl http://127.0.0.1:6172/user/123 vs /user/456"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;background&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;To verify that the hashing is consistent, we need to show that requesting the same URL repeatedly &lt;em&gt;always&lt;/em&gt; results in the same backend being chosen, but different URLs might map to different backends.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Start the Proxy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 28_consistent_hashing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Run the Test Loop
&lt;/h3&gt;

&lt;p&gt;Run the following command in your client terminal. It loops through 5 different "User IDs" (paths), hitting each one twice.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; pingora_client_1 bash &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"for i in {1..5}; do curl -s http://172.28.0.10:6172/user/&lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="s2"&gt;i; echo; curl -s http://172.28.0.10:6172/user/&lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="s2"&gt;i; echo; echo ---; done"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Analyze Results
&lt;/h3&gt;

&lt;p&gt;You will observe &lt;strong&gt;Sticky&lt;/strong&gt; behavior:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;User 1:&lt;/strong&gt; Might go to &lt;strong&gt;Green&lt;/strong&gt; both times.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User 3:&lt;/strong&gt; Might go to &lt;strong&gt;Blue&lt;/strong&gt; both times.&lt;/li&gt;
&lt;li&gt;Unlike Round Robin, you will never see &lt;code&gt;/user/1&lt;/code&gt; go to Blue and then immediately swap to Green.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Sample Log Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[INFO] Path '/user/1' hashed to upstream: Backend { addr: Inet(172.28.0.21:8080)... }
[INFO] Path '/user/1' hashed to upstream: Backend { addr: Inet(172.28.0.21:8080)... }
...
[INFO] Path '/user/3' hashed to upstream: Backend { addr: Inet(172.28.0.20:8080)... }
[INFO] Path '/user/3' hashed to upstream: Backend { addr: Inet(172.28.0.20:8080)... }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This confirms that Pingora is correctly using the path as a stable routing key.&lt;/p&gt;

&lt;h1&gt;
  
  
  Lesson 29: Sticky Sessions (Cookie-Based)
&lt;/h1&gt;

&lt;p&gt;In the previous lesson, we used &lt;strong&gt;Consistent Hashing&lt;/strong&gt; on the &lt;em&gt;URL Path&lt;/em&gt;. This ensures that &lt;code&gt;/image.png&lt;/code&gt; always comes from the same server, which is great for caching.&lt;/p&gt;

&lt;p&gt;However, for stateful applications (e.g., a shopping cart or a login session), we need &lt;strong&gt;User Affinity&lt;/strong&gt;. We want User A to stay on Server Blue, regardless of whether they visit &lt;code&gt;/home&lt;/code&gt;, &lt;code&gt;/profile&lt;/code&gt;, or &lt;code&gt;/cart&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To achieve this, we combine &lt;strong&gt;Consistent Hashing&lt;/strong&gt; with &lt;strong&gt;Cookies&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Concepts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. The Session Cookie
&lt;/h3&gt;

&lt;p&gt;The logic flow is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Incoming Request:&lt;/strong&gt; Does it have a &lt;code&gt;session-id&lt;/code&gt; cookie?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No (New User):&lt;/strong&gt; Generate a new ID (e.g., &lt;code&gt;user-100&lt;/code&gt;), hash it to pick a server, and send a &lt;code&gt;Set-Cookie&lt;/code&gt; header so the client remembers it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Yes (Returning User):&lt;/strong&gt; Extract the ID, hash it (which results in the exact same server), and route the request.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  2. The Context (&lt;code&gt;CTX&lt;/code&gt;) Bridge
&lt;/h3&gt;

&lt;p&gt;This lesson demonstrates a critical pattern in Pingora: &lt;strong&gt;Sharing state between Request and Response phases.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We detect/generate the session ID in &lt;code&gt;upstream_peer&lt;/code&gt; (Request Phase).&lt;/li&gt;
&lt;li&gt;We need to write the &lt;code&gt;Set-Cookie&lt;/code&gt; header in &lt;code&gt;response_filter&lt;/code&gt; (Response Phase).&lt;/li&gt;
&lt;li&gt;We use a custom &lt;code&gt;StickyCtx&lt;/code&gt; struct to carry this data across the lifecycle.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Code (&lt;code&gt;examples/29_sticky_sessions.rs&lt;/code&gt;)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;services&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;background&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;background_service&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;http&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ResponseHeader&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora_load_balancing&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;LoadBalancer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora_load_balancing&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;selection&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;consistent&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;KetamaHashing&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;atomic&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;AtomicUsize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Ordering&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Global counter to generate simple unique session IDs for this demo&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;SESSION_COUNTER&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AtomicUsize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;AtomicUsize&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// 1. Context: Holds state between the Request phase and the Response phase.&lt;/span&gt;
&lt;span class="c1"&gt;// If we generate a new ID, we must store it here to inject the Set-Cookie header later.&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;StickyCtx&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;new_session_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Struct wrapping the Ketama-based Load Balancer&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nf"&gt;LB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LoadBalancer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;KetamaHashing&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ProxyHttp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;LB&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;StickyCtx&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new_ctx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;StickyCtx&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;new_session_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_peer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;session_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// 2. Cookie Parsing Logic&lt;/span&gt;
        &lt;span class="c1"&gt;// We look for "Cookie: session-id=xyz".&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cookie_val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="nf"&gt;.req_header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="py"&gt;.headers&lt;/span&gt;&lt;span class="nf"&gt;.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Cookie"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;cookie_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cookie_val&lt;/span&gt;&lt;span class="nf"&gt;.to_str&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;part&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cookie_str&lt;/span&gt;&lt;span class="nf"&gt;.split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;';'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;part&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;part&lt;/span&gt;&lt;span class="nf"&gt;.trim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;part&lt;/span&gt;&lt;span class="nf"&gt;.starts_with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"session-id="&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;session_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;part&lt;/span&gt;&lt;span class="nf"&gt;.trim_start_matches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"session-id="&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Found existing session cookie: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// 3. New Session Generation&lt;/span&gt;
        &lt;span class="c1"&gt;// If no cookie was found, create a new ID and mark it for injection.&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="nf"&gt;.is_empty&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;new_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SESSION_COUNTER&lt;/span&gt;&lt;span class="nf"&gt;.fetch_add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;Ordering&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Relaxed&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;session_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user-{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"No cookie found. Generated new session id: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.new_session_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// 4. Consistent Hashing Selection&lt;/span&gt;
        &lt;span class="c1"&gt;// Crucial: Use the session_id as the hash key, NOT the request path.&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="nf"&gt;.as_bytes&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;upstream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="na"&gt;.0&lt;/span&gt;
            &lt;span class="nf"&gt;.select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;.ok_or_else&lt;/span&gt;&lt;span class="p"&gt;(||&lt;/span&gt; &lt;span class="nn"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;explain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ErrorType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Custom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"NoUpstreamAvailable"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"Empty upstream pool"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Session '{}' stuck to upstream: {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;upstream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;upstream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"sticky.cluster"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;));&lt;/span&gt;

        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// 5. Response Filter&lt;/span&gt;
    &lt;span class="c1"&gt;// Injects the Set-Cookie header if we generated a new session ID.&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;response_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;upstream_response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;ResponseHeader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.new_session_id&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;cookie_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"session-id={}; Path=/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;upstream_response&lt;/span&gt;&lt;span class="nf"&gt;.insert_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Set-Cookie"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cookie_value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Injected Set-Cookie header for {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// 6. Initialization&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;upstreams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;LoadBalancer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;try_from_iter&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="s"&gt;"172.28.0.20:8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Blue&lt;/span&gt;
        &lt;span class="s"&gt;"172.28.0.21:8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Green&lt;/span&gt;
    &lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;background&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;background_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sticky_lb"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;lb_ref&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;background&lt;/span&gt;&lt;span class="nf"&gt;.task&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;http_proxy_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;LB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lb_ref&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="nf"&gt;.add_tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:6173"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Sticky Session LB running on 0.0.0.0:6173"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;background&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;To verify sticky sessions, we must act like a browser that saves cookies. We will use &lt;code&gt;curl&lt;/code&gt;'s "cookie jar" feature.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Start the Proxy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 29_sticky_sessions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. The First Visit (New User)
&lt;/h3&gt;

&lt;p&gt;Run this command. It saves received cookies to &lt;code&gt;cookies.txt&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; pingora_client_1 curl &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; /cookies.txt http://172.28.0.10:6173/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Result:&lt;/strong&gt; You will see &lt;code&gt;&amp;lt; Set-Cookie: session-id=user-100; Path=/&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Routing:&lt;/strong&gt; The logs will show "No cookie found" and routing to a specific server (e.g., Green).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. The Second Visit (Returning User)
&lt;/h3&gt;

&lt;p&gt;Run this command. It reads cookies from &lt;code&gt;cookies.txt&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; pingora_client_1 curl &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nt"&gt;-b&lt;/span&gt; /cookies.txt http://172.28.0.10:6173/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Result:&lt;/strong&gt; You will see &lt;code&gt;&amp;gt; Cookie: session-id=user-100&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Routing:&lt;/strong&gt; The logs will show "Found existing session cookie" and routing to &lt;strong&gt;Green&lt;/strong&gt; (the same server).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. The Third Visit (Different URL)
&lt;/h3&gt;

&lt;p&gt;Try a different path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; pingora_client_1 curl &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nt"&gt;-b&lt;/span&gt; /cookies.txt http://172.28.0.10:6173/different/path
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Result:&lt;/strong&gt; Still routed to &lt;strong&gt;Green&lt;/strong&gt;. The routing is now tied to the &lt;em&gt;User&lt;/em&gt;, not the &lt;em&gt;URL&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Lesson 30: TCP Health Checks
&lt;/h1&gt;

&lt;p&gt;In previous lessons, our Load Balancer was "blind." If an upstream server crashed, Pingora would still try to route requests to it, resulting in errors for the client (502 Bad Gateway) until the server came back online.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Active Health Checking&lt;/strong&gt; solves this. Pingora can actively probe your servers in the background. If a server fails to respond, it is marked "unhealthy" and removed from the rotation &lt;em&gt;before&lt;/em&gt; a real user request hits it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Concepts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Active vs. Passive
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Passive Checking:&lt;/strong&gt; The load balancer watches real traffic. If 5 requests fail in a row, it marks the server down. &lt;em&gt;Downside: Real users have to see errors for the system to react.&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Active Checking:&lt;/strong&gt; The load balancer opens a separate connection (probe) every second. If the probe fails, the server is marked down. &lt;em&gt;Upside: The system often reacts before users notice.&lt;/em&gt; We are using this approach today.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. The &lt;code&gt;TcpHealthCheck&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This is a Layer 4 check. It simply tries to establish a TCP handshake.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Success:&lt;/strong&gt; Handshake completes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Failure:&lt;/strong&gt; Connection refused or timeout.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. The Race Condition
&lt;/h3&gt;

&lt;p&gt;There is always a tiny window between probes (e.g., 1 second) where a server could die. To handle this, we also set a short &lt;code&gt;connection_timeout&lt;/code&gt; on the actual request peer. This ensures that if we &lt;em&gt;do&lt;/em&gt; hit a dead server during that window, we fail fast rather than hanging for a minute.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Code (&lt;code&gt;examples/30_health_check_tcp.rs&lt;/code&gt;)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;services&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;background&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;background_service&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora_load_balancing&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;LoadBalancer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora_load_balancing&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;selection&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;RoundRobin&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora_load_balancing&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;health_check&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;TcpHealthCheck&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nf"&gt;LB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LoadBalancer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RoundRobin&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ProxyHttp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;LB&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new_ctx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_peer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Selection: The LoadBalancer handles filtering.&lt;/span&gt;
        &lt;span class="c1"&gt;// If a backend is marked unhealthy, select() will effectively skip it.&lt;/span&gt;
        &lt;span class="c1"&gt;// If ALL backends are unhealthy, this returns None (mapped to Error).&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;upstream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="na"&gt;.0&lt;/span&gt;
            &lt;span class="nf"&gt;.select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;b""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;.ok_or_else&lt;/span&gt;&lt;span class="p"&gt;(||&lt;/span&gt; &lt;span class="nn"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;explain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ErrorType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Custom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"NoUpstreamAvailable"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"All upstreams are down"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Routed to upstream: {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;upstream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;upstream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"health-check.cluster"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;));&lt;/span&gt;

        &lt;span class="c1"&gt;// Critical: Set timeouts on the actual request peer as well.&lt;/span&gt;
        &lt;span class="c1"&gt;// If the health check hasn't caught a failure yet (race condition window),&lt;/span&gt;
        &lt;span class="c1"&gt;// we don't want the client waiting 60s.&lt;/span&gt;
        &lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="py"&gt;.options.read_timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_secs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="py"&gt;.options.connection_timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_secs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_request_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;upstream_request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;RequestHeader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;upstream_request&lt;/span&gt;&lt;span class="nf"&gt;.insert_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Host"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"health-check-cluster"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// 1. Initialize Load Balancer&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;upstreams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;LoadBalancer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;try_from_iter&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="s"&gt;"172.28.0.20:8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Blue&lt;/span&gt;
        &lt;span class="s"&gt;"172.28.0.21:8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Green&lt;/span&gt;
    &lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. Configure the TCP Health Check&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;hc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;TcpHealthCheck&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="c1"&gt;// Fail Fast: probe times out after 1s&lt;/span&gt;
    &lt;span class="n"&gt;hc&lt;/span&gt;&lt;span class="py"&gt;.peer_template.options.connection_timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_secs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="c1"&gt;// Sensitivity: 1 failure marks it down, 1 success marks it up&lt;/span&gt;
    &lt;span class="n"&gt;hc&lt;/span&gt;&lt;span class="py"&gt;.consecutive_success&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;hc&lt;/span&gt;&lt;span class="py"&gt;.consecutive_failure&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// 3. Attach Check to LB&lt;/span&gt;
    &lt;span class="n"&gt;upstreams&lt;/span&gt;&lt;span class="nf"&gt;.set_health_check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;upstreams&lt;/span&gt;&lt;span class="py"&gt;.health_check_frequency&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_secs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;upstreams&lt;/span&gt;&lt;span class="py"&gt;.parallel_health_check&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// 4. Background Service (The "Heart" of the health check)&lt;/span&gt;
    &lt;span class="c1"&gt;// The health checks run inside this service.&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;background&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;background_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"health_check_lb"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;lb_ref&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;background&lt;/span&gt;&lt;span class="nf"&gt;.task&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;http_proxy_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;LB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lb_ref&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="nf"&gt;.add_tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:6174"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"TCP Health Check LB running on 0.0.0.0:6174"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;background&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;To verify this, we will crash a server mid-stream and watch Pingora react.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Start the Proxy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 30_health_check_tcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Start the Traffic Loop
&lt;/h3&gt;

&lt;p&gt;In a separate terminal, run a continuous check against the proxy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do &lt;/span&gt;docker &lt;span class="nb"&gt;exec &lt;/span&gt;pingora_client_1 curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://172.28.0.10:6174/&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;sleep &lt;/span&gt;0.5&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Output:&lt;/em&gt; You should see alternating &lt;code&gt;Response from BLUE&lt;/code&gt; and &lt;code&gt;Response from GREEN&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Kill Blue
&lt;/h3&gt;

&lt;p&gt;In a third terminal, stop the Blue container.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker container stop pingora-guide-upstream_blue-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Observe Reaction
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Client:&lt;/strong&gt; The output will immediately switch to &lt;code&gt;Response from GREEN&lt;/code&gt; only. You might see one single error if you hit the race window, but otherwise, it's seamless.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logs:&lt;/strong&gt; You will see the detection log:
&lt;code&gt;[WARN] Backend { ... } becomes unhealthy, ConnectTimedout&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  5. Revive Blue
&lt;/h3&gt;

&lt;p&gt;Start the container again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker container start pingora-guide-upstream_blue-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Logs:&lt;/strong&gt; &lt;code&gt;[INFO] Backend { ... } becomes healthy&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client:&lt;/strong&gt; Traffic seamlessly resumes alternating between Blue and Green.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This confirms that Pingora is actively managing the health of your upstream pool.&lt;/p&gt;

&lt;h1&gt;
  
  
  Lesson 31: HTTP Health Checks
&lt;/h1&gt;

&lt;p&gt;In the previous lesson, we used &lt;strong&gt;TCP Health Checks&lt;/strong&gt; to verify that a server's port was open. However, "Port Open" does not mean "Application Working."&lt;/p&gt;

&lt;p&gt;A server can be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Deadlocked:&lt;/strong&gt; The process is running and accepting TCP connections, but stuck in an infinite loop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Overloaded:&lt;/strong&gt; It accepts connections but never responds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Broken:&lt;/strong&gt; It returns &lt;code&gt;500 Internal Server Error&lt;/code&gt; for every request.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A TCP check will pass in all these cases. An &lt;strong&gt;HTTP Health Check&lt;/strong&gt; (Layer 7) solves this by sending a real request (e.g., &lt;code&gt;GET /&lt;/code&gt;) and requiring a valid &lt;code&gt;200 OK&lt;/code&gt; response.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Concepts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Layer 4 vs. Layer 7 Checks
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Layer 4 (TCP):&lt;/strong&gt; "Is the phone line connected?" (Fast, cheap, catches crashes).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Layer 7 (HTTP):&lt;/strong&gt; "Is the person answering the phone making sense?" (Slower, more expensive, catches application bugs).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. The &lt;code&gt;HttpHealthCheck&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This struct performs the active probing.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Request:&lt;/strong&gt; &lt;code&gt;GET /&lt;/code&gt; (default, but customizable).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validation:&lt;/strong&gt; Requires status &lt;code&gt;200 OK&lt;/code&gt; (default).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timeout:&lt;/strong&gt; If the server accepts the connection but doesn't send headers back within &lt;code&gt;read_timeout&lt;/code&gt;, it is marked unhealthy.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Code (&lt;code&gt;examples/31_health_check_http.rs&lt;/code&gt;)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;services&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;background&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;background_service&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora_load_balancing&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;LoadBalancer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora_load_balancing&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;selection&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;RoundRobin&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora_load_balancing&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;health_check&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HttpHealthCheck&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nf"&gt;LB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LoadBalancer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RoundRobin&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ProxyHttp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;LB&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new_ctx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_peer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Selection: Returns only endpoints responding 200 OK to probes&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;upstream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="na"&gt;.0&lt;/span&gt;
            &lt;span class="nf"&gt;.select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;b""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;.ok_or_else&lt;/span&gt;&lt;span class="p"&gt;(||&lt;/span&gt; &lt;span class="nn"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;explain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ErrorType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Custom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"NoUpstreamAvailable"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"All upstreams are down"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Routed to upstream: {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;upstream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;upstream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"http-health-check.cluster.local"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="p"&gt;));&lt;/span&gt;

        &lt;span class="c1"&gt;// Critical: Set timeouts on client traffic too.&lt;/span&gt;
        &lt;span class="c1"&gt;// This ensures the client doesn't hang if the server dies between probes.&lt;/span&gt;
        &lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="py"&gt;.options.read_timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_secs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="py"&gt;.options.connection_timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_secs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_request_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;upstream_request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;RequestHeader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;upstream_request&lt;/span&gt;&lt;span class="nf"&gt;.insert_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Host"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"http-health-check-cluster"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// 1. Initialize Load Balancer&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;upstreams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;LoadBalancer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;try_from_iter&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="s"&gt;"172.28.0.20:8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"172.28.0.21:8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. Configure HTTP Health Check&lt;/span&gt;
    &lt;span class="c1"&gt;// "localhost" -&amp;gt; The Host header sent in the probe&lt;/span&gt;
    &lt;span class="c1"&gt;// false -&amp;gt; No TLS (use HTTP)&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;hc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;HttpHealthCheck&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"localhost"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="c1"&gt;// 3. Tuning&lt;/span&gt;
    &lt;span class="c1"&gt;// Read timeout catches the "Hang": connection established, but no data returned.&lt;/span&gt;
    &lt;span class="n"&gt;hc&lt;/span&gt;&lt;span class="py"&gt;.peer_template.options.read_timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_secs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;hc&lt;/span&gt;&lt;span class="py"&gt;.peer_template.options.connection_timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_secs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;hc&lt;/span&gt;&lt;span class="py"&gt;.consecutive_success&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;hc&lt;/span&gt;&lt;span class="py"&gt;.consecutive_failure&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// 4. Attach Check&lt;/span&gt;
    &lt;span class="c1"&gt;// Note: HttpHealthCheck must be manually boxed here.&lt;/span&gt;
    &lt;span class="n"&gt;upstreams&lt;/span&gt;&lt;span class="nf"&gt;.set_health_check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Frequency: How often to probe (every 1s)&lt;/span&gt;
    &lt;span class="n"&gt;upstreams&lt;/span&gt;&lt;span class="py"&gt;.health_check_frequency&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_secs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;upstreams&lt;/span&gt;&lt;span class="py"&gt;.parallel_health_check&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// 5. Background Service&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;background&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;background_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"http_health_check_lb"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;lb_ref&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;background&lt;/span&gt;&lt;span class="nf"&gt;.task&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;http_proxy_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;LB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lb_ref&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="nf"&gt;.add_tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:6175"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"HTTP Health Check LB running on 0.0.0.0:6175"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;background&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;To verify the power of Layer 7 checks, we will simulate a "Frozen App."&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Start the Proxy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 31_health_check_http
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Start Traffic
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do &lt;/span&gt;docker &lt;span class="nb"&gt;exec &lt;/span&gt;pingora_client_1 curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://172.28.0.10:6175/&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;sleep &lt;/span&gt;0.5&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Freeze the App (Docker Pause)
&lt;/h3&gt;

&lt;p&gt;Instead of stopping the container (which closes the port), we &lt;code&gt;pause&lt;/code&gt; it. This freezes the process in memory. The TCP stack (kernel) is still alive, so &lt;code&gt;telnet&lt;/code&gt; would succeed, but the app cannot respond.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker container pause pingora-guide-upstream_blue-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Observe Reaction
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Logs:&lt;/strong&gt; &lt;code&gt;[WARN] Backend ... becomes unhealthy, ReadTimedout&lt;/code&gt;.

&lt;ul&gt;
&lt;li&gt;Pingora connected (TCP success), waited 1 second for headers, got nothing, and marked it dead.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Traffic:&lt;/strong&gt; All traffic shifts to Green.&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. Unfreeze
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker container unpause pingora-guide-upstream_blue-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The app wakes up, answers the next probe, and re-enters rotation.&lt;/p&gt;

&lt;h1&gt;
  
  
  Lesson 32: Custom Health Checks (Body Inspection)
&lt;/h1&gt;

&lt;p&gt;Standard HTTP health checks have a limitation: they typically only validate the &lt;strong&gt;Status Code&lt;/strong&gt;. A server might return &lt;code&gt;200 OK&lt;/code&gt; but serve an error page saying "Database Connection Failed."&lt;/p&gt;

&lt;p&gt;To catch these "zombie" servers, we need &lt;strong&gt;Deep Inspection&lt;/strong&gt;. We must read the response body and verify that it matches a specific pattern.&lt;/p&gt;

&lt;p&gt;In this lesson, we enforce a strict business rule:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rule:&lt;/strong&gt; A server is healthy &lt;strong&gt;only&lt;/strong&gt; if its response contains the string &lt;code&gt;"BLUE"&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scenario:&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Upstream Blue:&lt;/strong&gt; Returns "Response from BLUE" -&amp;gt; &lt;strong&gt;Pass&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Upstream Green:&lt;/strong&gt; Returns "Response from GREEN" -&amp;gt; &lt;strong&gt;Fail&lt;/strong&gt; (even though it returns 200 OK).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key Concepts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. The &lt;code&gt;HealthCheck&lt;/code&gt; Trait
&lt;/h3&gt;

&lt;p&gt;Pingora provides built-in &lt;code&gt;TcpHealthCheck&lt;/code&gt; and &lt;code&gt;HttpHealthCheck&lt;/code&gt;, but for custom logic, we implement the &lt;code&gt;HealthCheck&lt;/code&gt; trait ourselves. This gives us complete control. We can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Validate specific JSON fields.&lt;/li&gt;
&lt;li&gt;Check cryptographic signatures.&lt;/li&gt;
&lt;li&gt;Verify database connectivity.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Manual Socket Handling
&lt;/h3&gt;

&lt;p&gt;Because we are bypassing the standard check, we must handle the networking manually.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Connect:&lt;/strong&gt; Open a TCP stream to the backend.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write:&lt;/strong&gt; Send a raw HTTP request bytes (&lt;code&gt;GET / ...&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read:&lt;/strong&gt; Buffer the response.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validate:&lt;/strong&gt; check if the buffer contains our target string.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Code (&lt;code&gt;examples/32_custom_health_check.rs&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;We define a struct &lt;code&gt;BodyMatchCheck&lt;/code&gt; and implement the health check logic to scan the response body.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;services&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;background&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;background_service&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora_load_balancing&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;LoadBalancer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Backend&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora_load_balancing&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;selection&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;RoundRobin&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora_load_balancing&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;health_check&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HealthCheck&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;protocols&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;l4&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;SocketAddr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;net&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;TcpStream&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;AsyncReadExt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AsyncWriteExt&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// 1. Define Custom Health Check Struct&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;BodyMatchCheck&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;expected_string&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// 2. Implement the HealthCheck Trait&lt;/span&gt;
&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;HealthCheck&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;BodyMatchCheck&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Backend&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// A. Extract Standard Socket Address&lt;/span&gt;
        &lt;span class="c1"&gt;// Pingora uses a custom SocketAddr enum (Inet/Unix). We convert it for Tokio.&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="py"&gt;.addr&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nn"&gt;SocketAddr&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Inet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nn"&gt;SocketAddr&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Unix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;explain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ErrorType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;InternalError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Custom check only supports TCP backends"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="c1"&gt;// B. Connect (Fail Fast Timeout)&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_secs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
            &lt;span class="nn"&gt;TcpStream&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;explain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ErrorType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ConnectTimedout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Connection timed out"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="c1"&gt;// C. Send Raw HTTP Request&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"GET / HTTP/1.1&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s"&gt;Host: {}&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s"&gt;Connection: close&lt;/span&gt;&lt;span class="se"&gt;\r\n\r\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.host&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="nf"&gt;.write_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="nf"&gt;.as_bytes&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
             &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;explain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ErrorType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;WriteError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// D. Read Response&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0u8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_secs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
            &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="nf"&gt;.read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;explain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ErrorType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ReadTimedout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Read timed out"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="c1"&gt;// E. Validate Content&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;response_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_utf8_lossy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response_text&lt;/span&gt;&lt;span class="nf"&gt;.contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.expected_string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nd"&gt;warn!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Custom Check Failed for {:?}: Body did not contain '{}'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.expected_string&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;explain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ErrorType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Custom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"InvalidBody"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"Body validation failed"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;health_threshold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;usize&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="mi"&gt;1&lt;/span&gt; 
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nf"&gt;LB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LoadBalancer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RoundRobin&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ProxyHttp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;LB&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new_ctx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_peer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Selection: Automatically filters out nodes failing the custom check&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;upstream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="na"&gt;.0&lt;/span&gt;
            &lt;span class="nf"&gt;.select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;b""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;.ok_or_else&lt;/span&gt;&lt;span class="p"&gt;(||&lt;/span&gt; &lt;span class="nn"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;explain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ErrorType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Custom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"NoUpstreamAvailable"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"All upstreams are down"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Routed to upstream: {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;upstream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;upstream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"custom-check.cluster.local"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="p"&gt;));&lt;/span&gt;

        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_request_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;upstream_request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;RequestHeader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;upstream_request&lt;/span&gt;&lt;span class="nf"&gt;.insert_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Host"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"custom-check-cluster"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;upstreams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;LoadBalancer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;try_from_iter&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="s"&gt;"172.28.0.20:8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Blue&lt;/span&gt;
        &lt;span class="s"&gt;"172.28.0.21:8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Green&lt;/span&gt;
    &lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Configure the Custom Check&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;hc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BodyMatchCheck&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"localhost"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;expected_string&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"BLUE"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="c1"&gt;// Attach it (Boxing required)&lt;/span&gt;
    &lt;span class="n"&gt;upstreams&lt;/span&gt;&lt;span class="nf"&gt;.set_health_check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hc&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;upstreams&lt;/span&gt;&lt;span class="py"&gt;.health_check_frequency&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_secs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;upstreams&lt;/span&gt;&lt;span class="py"&gt;.parallel_health_check&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;background&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;background_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"custom_health_check"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;lb_ref&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;background&lt;/span&gt;&lt;span class="nf"&gt;.task&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;http_proxy_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;LB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lb_ref&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="nf"&gt;.add_tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:6176"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Custom Health Check LB running on 0.0.0.0:6176"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;background&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;We will verify that &lt;strong&gt;Upstream Green&lt;/strong&gt; is removed from the rotation because it fails the string match.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Start the Proxy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 32_custom_health_check
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Observe the Logs
&lt;/h3&gt;

&lt;p&gt;Watch the logs immediately. You will see Pingora rejecting Green:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[WARN] Custom Check Failed for 172.28.0.21:8080: Body did not contain 'BLUE'
[WARN] Backend { ... } becomes unhealthy, InvalidBody context: Body validation failed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Verify Traffic
&lt;/h3&gt;

&lt;p&gt;Run a loop against the proxy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do &lt;/span&gt;docker &lt;span class="nb"&gt;exec &lt;/span&gt;pingora_client_1 curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://172.28.0.10:6176/&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;sleep &lt;/span&gt;0.5&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected Output:&lt;/strong&gt;&lt;br&gt;
You should see 100% Blue responses.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;'Response from BLUE'
'Response from BLUE'
'Response from BLUE'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Green is returning &lt;code&gt;200 OK&lt;/code&gt; (so a standard HTTP check would pass it), but our custom logic successfully filtered it out based on the content.&lt;/p&gt;

&lt;h1&gt;
  
  
  Lesson 33: Active-Passive Load Balancing (Failover)
&lt;/h1&gt;

&lt;p&gt;In standard load balancing (like Round Robin), all servers are "Active." Traffic is shared among them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Active-Passive&lt;/strong&gt; routing is different. You have a &lt;strong&gt;Primary&lt;/strong&gt; server (or pool) that handles 100% of the traffic. You also have a &lt;strong&gt;Backup&lt;/strong&gt; server that sits idle, receiving zero traffic, until the Primary fails. This is common for disaster recovery setups or when the backup is a "Sorry Page" server hosted in a different region.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Concepts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Pool Separation
&lt;/h3&gt;

&lt;p&gt;To achieve this in Pingora, we &lt;strong&gt;do not&lt;/strong&gt; put the Backup server into the &lt;code&gt;LoadBalancer&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If we put both Blue and Green in the &lt;code&gt;LoadBalancer&lt;/code&gt;, Pingora would try to route traffic to both (Active-Active).&lt;/li&gt;
&lt;li&gt;Instead, we put &lt;strong&gt;only Blue&lt;/strong&gt; in the &lt;code&gt;LoadBalancer&lt;/code&gt;. Green is defined manually in the code as a fallback.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. The Logic Flow
&lt;/h3&gt;

&lt;p&gt;We rely on the return value of &lt;code&gt;select()&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Some(upstream)&lt;/code&gt;:&lt;/strong&gt; The Primary is healthy. Use it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;None&lt;/code&gt;:&lt;/strong&gt; The Primary is unhealthy (the pool is empty). Execute the fallback logic to route to the Backup.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Code (&lt;code&gt;examples/33_active_passive_lb.rs&lt;/code&gt;)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// Import background service for running health checks&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;services&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;background&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;background_service&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora_load_balancing&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;LoadBalancer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora_load_balancing&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;selection&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;RoundRobin&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora_load_balancing&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;health_check&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;TcpHealthCheck&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nf"&gt;LB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LoadBalancer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RoundRobin&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ProxyHttp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;LB&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new_ctx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_peer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// 1. Attempt to select from the Primary Pool&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;selection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="na"&gt;.0&lt;/span&gt;&lt;span class="nf"&gt;.select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;b""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;selection&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;upstream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// HAPPY PATH: Primary is healthy&lt;/span&gt;
                &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Primary (Blue) is healthy. Routing to {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;upstream&lt;/span&gt;&lt;span class="py"&gt;.addr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;upstream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="s"&gt;"primary.cluster.local"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="p"&gt;));&lt;/span&gt;
                &lt;span class="c1"&gt;// Important: Set timeouts to ensure we don't hang if the server dies mid-request&lt;/span&gt;
                &lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="py"&gt;.options.read_timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_secs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
                &lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="py"&gt;.options.connection_timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_secs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
                &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nb"&gt;None&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// FAILURE PATH: Primary is down (Health Check removed it)&lt;/span&gt;
                &lt;span class="nd"&gt;warn!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"FAILOVER ALERT: Primary pool is empty! Switching to Backup (Green)."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="c1"&gt;// Manually define the Backup IP&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;backup_addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"172.28.0.21"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;backup_addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="s"&gt;"backup.cluster.local"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="p"&gt;));&lt;/span&gt;
                &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_request_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;upstream_request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;RequestHeader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;upstream_request&lt;/span&gt;&lt;span class="nf"&gt;.insert_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Host"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"active-passive-cluster"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. Initialize Load Balancer with ONLY the Primary (Blue)&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;upstreams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;LoadBalancer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;try_from_iter&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="s"&gt;"172.28.0.20:8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// 3. Configure Aggressive Health Check (Fail Fast)&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;hc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;TcpHealthCheck&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;hc&lt;/span&gt;&lt;span class="py"&gt;.peer_template.options.connection_timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_secs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;hc&lt;/span&gt;&lt;span class="py"&gt;.consecutive_success&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;hc&lt;/span&gt;&lt;span class="py"&gt;.consecutive_failure&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;upstreams&lt;/span&gt;&lt;span class="nf"&gt;.set_health_check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;upstreams&lt;/span&gt;&lt;span class="py"&gt;.health_check_frequency&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_secs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;upstreams&lt;/span&gt;&lt;span class="py"&gt;.parallel_health_check&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// 4. Background Service&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;background&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;background_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"primary_pool_lb"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;lb_ref&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;background&lt;/span&gt;&lt;span class="nf"&gt;.task&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;http_proxy_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;LB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lb_ref&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="nf"&gt;.add_tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:6177"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Active-Passive LB running on 0.0.0.0:6177"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Primary: Blue (Checked). Backup: Green (Static Fallback)."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;background&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;To verify this, we will crash the Primary and watch the traffic automatically flow to the Backup.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Start the Proxy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 33_active_passive_lb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Start the Traffic Loop
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do &lt;/span&gt;docker &lt;span class="nb"&gt;exec &lt;/span&gt;pingora_client_1 curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://172.28.0.10:6177/&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;sleep &lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Status:&lt;/strong&gt; &lt;code&gt;Response from BLUE&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logs:&lt;/strong&gt; &lt;code&gt;Primary (Blue) is healthy.&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Kill the Primary (Blue)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker container stop pingora-guide-upstream_blue-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Status:&lt;/strong&gt; &lt;code&gt;Response from GREEN&lt;/code&gt; (Immediate switch).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logs:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   [WARN] Backend ... becomes unhealthy
   [WARN] FAILOVER ALERT: Primary pool is empty! Switching to Backup (Green).
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Revive the Primary
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker container start pingora-guide-upstream_blue-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Status:&lt;/strong&gt; &lt;code&gt;Response from BLUE&lt;/code&gt; (Automatic recovery).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logs:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   [INFO] Backend ... becomes healthy
   [INFO] Primary (Blue) is healthy.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Lesson 34: Service Discovery (File-Based)
&lt;/h1&gt;

&lt;p&gt;In all previous lessons, we hardcoded IP addresses (e.g., &lt;code&gt;172.28.0.20:8080&lt;/code&gt;) directly into the Rust source code. This is fine for a lab, but in production, servers change IP addresses, scale up, or scale down constantly. Recompiling and restarting the proxy every time an IP changes is not acceptable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Service Discovery&lt;/strong&gt; allows Pingora to fetch the list of upstream servers from an external source (a file, an API, DNS, Consul, etc.) dynamically.&lt;/p&gt;

&lt;p&gt;In this lesson, we will implement &lt;strong&gt;File-Based Discovery&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Pingora will read &lt;code&gt;conf/upstreams.txt&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;It will populate the Load Balancer with the IPs found there.&lt;/li&gt;
&lt;li&gt;It will watch the file for changes. If you edit the file, Pingora updates its routing table &lt;strong&gt;instantly&lt;/strong&gt; without a restart.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Key Concepts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. The &lt;code&gt;ServiceDiscovery&lt;/code&gt; Trait
&lt;/h3&gt;

&lt;p&gt;This is the heart of dynamic routing in Pingora. It defines a single method: &lt;code&gt;discover()&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Input:&lt;/strong&gt; None.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Output:&lt;/strong&gt; A list of &lt;code&gt;Backend&lt;/code&gt; objects.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Behavior:&lt;/strong&gt; The Load Balancer calls this method periodically (e.g., every 1 second). If the list returned is different from the current list, the Load Balancer performs an atomic swap.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Hot Reloading (Atomic Swaps)
&lt;/h3&gt;

&lt;p&gt;When the discovery service returns a new list of backends, Pingora replaces the old list in memory.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Existing connections&lt;/strong&gt; continue to finish on the old servers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;New requests&lt;/strong&gt; immediately use the new list.&lt;/li&gt;
&lt;li&gt;This ensures &lt;strong&gt;Zero Downtime&lt;/strong&gt; during configuration changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Code (&lt;code&gt;examples/34_service_discovery_file.rs&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;Since Pingora provides the trait but leaves the specific implementation details flexible, we will implement a simple &lt;code&gt;FileDiscovery&lt;/code&gt; struct that reads a file and parses IP addresses line-by-line.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;services&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;background&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;background_service&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora_load_balancing&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;Backends&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LoadBalancer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Backend&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora_load_balancing&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;discovery&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ServiceDiscovery&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora_load_balancing&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;selection&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;RoundRobin&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// We need the internal SocketAddr enum to construct Backends manually&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;protocols&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;l4&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;SocketAddr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;path&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;PathBuf&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;collections&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;BTreeSet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HashMap&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;net&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ToSocketAddrs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;http&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Extensions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// 1. Define Custom Discovery Struct&lt;/span&gt;
&lt;span class="c1"&gt;// This holds the configuration path state.&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;FileDiscovery&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PathBuf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// 2. Implement the ServiceDiscovery Trait&lt;/span&gt;
&lt;span class="c1"&gt;// This is the contract Pingora uses to pull backend updates.&lt;/span&gt;
&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ServiceDiscovery&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;FileDiscovery&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;discover&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BTreeSet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Backend&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// A. Read the file&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;contents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;read_to_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nd"&gt;error!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to read upstream file: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;explain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ErrorType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;InternalError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="c1"&gt;// B. Parse Line-by-Line&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;upstreams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;BTreeSet&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;contents&lt;/span&gt;&lt;span class="nf"&gt;.lines&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="nf"&gt;.trim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="c1"&gt;// Skip empty lines and comments&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="nf"&gt;.is_empty&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="nf"&gt;.starts_with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="c1"&gt;// C. Resolve Address&lt;/span&gt;
            &lt;span class="c1"&gt;// Converts "1.2.3.4:80" into a SocketAddr&lt;/span&gt;
            &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="nf"&gt;.to_socket_addrs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;addrs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;addrs&lt;/span&gt;&lt;span class="nf"&gt;.next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;backend&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Backend&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                            &lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;SocketAddr&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Inet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                            &lt;span class="n"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                            &lt;span class="n"&gt;ext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;Extensions&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                        &lt;span class="p"&gt;};&lt;/span&gt;
                        &lt;span class="n"&gt;upstreams&lt;/span&gt;&lt;span class="nf"&gt;.insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;backend&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;error!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to parse address '{}': {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Return the new set of backends. &lt;/span&gt;
        &lt;span class="c1"&gt;// The second return value (HashMap) is for readiness/health overrides, which we leave empty.&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;HashMap&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nf"&gt;LB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LoadBalancer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RoundRobin&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ProxyHttp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;LB&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new_ctx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_peer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Selection: The LoadBalancer has the most recent list from the file&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;upstream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="na"&gt;.0&lt;/span&gt;
            &lt;span class="nf"&gt;.select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;b""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;.ok_or_else&lt;/span&gt;&lt;span class="p"&gt;(||&lt;/span&gt; &lt;span class="nn"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;explain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ErrorType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Custom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"NoUpstreamAvailable"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"Empty upstream pool"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Routed to upstream: {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;upstream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;upstream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"file-discovery.cluster"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;));&lt;/span&gt;

        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_request_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;upstream_request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;RequestHeader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;upstream_request&lt;/span&gt;&lt;span class="nf"&gt;.insert_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Host"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"file-discovery-cluster"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// 1. Initialize Custom Discovery&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;discovery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FileDiscovery&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;PathBuf&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"conf/upstreams.txt"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. Setup Backends and LoadBalancer&lt;/span&gt;
    &lt;span class="c1"&gt;// We wrap our discovery struct in the Backends container&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;backends&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Backends&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;discovery&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;upstreams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;LoadBalancer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_backends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;backends&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 3. Configure Update Frequency&lt;/span&gt;
    &lt;span class="c1"&gt;// Critical: This tells Pingora to call discover() every 1 second.&lt;/span&gt;
    &lt;span class="n"&gt;upstreams&lt;/span&gt;&lt;span class="py"&gt;.update_frequency&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_secs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="c1"&gt;// 4. Background Service&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;background&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;background_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"file_discovery"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;lb_ref&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;background&lt;/span&gt;&lt;span class="nf"&gt;.task&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;http_proxy_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;LB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lb_ref&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="nf"&gt;.add_tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:6178"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"File-Based Discovery LB running on 0.0.0.0:6178"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Watching file: conf/upstreams.txt"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;background&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;We will start with one server, verify traffic, then edit the file on the fly to switch servers.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Setup the Config File
&lt;/h3&gt;

&lt;p&gt;Create a &lt;code&gt;conf&lt;/code&gt; directory if it doesn't exist, and add the &lt;strong&gt;Blue&lt;/strong&gt; upstream.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; conf
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"172.28.0.20:8080"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; conf/upstreams.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Start the Proxy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 34_service_discovery_file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Start Traffic (Terminal 2)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do &lt;/span&gt;docker &lt;span class="nb"&gt;exec &lt;/span&gt;pingora_client_1 curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://172.28.0.10:6178/&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;sleep &lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Output:&lt;/strong&gt; &lt;code&gt;Response from BLUE&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logs:&lt;/strong&gt; &lt;code&gt;Routed to upstream: ... 172.28.0.20:8080&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Hot Reload (Terminal 3)
&lt;/h3&gt;

&lt;p&gt;While the curl loop is running, overwrite the file with the &lt;strong&gt;Green&lt;/strong&gt; upstream.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"172.28.0.21:8080"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; conf/upstreams.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Observe Results
&lt;/h3&gt;

&lt;p&gt;Within 1 second (our configured update frequency), you will see the traffic shift in the client terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;'Response from BLUE'
'Response from BLUE'
'Response from GREEN'  &amp;lt;-- Automatic Switch
'Response from GREEN'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And in the proxy logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[INFO] Routed to upstream: Backend { addr: Inet(172.28.0.20:8080)... }
[INFO] Routed to upstream: Backend { addr: Inet(172.28.0.21:8080)... }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This confirms that Pingora successfully discovered the new configuration and applied it dynamically.&lt;/p&gt;

&lt;h1&gt;
  
  
  Lesson 35: Dynamic Reconfiguration (Hot-Swap API)
&lt;/h1&gt;

&lt;p&gt;In the previous lesson, we used &lt;strong&gt;File-Based Discovery&lt;/strong&gt;, where Pingora "pulled" configuration changes by watching a file. While effective, modern cloud-native environments often prefer a &lt;strong&gt;"Push" model&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In this lesson, we will implement an &lt;strong&gt;Admin Control Plane&lt;/strong&gt;. We will run two separate services inside our Pingora process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Proxy Service (Port 6179):&lt;/strong&gt; Handles standard user traffic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Admin Service (Port 9090):&lt;/strong&gt; Listens for configuration commands.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We will share state between them using a thread-safe lock (&lt;code&gt;RwLock&lt;/code&gt;). When you send a POST request to the Admin Port, it updates the memory immediately, and the Proxy Service sees the change instantly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Concepts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Shared State (&lt;code&gt;Arc&amp;lt;RwLock&amp;lt;...&amp;gt;&amp;gt;&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;To make two independent services talk to each other, we create a shared "Source of Truth" for the upstream list.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Arc&lt;/code&gt;:&lt;/strong&gt; Allows multiple parts of the code to own the data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;RwLock&lt;/code&gt;:&lt;/strong&gt; Allows multiple readers (the Load Balancer) to access data simultaneously, but ensures exclusive access for the writer (the Admin API) during updates.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. The Admin Service
&lt;/h3&gt;

&lt;p&gt;This is a &lt;code&gt;BackgroundService&lt;/code&gt; that opens a TCP listener on port 9090. It parses incoming HTTP requests, extracts the new IP address from the body, and updates the shared state.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. In-Memory Discovery
&lt;/h3&gt;

&lt;p&gt;We implement a custom &lt;code&gt;ServiceDiscovery&lt;/code&gt; struct. Instead of reading a file or querying DNS, its &lt;code&gt;discover()&lt;/code&gt; method simply acquires a &lt;strong&gt;Read Lock&lt;/strong&gt; on the shared state and returns a copy of the current list.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Code (&lt;code&gt;examples/35_dynamic_reconfiguration.rs&lt;/code&gt;)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ShutdownWatch&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// We import BackgroundService to run our Admin API alongside the proxy&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;services&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;background&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;background_service&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BackgroundService&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora_load_balancing&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;Backends&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LoadBalancer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Backend&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora_load_balancing&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;discovery&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ServiceDiscovery&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora_load_balancing&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;selection&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;RoundRobin&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;protocols&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;l4&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;SocketAddr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;collections&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;BTreeSet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HashMap&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;net&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ToSocketAddrs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;RwLock&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;net&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;TcpListener&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;AsyncReadExt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AsyncWriteExt&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;http&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Extensions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// 1. Shared State Container&lt;/span&gt;
&lt;span class="c1"&gt;// This holds the "Source of Truth" for our upstreams.&lt;/span&gt;
&lt;span class="c1"&gt;// - Wrapped in Arc for shared ownership across threads/tasks.&lt;/span&gt;
&lt;span class="c1"&gt;// - Wrapped in RwLock for thread-safe access (Multiple Readers, One Writer).&lt;/span&gt;
&lt;span class="nd"&gt;#[derive(Clone)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;UpstreamState&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RwLock&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;BTreeSet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Backend&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// 2. In-Memory Discovery Strategy&lt;/span&gt;
&lt;span class="c1"&gt;// This struct implements the ServiceDiscovery trait required by the LoadBalancer.&lt;/span&gt;
&lt;span class="c1"&gt;// Instead of reading a file, it simply locks the shared memory and returns a copy.&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;InMemoryDiscovery&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;UpstreamState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ServiceDiscovery&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;InMemoryDiscovery&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;discover&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BTreeSet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Backend&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// READ LOCK: Fast and non-blocking for concurrent readers&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;guard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.state.upstreams&lt;/span&gt;&lt;span class="nf"&gt;.read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;guard&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nn"&gt;HashMap&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// 3. Admin API Service&lt;/span&gt;
&lt;span class="c1"&gt;// This runs as a generic background service. It binds to port 9090 and&lt;/span&gt;
&lt;span class="c1"&gt;// listens for configuration updates (POST requests).&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;AdminApiService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;UpstreamState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;BackgroundService&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;AdminApiService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;shutdown&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ShutdownWatch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;listener&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;TcpListener&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:9090"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to bind Admin Port 9090"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Admin API listening on 0.0.0.0:9090"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// We use tokio::select! to handle incoming connections OR shutdown signals simultaneously.&lt;/span&gt;
            &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;select!&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;shutdown&lt;/span&gt;&lt;span class="nf"&gt;.changed&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Admin API shutting down..."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;listener&lt;/span&gt;&lt;span class="nf"&gt;.accept&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                            &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Admin connection from {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.state&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

                            &lt;span class="c1"&gt;// Spawn a new lightweight task for each connection so we don't block the listener.&lt;/span&gt;
                            &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;move&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0u8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
                                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="nf"&gt;.read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                                    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                    &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Connection closed&lt;/span&gt;
                                &lt;span class="p"&gt;};&lt;/span&gt;

                                &lt;span class="c1"&gt;// Basic HTTP parsing (Demonstration purposes only)&lt;/span&gt;
                                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;req_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_utf8_lossy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

                                &lt;span class="c1"&gt;// Extract body (everything after the double newline)&lt;/span&gt;
                                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;req_str&lt;/span&gt;&lt;span class="nf"&gt;.find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\r\n\r\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                                    &lt;span class="n"&gt;req_str&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nf"&gt;.trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                                &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                                    &lt;span class="n"&gt;req_str&lt;/span&gt;&lt;span class="nf"&gt;.trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                                &lt;span class="p"&gt;};&lt;/span&gt;

                                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="nf"&gt;.is_empty&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                                    &lt;span class="c1"&gt;// Parse the IP address from the body&lt;/span&gt;
                                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;addrs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="nf"&gt;.to_socket_addrs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                                        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_addr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;addrs&lt;/span&gt;&lt;span class="nf"&gt;.next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                                            &lt;span class="c1"&gt;// WRITE LOCK: Exclusive access to update the config&lt;/span&gt;
                                            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;guard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="py"&gt;.upstreams&lt;/span&gt;&lt;span class="nf"&gt;.write&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                                            &lt;span class="n"&gt;guard&lt;/span&gt;&lt;span class="nf"&gt;.clear&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                                            &lt;span class="n"&gt;guard&lt;/span&gt;&lt;span class="nf"&gt;.insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Backend&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                                                &lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;SocketAddr&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Inet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_addr&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                                                &lt;span class="n"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                                &lt;span class="n"&gt;ext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;Extensions&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                                            &lt;span class="p"&gt;});&lt;/span&gt;
                                            &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Admin: Hot-swapped upstream to {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_addr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                                            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="nf"&gt;.write_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;b"HTTP/1.1 200 OK&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s"&gt;Content-Length: 2&lt;/span&gt;&lt;span class="se"&gt;\r\n\r\n&lt;/span&gt;&lt;span class="s"&gt;OK"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                                        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                                            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="nf"&gt;.write_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;b"HTTP/1.1 400 Bad Request&lt;/span&gt;&lt;span class="se"&gt;\r\n\r\n&lt;/span&gt;&lt;span class="s"&gt;Invalid IP"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                                        &lt;span class="p"&gt;}&lt;/span&gt;
                                    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                                        &lt;span class="nd"&gt;error!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Admin: Failed to parse IP: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                                        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="nf"&gt;.write_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;b"HTTP/1.1 400 Bad Request&lt;/span&gt;&lt;span class="se"&gt;\r\n\r\n&lt;/span&gt;&lt;span class="s"&gt;Parse Error"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                                    &lt;span class="p"&gt;}&lt;/span&gt;
                                &lt;span class="p"&gt;}&lt;/span&gt;
                            &lt;span class="p"&gt;});&lt;/span&gt;
                        &lt;span class="p"&gt;}&lt;/span&gt;
                        &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;error!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Admin accept error: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nf"&gt;LB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LoadBalancer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RoundRobin&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ProxyHttp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;LB&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new_ctx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_peer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Selection: Uses the InMemoryDiscovery logic implicitly&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;upstream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="na"&gt;.0&lt;/span&gt;
            &lt;span class="nf"&gt;.select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;b""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;.ok_or_else&lt;/span&gt;&lt;span class="p"&gt;(||&lt;/span&gt; &lt;span class="nn"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;explain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ErrorType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Custom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"NoUpstreamAvailable"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"Empty upstream pool"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Routed to upstream: {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;upstream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;upstream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"hot-swap.cluster"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;));&lt;/span&gt;

        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_request_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;upstream_request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;RequestHeader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;upstream_request&lt;/span&gt;&lt;span class="nf"&gt;.insert_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Host"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"hot-swap-cluster"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// 1. Initialize Shared State (Default: Blue)&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;initial_addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"172.28.0.20:8080"&lt;/span&gt;&lt;span class="nf"&gt;.to_socket_addrs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="nf"&gt;.next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;initial_set&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;BTreeSet&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;initial_set&lt;/span&gt;&lt;span class="nf"&gt;.insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Backend&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;SocketAddr&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Inet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;initial_addr&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;Extensions&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;UpstreamState&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;RwLock&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;initial_set&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. Setup Load Balancer with InMemoryDiscovery&lt;/span&gt;
    &lt;span class="c1"&gt;// Pass a clone of the state so the LB can read from it&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;discovery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;InMemoryDiscovery&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;backends&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Backends&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;discovery&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;upstreams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;LoadBalancer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_backends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;backends&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Update Frequency: Poll the shared memory state every 500ms&lt;/span&gt;
    &lt;span class="n"&gt;upstreams&lt;/span&gt;&lt;span class="py"&gt;.update_frequency&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_millis&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="c1"&gt;// 3. Register Services&lt;/span&gt;

    &lt;span class="c1"&gt;// A. LB Updater: Runs discover() periodically&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;lb_service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;background_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"lb_updater"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;lb_ref&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lb_service&lt;/span&gt;&lt;span class="nf"&gt;.task&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// B. Admin API: Listens on 9090 and updates state&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;admin_task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AdminApiService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;admin_service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;background_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"admin_api"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;admin_task&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// C. Proxy: Listens on 6179 and serves traffic&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;http_proxy_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;LB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lb_ref&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="nf"&gt;.add_tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:6179"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hot-Swap Proxy running on 0.0.0.0:6179"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Admin API running on 0.0.0.0:9090"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lb_service&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;admin_service&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;This verification requires us to act as both the &lt;strong&gt;End User&lt;/strong&gt; (calling the proxy) and the &lt;strong&gt;System Admin&lt;/strong&gt; (calling the admin port).&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Start the Server
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 35_dynamic_reconfiguration
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Logs:&lt;/strong&gt; &lt;code&gt;Admin API listening on 0.0.0.0:9090&lt;/code&gt; and &lt;code&gt;Hot-Swap Proxy running on 0.0.0.0:6179&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Generate Traffic (Terminal 2)
&lt;/h3&gt;

&lt;p&gt;The client will hit the proxy continuously.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do &lt;/span&gt;docker &lt;span class="nb"&gt;exec &lt;/span&gt;pingora_client_1 curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://172.28.0.10:6179/&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;sleep &lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Output:&lt;/strong&gt; &lt;code&gt;Response from BLUE&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logs:&lt;/strong&gt; &lt;code&gt;Routed to upstream: ... 172.28.0.20:8080&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Perform Hot-Swap (Terminal 1 or 3)
&lt;/h3&gt;

&lt;p&gt;We will now use &lt;code&gt;curl&lt;/code&gt; to send a POST request to the Admin API.&lt;br&gt;
&lt;strong&gt;Important:&lt;/strong&gt; This command must be run &lt;strong&gt;from the developer container&lt;/strong&gt; (where the server is running on localhost:9090), not from the client container.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# -d sends the new IP in the body&lt;/span&gt;
curl &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"172.28.0.21:8080"&lt;/span&gt; http://127.0.0.1:9090/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Observe Results
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Admin Logs:&lt;/strong&gt; &lt;code&gt;Admin: Hot-swapped upstream to 172.28.0.21:8080&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Traffic Output:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;'Response from BLUE'
'Response from GREEN'  &amp;lt;-- Instant switch
'Response from GREEN'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You have successfully built a control plane that allows you to reconfigure Pingora's routing logic in real-time without restarting the process.&lt;/p&gt;

&lt;h1&gt;
  
  
  Module 5: Traffic Control &amp;amp; Security
&lt;/h1&gt;

&lt;p&gt;We have built a proxy that routes, balances, and heals itself. But a production system must also &lt;strong&gt;protect&lt;/strong&gt; itself.&lt;/p&gt;

&lt;p&gt;A proxy is the gateway to your infrastructure. If it allows every request through unchecked, your backend services will be overwhelmed by traffic spikes, abusive bots, or malicious attackers.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Module 5&lt;/strong&gt;, we will implement the defensive layer of Pingora using the &lt;code&gt;pingora-limits&lt;/code&gt; crate and custom filters. We will explore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rate Limiting:&lt;/strong&gt; How to cap the number of requests a user can send per second (Fixed &amp;amp; Sliding Windows).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Concurrency Control:&lt;/strong&gt; How to limit the number of active connections to a fragile backend to prevent it from crashing under load.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security Filtering:&lt;/strong&gt; How to block IPs at the TCP level and reject requests that are too large.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authentication:&lt;/strong&gt; How to validate Bearer tokens and Basic Auth headers &lt;em&gt;at the edge&lt;/em&gt;, offloading this work from your microservices.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By the end of this module, your proxy will not just be a router; it will be a shield.&lt;/p&gt;

&lt;h1&gt;
  
  
  Lesson 36: Basic Rate Limiting (Fixed Window)
&lt;/h1&gt;

&lt;p&gt;A proxy isn't just a router; it's a bouncer. Without limits, a single abusive client or a buggy script can overwhelm your backend services.&lt;/p&gt;

&lt;p&gt;In this lesson, we implement a &lt;strong&gt;Fixed Window Rate Limiter&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Rule:&lt;/strong&gt; A client IP can send max &lt;strong&gt;5 requests&lt;/strong&gt; in a &lt;strong&gt;1-second window&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Consequence:&lt;/strong&gt; If they exceed this, the proxy rejects them immediately with &lt;code&gt;429 Too Many Requests&lt;/code&gt;. The request never touches the upstream server.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key Concepts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. The &lt;code&gt;request_filter&lt;/code&gt; Hook
&lt;/h3&gt;

&lt;p&gt;Up until now, we have mostly focused on &lt;code&gt;upstream_peer&lt;/code&gt;. However, for security logic, we need to intervene &lt;em&gt;before&lt;/em&gt; we even select a server.&lt;br&gt;
The &lt;code&gt;request_filter&lt;/code&gt; method in the &lt;code&gt;ProxyHttp&lt;/code&gt; trait allows us to inspect the request and make a go/no-go decision.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Return &lt;code&gt;Ok(false)&lt;/code&gt;:&lt;/strong&gt; "Everything looks good, proceed to upstream."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Return &lt;code&gt;Ok(true)&lt;/code&gt;:&lt;/strong&gt; "Stop here. I have handled the response (e.g., sent an error)."&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  2. The &lt;code&gt;pingora-limits&lt;/code&gt; Crate
&lt;/h3&gt;

&lt;p&gt;Pingora provides a high-performance counting library called &lt;code&gt;pingora-limits&lt;/code&gt;. It uses a "double-buffered" window implementation suitable for high-concurrency environments.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Rate&lt;/code&gt; Struct:&lt;/strong&gt; The central counter.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;observe(key, count)&lt;/code&gt;:&lt;/strong&gt; Increments the counter for a specific key (IP address) and returns the &lt;em&gt;current&lt;/em&gt; total for the active window.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  The Code (&lt;code&gt;examples/36_basic_rate_limit.rs&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;This code initializes a global rate limiter and checks every request's IP address against it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;once_cell&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Lazy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// Import the specific Rate struct from the limits crate&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora_limits&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;rate&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Rate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora_load_balancing&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;LoadBalancer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;selection&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;RoundRobin&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// 1. Global Rate Limiter State&lt;/span&gt;
&lt;span class="c1"&gt;// We use `Lazy` to initialize the rate limiter globally.&lt;/span&gt;
&lt;span class="c1"&gt;// The `Rate` struct manages the sliding windows internally.&lt;/span&gt;
&lt;span class="c1"&gt;// We set the window duration to 1 second.&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;RATE_LIMITER&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Lazy&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Rate&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Lazy&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(||&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nn"&gt;Rate&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_secs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// The Threshold: 5 requests per second per IP&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;MAX_REQ_PER_SEC&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;isize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nf"&gt;RateLimiterProxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LoadBalancer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RoundRobin&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ProxyHttp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;RateLimiterProxy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new_ctx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. The Upstream Selection (Standard Round Robin)&lt;/span&gt;
    &lt;span class="c1"&gt;// This only runs if request_filter returns Ok(false).&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_peer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;upstream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="na"&gt;.0&lt;/span&gt;
            &lt;span class="nf"&gt;.select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;b""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;.ok_or_else&lt;/span&gt;&lt;span class="p"&gt;(||&lt;/span&gt; &lt;span class="nn"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;explain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ErrorType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Custom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"NoUpstreamAvailable"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"Empty upstream pool"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Selected upstream: {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;upstream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;upstream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"rate-limited.cluster"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// 3. The Security Filter (The Core Logic)&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;request_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;where&lt;/span&gt;
        &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Send&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Sync&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// IP Extraction Logic:&lt;/span&gt;
        &lt;span class="c1"&gt;// Pingora's SocketAddr is an enum (Inet or Unix). We expect TCP connections here.&lt;/span&gt;
        &lt;span class="c1"&gt;// We unwrap the Inet variant to get a hashable IP address.&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;client_ip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="nf"&gt;.client_addr&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="nf"&gt;.as_inet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.ip&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="nb"&gt;None&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nd"&gt;warn!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Could not determine client IP, allowing request."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="c1"&gt;// 4. Observe and Check&lt;/span&gt;
        &lt;span class="c1"&gt;// .observe() increments the counter for this IP and returns the *current* total.&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;curr_req_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RATE_LIMITER&lt;/span&gt;&lt;span class="nf"&gt;.observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;client_ip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// 5. Enforcement&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;curr_req_count&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;MAX_REQ_PER_SEC&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nd"&gt;warn!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Rate Limit Exceeded for {}: {} req/s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;client_ip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;curr_req_count&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// Return standard HTTP 429 response&lt;/span&gt;
            &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="nf"&gt;.respond_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;429&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="c1"&gt;// Critical: Return `true` to tell Pingora to STOP processing this request.&lt;/span&gt;
            &lt;span class="c1"&gt;// The request will NOT be forwarded to the upstream_peer.&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Return `false` to continue normal processing&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Standard Load Balancer setup (Blue/Green)&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;upstreams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;LoadBalancer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;try_from_iter&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="s"&gt;"172.28.0.20:8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"172.28.0.21:8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;http_proxy_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;RateLimiterProxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
    &lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="nf"&gt;.add_tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:6180"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Rate Limiter Proxy running on 0.0.0.0:6180"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Limit: {} requests per second per IP"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MAX_REQ_PER_SEC&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;We will confirm that the first 5 requests pass, and subsequent requests fail immediately.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Start the Proxy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 36_basic_rate_limit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Burst Traffic Test
&lt;/h3&gt;

&lt;p&gt;Run this loop in your client terminal to fire 10 requests rapidly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; pingora_client_1 bash &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'for i in {1..10}; do curl -s -o /dev/null -w "%{http_code}\n" http://172.28.0.10:6180/; done'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Analyze Output
&lt;/h3&gt;

&lt;p&gt;You will see a clean cutoff:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;200  &amp;lt;- 1 (Allowed)
200  &amp;lt;- 2 (Allowed)
200  &amp;lt;- 3 (Allowed)
200  &amp;lt;- 4 (Allowed)
200  &amp;lt;- 5 (Allowed, bucket full)
429  &amp;lt;- 6 (Blocked)
429  &amp;lt;- 7 (Blocked)
429  &amp;lt;- 8 (Blocked)
429  &amp;lt;- 9 (Blocked)
429  &amp;lt;- 10 (Blocked)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Check Proxy Logs
&lt;/h3&gt;

&lt;p&gt;The server logs confirm the interception logic works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[WARN] Rate Limit Exceeded for 172.28.0.30: 6 req/s
[WARN] Rate Limit Exceeded for 172.28.0.30: 7 req/s
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice there are &lt;strong&gt;no&lt;/strong&gt; &lt;code&gt;Selected upstream&lt;/code&gt; logs for requests 6–10. The &lt;code&gt;request_filter&lt;/code&gt; successfully short-circuited the pipeline.&lt;/p&gt;

&lt;h1&gt;
  
  
  Lesson 37: Sliding Window Rate Limiting
&lt;/h1&gt;

&lt;p&gt;The &lt;strong&gt;Fixed Window&lt;/strong&gt; approach (Lesson 36) has a flaw known as the "boundary burst." If the window resets at 1.0s, a user could send 5 requests at 0.9s and 5 requests at 1.1s. In a 0.2s span, they sent 10 requests, effectively doubling the allowed rate, yet the limiter sees two separate valid windows.&lt;/p&gt;

&lt;p&gt;To solve this, we use a &lt;strong&gt;Sliding Window&lt;/strong&gt; (smoothed) limiter. This algorithm calculates the request rate as a weighted average over time, preventing boundary gaming and allowing for more "elastic" traffic handling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Concepts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. &lt;code&gt;observe()&lt;/code&gt; vs &lt;code&gt;rate()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;When using Pingora's &lt;code&gt;pingora_limits::rate::Rate&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;observe(key, count)&lt;/code&gt;:&lt;/strong&gt; This acts as the "writer." It increments the counter for the current time bucket. You &lt;strong&gt;must&lt;/strong&gt; call this for every request.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;rate(key)&lt;/code&gt;:&lt;/strong&gt; This acts as the "reader." It returns an &lt;code&gt;f64&lt;/code&gt; representing the smoothed requests per second. It looks at both the current bucket and the previous bucket to interpolate the actual velocity of traffic.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. The Logic Flow
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Request arrives.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Increment:&lt;/strong&gt; &lt;code&gt;observe(&amp;amp;ip, 1)&lt;/code&gt; (Record the event).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Calculate:&lt;/strong&gt; &lt;code&gt;rate(&amp;amp;ip)&lt;/code&gt; (Check the speed).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decide:&lt;/strong&gt; If &lt;code&gt;rate &amp;gt; 5.0&lt;/code&gt;, block.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Code (&lt;code&gt;examples/37_sliding_window.rs&lt;/code&gt;)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;once_cell&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Lazy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora_limits&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;rate&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Rate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora_load_balancing&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;LoadBalancer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;selection&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;RoundRobin&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// 1. Global Rate Limiter State&lt;/span&gt;
&lt;span class="c1"&gt;// The Rate struct manages the sliding window logic (Red/Blue slots) internally.&lt;/span&gt;
&lt;span class="c1"&gt;// A 1-second window allows us to calculate "Requests Per Second".&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;RATE_LIMITER&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Lazy&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Rate&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Lazy&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(||&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nn"&gt;Rate&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_secs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Threshold: 5.0 requests per second (Smoothed)&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;MAX_REQ_PER_SEC&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;f64&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;5.0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nf"&gt;SlidingWindowProxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LoadBalancer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RoundRobin&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ProxyHttp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;SlidingWindowProxy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new_ctx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. The Security Filter&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;request_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;where&lt;/span&gt;
        &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Send&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Sync&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// IP Extraction Logic (Unwrap Inet address)&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;client_ip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="nf"&gt;.client_addr&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="nf"&gt;.as_inet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.ip&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="nb"&gt;None&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nd"&gt;warn!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Could not determine client IP, allowing request."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="c1"&gt;// 3. Observe &lt;/span&gt;
        &lt;span class="c1"&gt;// Vital: We must increment the counter first.&lt;/span&gt;
        &lt;span class="c1"&gt;// .observe() returns the raw count (isize), but we ignore it here.&lt;/span&gt;
        &lt;span class="n"&gt;RATE_LIMITER&lt;/span&gt;&lt;span class="nf"&gt;.observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;client_ip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// 4. Check Rate&lt;/span&gt;
        &lt;span class="c1"&gt;// .rate() returns the smoothed requests per second as an f64.&lt;/span&gt;
        &lt;span class="c1"&gt;// This handles the interpolation between previous and current windows.&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;current_rate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RATE_LIMITER&lt;/span&gt;&lt;span class="nf"&gt;.rate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;client_ip&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// 5. Enforcement&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;current_rate&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;MAX_REQ_PER_SEC&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nd"&gt;warn!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Rate Limit Exceeded for {}: {:.2} req/s (Limit: {:.1})"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                  &lt;span class="n"&gt;client_ip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;current_rate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MAX_REQ_PER_SEC&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="nf"&gt;.respond_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;429&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Short-circuit&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_peer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;upstream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="na"&gt;.0&lt;/span&gt;
            &lt;span class="nf"&gt;.select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;b""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;.ok_or_else&lt;/span&gt;&lt;span class="p"&gt;(||&lt;/span&gt; &lt;span class="nn"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;explain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ErrorType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Custom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"NoUpstreamAvailable"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"Empty upstream pool"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;upstream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"sliding-window.cluster"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;upstreams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;LoadBalancer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;try_from_iter&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="s"&gt;"172.28.0.20:8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"172.28.0.21:8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;http_proxy_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;SlidingWindowProxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
    &lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="nf"&gt;.add_tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:6181"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Sliding Window Rate Limiter running on 0.0.0.0:6181"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Limit: {:.1} requests per second (Smoothed)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MAX_REQ_PER_SEC&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;To verify the "smoothing" effect, we will run two tests: one exactly at the limit, and one slightly over.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Start the Proxy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 37_sliding_window
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Test A: Respecting the Limit (0.2s interval)
&lt;/h3&gt;

&lt;p&gt;Requests sent every 0.20s equal exactly 5 req/s. The smoothed limiter should allow this indefinitely.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; pingora_client_1 bash &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'for i in {1..15}; do curl -s -o /dev/null -w "%{http_code}\n" http://172.28.0.10:6181/; sleep 0.20; done'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Result:&lt;/strong&gt; 15 &lt;code&gt;200 OK&lt;/code&gt; responses.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Test B: Overload (0.15s interval)
&lt;/h3&gt;

&lt;p&gt;Requests sent every 0.15s equal ~6.6 req/s.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; pingora_client_1 bash &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'for i in {1..15}; do curl -s -o /dev/null -w "%{http_code}\n" http://172.28.0.10:6181/; sleep 0.15; done'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Result:&lt;/strong&gt; The first ~7 requests pass (burst allowance), but then the moving average crosses 5.0, and the rest return &lt;code&gt;429&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logs:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[WARN] Rate Limit Exceeded for 172.28.0.30: 7.00 req/s (Limit: 5.0)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This confirms the limiter is successfully calculating velocity rather than just counting bucket hits.&lt;/p&gt;

&lt;h1&gt;
  
  
  Lesson 38: In-flight Concurrency Limiting
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Rate Limiting&lt;/strong&gt; (Lessons 36-37) controls &lt;em&gt;how often&lt;/em&gt; a user can knock on your door.&lt;br&gt;
&lt;strong&gt;In-flight Limiting&lt;/strong&gt; controls &lt;em&gt;how many&lt;/em&gt; people can be inside the house at once.&lt;/p&gt;

&lt;p&gt;This distinction is vital. A heavy API endpoint (e.g., "Generate PDF Report") might take 5 seconds to process. Even if a user only sends 1 request every 5 seconds (low rate), if 100 users do it simultaneously, you have 100 active threads, which could crash your database.&lt;/p&gt;

&lt;p&gt;In this lesson, we implement a &lt;strong&gt;Concurrency Limiter&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rule:&lt;/strong&gt; Max &lt;strong&gt;2 simultaneous requests&lt;/strong&gt; allowed globally.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Result:&lt;/strong&gt; The 3rd simultaneous request gets rejected immediately with &lt;code&gt;429 Too Many Requests&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Key Concepts
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. The &lt;code&gt;Inflight&lt;/code&gt; Struct and &lt;code&gt;Guard&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;pingora-limits&lt;/code&gt; crate uses a semaphore-like pattern.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;incr(key)&lt;/code&gt;: Increments the counter and returns a &lt;code&gt;Guard&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Guard&lt;/code&gt;: This is a "ticket." As long as this object exists in memory, the "slot" is occupied.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic Cleanup:&lt;/strong&gt; When the &lt;code&gt;Guard&lt;/code&gt; is dropped (goes out of scope), the counter is automatically decremented.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  2. The Context (&lt;code&gt;CTX&lt;/code&gt;) Bridge
&lt;/h3&gt;

&lt;p&gt;Since a request takes time to complete, we need to hold onto the &lt;code&gt;Guard&lt;/code&gt; from the beginning of the request until the end.&lt;br&gt;
We define a custom Context struct (&lt;code&gt;InflightCtx&lt;/code&gt;) to store this guard.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Request Start (&lt;code&gt;request_filter&lt;/code&gt;):&lt;/strong&gt; We acquire the guard and move it into &lt;code&gt;ctx.guard&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Request End:&lt;/strong&gt; Pingora drops the &lt;code&gt;ctx&lt;/code&gt;, which drops the &lt;code&gt;guard&lt;/code&gt;, which frees the slot.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  The Code (&lt;code&gt;examples/38_inflight_limit.rs&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;We intentionally add a &lt;strong&gt;2-second sleep&lt;/strong&gt; in &lt;code&gt;upstream_peer&lt;/code&gt; to simulate a slow backend. This ensures requests overlap in time so we can verify the limit.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;once_cell&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Lazy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora_limits&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;inflight&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;Inflight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Guard&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora_load_balancing&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;LoadBalancer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;selection&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;RoundRobin&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// 1. Global In-flight Limiter State&lt;/span&gt;
&lt;span class="c1"&gt;// Counts active connections. Does not rely on time windows.&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;INFLIGHT_LIMITER&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Lazy&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Inflight&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Lazy&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(||&lt;/span&gt; &lt;span class="nn"&gt;Inflight&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="c1"&gt;// Limit: Max 2 simultaneous requests&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;MAX_CONCURRENT_REQ&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;isize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// 2. The Context&lt;/span&gt;
&lt;span class="c1"&gt;// This struct holds the "ticket" (Guard) for the duration of the request.&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;InflightCtx&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;guard&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Guard&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nf"&gt;InflightLimitProxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LoadBalancer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RoundRobin&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ProxyHttp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;InflightLimitProxy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;InflightCtx&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new_ctx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;InflightCtx&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;guard&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// 3. The Security Filter&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;request_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;where&lt;/span&gt;
        &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Send&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Sync&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Define the scope of the limit (Global vs Per-IP)&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"global_limit"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// Increment the counter. Returns the guard and the *new* count.&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;guard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;INFLIGHT_LIMITER&lt;/span&gt;&lt;span class="nf"&gt;.incr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// 4. Check Limit&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;MAX_CONCURRENT_REQ&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nd"&gt;warn!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"In-flight Limit Exceeded {}/{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MAX_CONCURRENT_REQ&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="nf"&gt;.respond_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;429&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="c1"&gt;// CRITICAL: We do NOT put the guard into 'ctx'.&lt;/span&gt;
            &lt;span class="c1"&gt;// When this function returns, 'guard' goes out of scope here.&lt;/span&gt;
            &lt;span class="c1"&gt;// Rust calls drop(guard), which decrements the counter immediately.&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// 5. Acquire Slot&lt;/span&gt;
        &lt;span class="c1"&gt;// Move the guard into the context. It will stay there until the &lt;/span&gt;
        &lt;span class="c1"&gt;// request completes (success or failure).&lt;/span&gt;
        &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.guard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;guard&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_peer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;upstream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="na"&gt;.0&lt;/span&gt;
            &lt;span class="nf"&gt;.select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;b""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;.ok_or_else&lt;/span&gt;&lt;span class="p"&gt;(||&lt;/span&gt; &lt;span class="nn"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;explain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ErrorType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Custom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"NoUpstreamAvailable"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"Empty upstream pool"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// SIMULATION: Artificial 2s delay.&lt;/span&gt;
        &lt;span class="c1"&gt;// This forces requests to overlap in time, triggering the concurrency limit.&lt;/span&gt;
        &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_secs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;upstream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"inflight.cluster"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;upstreams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;LoadBalancer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;try_from_iter&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="s"&gt;"172.28.0.20:8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"172.28.0.21:8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;http_proxy_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;InflightLimitProxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
    &lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="nf"&gt;.add_tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:6182"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"In-flight Limiter running on 0.0.0.0:6182"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Max Concurrent Requests: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MAX_CONCURRENT_REQ&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Note: Upstream connection has artificial 2s delay to simulate load."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;To verify this, we need to send requests faster than the server can finish them (which is easy, because the server takes 2 seconds).&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Start the Proxy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 38_inflight_limit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Run Parallel Requests
&lt;/h3&gt;

&lt;p&gt;We use a bash loop to fire 5 requests simultaneously into the background (&lt;code&gt;&amp;amp;&lt;/code&gt;) and then wait for them all to finish.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; pingora_client_1 bash &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'for i in {1..5}; do curl -s -o /dev/null -w "%{http_code}\n" http://172.28.0.10:6182/ &amp;amp; done; wait'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Analyze Output
&lt;/h3&gt;

&lt;p&gt;You should see a mix of &lt;code&gt;200&lt;/code&gt; and &lt;code&gt;429&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;429
429
429
200
200
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;200s:&lt;/strong&gt; Two requests grabbed the slots and slept for 2 seconds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;429s:&lt;/strong&gt; Three requests arrived, saw the limit &lt;code&gt;3/2&lt;/code&gt; (or &lt;code&gt;4/2&lt;/code&gt;, &lt;code&gt;5/2&lt;/code&gt;), and were rejected instantly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Check Logs
&lt;/h3&gt;

&lt;p&gt;The logs confirm the rejection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[WARN] In-flight Limit Exceeded 3/2
[WARN] In-flight Limit Exceeded 3/2
[WARN] In-flight Limit Exceeded 3/2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This proves the proxy is actively protecting your backend from concurrency overload.&lt;/p&gt;

&lt;h1&gt;
  
  
  Lesson 39: IP Filtering (Access Control)
&lt;/h1&gt;

&lt;p&gt;In network security, one of the most basic but effective defenses is the Access Control List (ACL). You simply maintain a list of who is allowed in and who is not.&lt;/p&gt;

&lt;p&gt;While specialized firewalls (like &lt;code&gt;iptables&lt;/code&gt; or AWS Security Groups) often handle this, doing it at the proxy level gives you finer control. You can log the attempts, return custom error pages, or dynamically update the blocklist without touching the OS kernel.&lt;/p&gt;

&lt;p&gt;In this lesson, we implement a simple &lt;strong&gt;IP Blocklist&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Client 1 (&lt;code&gt;172.28.0.30&lt;/code&gt;):&lt;/strong&gt; Allowed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client 2 (&lt;code&gt;172.28.0.31&lt;/code&gt;):&lt;/strong&gt; Blocked.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key Concepts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. &lt;code&gt;request_filter&lt;/code&gt; vs. &lt;code&gt;ConnectionFilter&lt;/code&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;request_filter&lt;/code&gt; (Layer 7):&lt;/strong&gt; This runs after the TCP handshake and TLS termination. We have access to the full HTTP request.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Pros:&lt;/em&gt; Can return a polite &lt;code&gt;403 Forbidden&lt;/code&gt; HTML page. Can log detailed metadata (User-Agent, Path).&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Cons:&lt;/em&gt; Slightly more resource-intensive as we parse the request first.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Usage:&lt;/em&gt; We use this today.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;ConnectionFilter&lt;/code&gt; (Layer 4):&lt;/strong&gt; This runs during the TCP handshake.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Pros:&lt;/em&gt; extremely fast. Drops the packet instantly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Cons:&lt;/em&gt; The user sees a "Connection Reset" error, not a helpful HTTP message.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; While effective, the Layer 4 API in Pingora is advanced/experimental, so we will focus on Layer 7 filtering which is the standard for application proxies.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Implementation Logic
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Extract IP:&lt;/strong&gt; Get the IP from the session.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Match:&lt;/strong&gt; Check against our "Bad Actor" list.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reject:&lt;/strong&gt; If matched, send &lt;code&gt;403&lt;/code&gt; and return &lt;code&gt;Ok(true)&lt;/code&gt; to stop processing.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Code (&lt;code&gt;examples/39_connection_filter.rs&lt;/code&gt;)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora_load_balancing&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;LoadBalancer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;selection&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;RoundRobin&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nf"&gt;FirewallProxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LoadBalancer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RoundRobin&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ProxyHttp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;FirewallProxy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new_ctx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="c1"&gt;// 1. The Access Control Logic&lt;/span&gt;
    &lt;span class="c1"&gt;// This hook runs immediately after the request headers are parsed.&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;request_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;where&lt;/span&gt;
        &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Send&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Sync&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Extract Client IP&lt;/span&gt;
        &lt;span class="c1"&gt;// We unwrap the Inet address to get a comparable IP object.&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;client_ip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="nf"&gt;.client_addr&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="nf"&gt;.as_inet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.ip&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="nb"&gt;None&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nd"&gt;warn!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Unknown client address, allowing..."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="c1"&gt;// 2. Define Blocklist&lt;/span&gt;
        &lt;span class="c1"&gt;// In a production scenario, this would likely be an optimized&lt;/span&gt;
        &lt;span class="c1"&gt;// Set (HashSet) or an IP CIDR matcher (IpNet).&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;bad_actor_ip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"172.28.0.31"&lt;/span&gt;&lt;span class="py"&gt;.parse&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;net&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;IpAddr&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// 3. Check and Block&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;client_ip&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;bad_actor_ip&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nd"&gt;warn!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Access Denied for IP: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;client_ip&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// Return 403 Forbidden&lt;/span&gt;
            &lt;span class="c1"&gt;// This sends a standard error page to the client.&lt;/span&gt;
            &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="nf"&gt;.respond_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="c1"&gt;// Short-circuit: Return `true` to signal that the request &lt;/span&gt;
            &lt;span class="c1"&gt;// has been fully handled and should NOT proceed to the upstream.&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Allow legitimate traffic&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_peer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;upstream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="na"&gt;.0&lt;/span&gt;
            &lt;span class="nf"&gt;.select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;b""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;.ok_or_else&lt;/span&gt;&lt;span class="p"&gt;(||&lt;/span&gt; &lt;span class="nn"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;explain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ErrorType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Custom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"NoUpstreamAvailable"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"Empty upstream pool"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;upstream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"firewall.cluster"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;upstreams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;LoadBalancer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;try_from_iter&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="s"&gt;"172.28.0.20:8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"172.28.0.21:8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;http_proxy_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;FirewallProxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
    &lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="nf"&gt;.add_tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:6183"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Firewall Proxy running on 0.0.0.0:6183"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Blocking Bad Actor: 172.28.0.31"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;We will confirm that Client 1 is allowed and Client 2 is blocked.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Start the Proxy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 39_connection_filter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Test Client 1 (Allowed)
&lt;/h3&gt;

&lt;p&gt;Run this command from your host machine (assuming the docker setup):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; pingora_client_1 curl &lt;span class="nt"&gt;-v&lt;/span&gt; http://172.28.0.10:6183/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Result:&lt;/strong&gt; &lt;code&gt;HTTP/1.1 200 OK&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Log:&lt;/strong&gt; No warnings. Normal routing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Test Client 2 (Blocked)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; pingora_client_2 curl &lt;span class="nt"&gt;-v&lt;/span&gt; http://172.28.0.10:6183/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Result:&lt;/strong&gt; &lt;code&gt;HTTP/1.1 403 Forbidden&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Log:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   [WARN] Access Denied for IP: 172.28.0.31
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This confirms the IP filter is actively protecting your service.&lt;/p&gt;

&lt;h1&gt;
  
  
  Lesson 40: Request Size Limiting
&lt;/h1&gt;

&lt;p&gt;Handling large file uploads is resource-intensive. If a client starts uploading a 10GB video to an endpoint meant for 1KB JSON payloads, your server waits, buffers, and eventually crashes or runs out of bandwidth.&lt;/p&gt;

&lt;p&gt;Pingora allows us to enforce limits at the edge. While &lt;code&gt;pingora-web&lt;/code&gt; (the higher-level framework) has global settings for this, implementing it manually in &lt;code&gt;pingora-proxy&lt;/code&gt; gives us fine-grained control—for example, allowing 500MB on &lt;code&gt;/upload&lt;/code&gt; but only 2KB on &lt;code&gt;/login&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In this lesson, we implement a &lt;strong&gt;Dual-Layer Defense&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Fast Path:&lt;/strong&gt; Check the &lt;code&gt;Content-Length&lt;/code&gt; header. If it says "1GB", reject immediately.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Slow Path:&lt;/strong&gt; If the client lies or uses &lt;code&gt;Transfer-Encoding: chunked&lt;/code&gt; (no total size declared), we count the bytes as they stream in and cut the connection if they exceed the limit.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Key Concepts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. &lt;code&gt;request_body_filter&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This is a new hook in the &lt;code&gt;ProxyHttp&lt;/code&gt; trait. It runs &lt;em&gt;for every chunk&lt;/em&gt; of data received from the client.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Input:&lt;/strong&gt; &lt;code&gt;body: &amp;amp;mut Option&amp;lt;Bytes&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Action:&lt;/strong&gt; We inspect the size, increment a counter in our &lt;code&gt;CTX&lt;/code&gt;, and decide whether to allow it or return an error.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. &lt;code&gt;fail_to_proxy&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;If &lt;code&gt;request_body_filter&lt;/code&gt; returns an error, Pingora aborts the upstream connection. However, by default, it might just close the socket or send a 502. To send a proper &lt;code&gt;413 Payload Too Large&lt;/code&gt; to the client, we must implement the &lt;code&gt;fail_to_proxy&lt;/code&gt; hook to catch our specific error and format the response.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Code (&lt;code&gt;examples/40_request_size_limit.rs&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;We set an artificially low limit of &lt;strong&gt;100 bytes&lt;/strong&gt; to make testing easy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Bytes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;FailToProxy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;http&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;header&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CONTENT_LENGTH&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora_load_balancing&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;LoadBalancer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;selection&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;RoundRobin&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Max 100 bytes (Artificially low for testing)&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;MAX_BODY_SIZE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;usize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// 1. Context to track streaming body size&lt;/span&gt;
&lt;span class="c1"&gt;// This persists across multiple calls to request_body_filter&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;SizeCtx&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;bytes_read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nf"&gt;SizeLimitProxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LoadBalancer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RoundRobin&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ProxyHttp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;SizeLimitProxy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SizeCtx&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new_ctx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;SizeCtx&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;bytes_read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. Filter 1: Check Content-Length Header&lt;/span&gt;
    &lt;span class="c1"&gt;// This catches large requests EARLY, before we accept the body payload.&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;request_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;where&lt;/span&gt;
        &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Send&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Sync&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="nf"&gt;.req_header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="py"&gt;.headers&lt;/span&gt;&lt;span class="nf"&gt;.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CONTENT_LENGTH&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;len_str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="nf"&gt;.to_str&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;len_str&lt;/span&gt;&lt;span class="py"&gt;.parse&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;usize&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;MAX_BODY_SIZE&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="nd"&gt;warn!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Rejecting request by Header: Content-Length: {} &amp;gt; {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MAX_BODY_SIZE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                        &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="nf"&gt;.respond_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;413&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Short-circuit&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// 3. Filter 2: Check Streaming Body&lt;/span&gt;
    &lt;span class="c1"&gt;// This catches "Transfer-Encoding: chunked" or lying Content-Length headers.&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;request_body_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Bytes&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_end_of_stream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;where&lt;/span&gt;
        &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Send&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Sync&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.bytes_read&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.bytes_read&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;MAX_BODY_SIZE&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nd"&gt;warn!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Rejecting request by Stream: Accumulated {} bytes &amp;gt; {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.bytes_read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MAX_BODY_SIZE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="c1"&gt;// We return a Custom error here. Standard errors would cause a 502.&lt;/span&gt;
                &lt;span class="c1"&gt;// We need `fail_to_proxy` to convert this into a 413.&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;explain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ErrorType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Custom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"BodyTooLarge"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"Stream exceeded limit"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_peer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;upstream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="na"&gt;.0&lt;/span&gt;
            &lt;span class="nf"&gt;.select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;b""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;.ok_or_else&lt;/span&gt;&lt;span class="p"&gt;(||&lt;/span&gt; &lt;span class="nn"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;explain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ErrorType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Custom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"NoUpstreamAvailable"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"Empty upstream pool"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;upstream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"size-limit.cluster"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// 4. Handle Streaming Errors&lt;/span&gt;
    &lt;span class="c1"&gt;// If request_body_filter raises an error, it ends up here.&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;fail_to_proxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;FailToProxy&lt;/span&gt;
    &lt;span class="k"&gt;where&lt;/span&gt;
        &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Send&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Sync&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Convert our custom error into a proper HTTP 413 response&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nn"&gt;ErrorType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Custom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"BodyTooLarge"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="py"&gt;.etype&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="nf"&gt;.respond_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;413&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;FailToProxy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;error_code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;413&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;can_reuse_downstream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;FailToProxy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;error_code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;502&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;can_reuse_downstream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;upstreams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;LoadBalancer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;try_from_iter&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="s"&gt;"172.28.0.20:8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"172.28.0.21:8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;http_proxy_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;SizeLimitProxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
    &lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="nf"&gt;.add_tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:6184"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Request Size Limiter running on 0.0.0.0:6184"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Max Body Size: {} bytes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MAX_BODY_SIZE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;We will verify both the fast header check and the slow streaming check.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Start the Proxy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 40_request_size_limit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Valid Request
&lt;/h3&gt;

&lt;p&gt;Send "tiny" (4 bytes).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"tiny"&lt;/span&gt; http://172.28.0.10:6184/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Result:&lt;/strong&gt; &lt;code&gt;200 OK&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Header Rejection (Fast Path)
&lt;/h3&gt;

&lt;p&gt;We create a ~150 byte payload. &lt;code&gt;curl&lt;/code&gt; automatically adds the &lt;code&gt;Content-Length: 150&lt;/code&gt; header.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Generate 150 'a' characters&lt;/span&gt;
&lt;span class="nv"&gt;payload&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'a%.0s'&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;1..150&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
curl &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$payload&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; http://172.28.0.10:6184/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Result:&lt;/strong&gt; &lt;code&gt;413 Payload Too Large&lt;/code&gt; (Immediate).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logs:&lt;/strong&gt; &lt;code&gt;Rejecting request by Header: Content-Length: 150 &amp;gt; 100&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Streaming Rejection (Slow Path)
&lt;/h3&gt;

&lt;p&gt;We force &lt;code&gt;chunked&lt;/code&gt; encoding so &lt;code&gt;curl&lt;/code&gt; does &lt;em&gt;not&lt;/em&gt; send a &lt;code&gt;Content-Length&lt;/code&gt;. Pingora must read the body to find the size.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Transfer-Encoding: chunked"&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$payload&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; http://172.28.0.10:6184/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Result:&lt;/strong&gt; &lt;code&gt;413 Payload Too Large&lt;/code&gt; (After uploading).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logs:&lt;/strong&gt; &lt;code&gt;Rejecting request by Stream: Accumulated 150 bytes &amp;gt; 100&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This proves your proxy cannot be fooled by omitting the length header.&lt;/p&gt;

&lt;h1&gt;
  
  
  Lesson 41: Authentication (Bearer Token)
&lt;/h1&gt;

&lt;p&gt;One of the most powerful patterns in modern architecture is &lt;strong&gt;Authentication Offloading&lt;/strong&gt; (or "Auth at the Edge").&lt;/p&gt;

&lt;p&gt;Instead of requiring every single microservice to implement token validation, logic handling, and error formatting, you enforce it once at the Proxy/Gateway level. If a request reaches your upstream service, that service can assume the user is already authenticated.&lt;/p&gt;

&lt;p&gt;In this lesson, we implement a simple API Gateway authentication check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No Header?&lt;/strong&gt; -&amp;gt; &lt;code&gt;401 Unauthorized&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wrong Token?&lt;/strong&gt; -&amp;gt; &lt;code&gt;403 Forbidden&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Correct Token?&lt;/strong&gt; -&amp;gt; Allowed (&lt;code&gt;200 OK&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key Concepts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. HTTP 401 vs 403
&lt;/h3&gt;

&lt;p&gt;It is important to be semantically correct with HTTP errors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;401 Unauthorized:&lt;/strong&gt; The user has not provided credentials. The client should prompt the user to log in.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;403 Forbidden:&lt;/strong&gt; The user provided credentials, but they are invalid or insufficient. Re-trying the same credentials will not work.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Header Inspection
&lt;/h3&gt;

&lt;p&gt;We use &lt;code&gt;session.req_header().headers.get("Authorization")&lt;/code&gt; to access the raw header value.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Performance Tip:&lt;/strong&gt; We compare the value as &lt;strong&gt;bytes&lt;/strong&gt; (&lt;code&gt;as_bytes()&lt;/code&gt;) rather than converting it to a Rust &lt;code&gt;String&lt;/code&gt; (&lt;code&gt;to_str()&lt;/code&gt;). This avoids unnecessary memory allocation and UTF-8 validation overhead, which is crucial for high-throughput gateways.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Code (&lt;code&gt;examples/41_auth_request.rs&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;We enforce a hardcoded token &lt;code&gt;Bearer super-secret-token&lt;/code&gt;. In a real app, you might validate a JWT signature or query a Redis cache here.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora_load_balancing&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;LoadBalancer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;selection&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;RoundRobin&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// The shared secret (Hardcoded for this lesson)&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;SECRET_TOKEN&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;b"Bearer super-secret-token"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nf"&gt;AuthProxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LoadBalancer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RoundRobin&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ProxyHttp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;AuthProxy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new_ctx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="c1"&gt;// 1. Authentication Logic&lt;/span&gt;
    &lt;span class="c1"&gt;// Runs before upstream connection.&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;request_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;where&lt;/span&gt;
        &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Send&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Sync&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Extract Authorization Header&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;auth_header&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="nf"&gt;.req_header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="py"&gt;.headers&lt;/span&gt;&lt;span class="nf"&gt;.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Authorization"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;auth_header&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Case A: Header Missing -&amp;gt; 401 Unauthorized&lt;/span&gt;
            &lt;span class="nb"&gt;None&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nd"&gt;warn!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Auth Failed: Missing Authorization header"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="nf"&gt;.respond_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Stop processing&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="c1"&gt;// Case B: Header Present -&amp;gt; Check Token&lt;/span&gt;
            &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// Direct byte comparison (fast)&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="nf"&gt;.as_bytes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;SECRET_TOKEN&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nd"&gt;warn!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Auth Failed: Invalid Token"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="nf"&gt;.respond_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Stop processing&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Case C: Valid Token -&amp;gt; Allow&lt;/span&gt;
        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Auth Success: Valid Token"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Continue to upstream_peer&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_peer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;upstream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="na"&gt;.0&lt;/span&gt;
            &lt;span class="nf"&gt;.select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;b""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;.ok_or_else&lt;/span&gt;&lt;span class="p"&gt;(||&lt;/span&gt; &lt;span class="nn"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;explain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ErrorType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Custom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"NoUpstreamAvailable"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"Empty upstream pool"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;upstream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"auth-protected.cluster"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;upstreams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;LoadBalancer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;try_from_iter&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="s"&gt;"172.28.0.20:8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"172.28.0.21:8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;http_proxy_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;AuthProxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
    &lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="nf"&gt;.add_tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:6185"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Auth Proxy running on 0.0.0.0:6185"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Required Token: Bearer super-secret-token"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;We will cycle through the three authentication states to ensure strict enforcement.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Start the Proxy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 41_auth_request
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Test A: Missing Header (401)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec &lt;/span&gt;pingora_client_1 curl &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null http://172.28.0.10:6185/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Result:&lt;/strong&gt; &lt;code&gt;HTTP 401 Unauthorized&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logs:&lt;/strong&gt; &lt;code&gt;[WARN] Auth Failed: Missing Authorization header&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Test B: Invalid Token (403)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec &lt;/span&gt;pingora_client_1 curl &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer bad-token"&lt;/span&gt; http://172.28.0.10:6185/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Result:&lt;/strong&gt; &lt;code&gt;HTTP 403 Forbidden&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logs:&lt;/strong&gt; &lt;code&gt;[WARN] Auth Failed: Invalid Token&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Test C: Valid Token (200)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec &lt;/span&gt;pingora_client_1 curl &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer super-secret-token"&lt;/span&gt; http://172.28.0.10:6185/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Result:&lt;/strong&gt; &lt;code&gt;HTTP 200 OK&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logs:&lt;/strong&gt; &lt;code&gt;[INFO] Auth Success: Valid Token&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The upstream service is now protected. No request reaches the backend unless it has the secret key.&lt;/p&gt;

&lt;h1&gt;
  
  
  Lesson 42: Basic Authentication
&lt;/h1&gt;

&lt;p&gt;In Lesson 41, we implemented Bearer Token authentication, where the client is expected to know the secret beforehand.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Basic Authentication&lt;/strong&gt; follows a different flow called "Challenge-Response." If a user visits your site without credentials, the server must &lt;strong&gt;challenge&lt;/strong&gt; them to provide a username and password. This is what triggers the native login popup in web browsers.&lt;/p&gt;

&lt;p&gt;In this lesson, we implement this flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Challenge:&lt;/strong&gt; If the request has no credentials, return &lt;code&gt;401 Unauthorized&lt;/code&gt; &lt;strong&gt;AND&lt;/strong&gt; a &lt;code&gt;WWW-Authenticate&lt;/code&gt; header.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify:&lt;/strong&gt; If the request returns with credentials, valid them against our stored user (&lt;code&gt;admin:password&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Key Concepts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. The &lt;code&gt;WWW-Authenticate&lt;/code&gt; Header
&lt;/h3&gt;

&lt;p&gt;Simply returning &lt;code&gt;401&lt;/code&gt; isn't enough. Without the &lt;code&gt;WWW-Authenticate&lt;/code&gt; header, a browser will simply display a generic error page.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Header:&lt;/strong&gt; &lt;code&gt;WWW-Authenticate: Basic realm="PingoraProxy"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Behavior:&lt;/strong&gt; This tells the browser, "I need Basic credentials for the realm 'PingoraProxy'." The browser then opens the username/password dialog.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Manual Response Construction
&lt;/h3&gt;

&lt;p&gt;The helper method &lt;code&gt;session.respond_error(401)&lt;/code&gt; is convenient, but it doesn't allow us to inject custom headers like &lt;code&gt;WWW-Authenticate&lt;/code&gt;.&lt;br&gt;
To solve this, we must build the response manually using &lt;code&gt;ResponseHeader::build&lt;/code&gt;, insert our custom header, and send it using &lt;code&gt;session.write_response_header&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Base64 Encoding
&lt;/h3&gt;

&lt;p&gt;Basic Auth credentials are sent as &lt;code&gt;username:password&lt;/code&gt; encoded in Base64.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;User:&lt;/strong&gt; &lt;code&gt;admin&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pass:&lt;/strong&gt; &lt;code&gt;password&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;String:&lt;/strong&gt; &lt;code&gt;admin:password&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Base64:&lt;/strong&gt; &lt;code&gt;YWRtaW46cGFzc3dvcmQ=&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Header:&lt;/strong&gt; &lt;code&gt;Authorization: Basic YWRtaW46cGFzc3dvcmQ=&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  The Code (&lt;code&gt;examples/42_basic_auth.rs&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;To keep our dependencies minimal, we perform a direct string comparison against the pre-calculated Base64 string rather than importing a Base64 library.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;http&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ResponseHeader&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Required for building custom headers&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora_load_balancing&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;LoadBalancer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;selection&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;RoundRobin&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Expected Header: "Authorization: Basic YWRtaW46cGFzc3dvcmQ="&lt;/span&gt;
&lt;span class="c1"&gt;// We compare the raw base64 string directly for performance, &lt;/span&gt;
&lt;span class="c1"&gt;// avoiding the need to import a base64 decoding crate.&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;EXPECTED_AUTH&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;b"Basic YWRtaW46cGFzc3dvcmQ="&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nf"&gt;BasicAuthProxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LoadBalancer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RoundRobin&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ProxyHttp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;BasicAuthProxy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new_ctx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;request_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;where&lt;/span&gt;
        &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Send&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Sync&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;auth_header&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="nf"&gt;.req_header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="py"&gt;.headers&lt;/span&gt;&lt;span class="nf"&gt;.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Authorization"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;auth_header&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Case A: Missing Credentials -&amp;gt; Send Challenge&lt;/span&gt;
            &lt;span class="nb"&gt;None&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nd"&gt;warn!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Auth Failed: Missing Header. Sending Challenge"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="c1"&gt;// 1. Build a raw 401 response&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;ResponseHeader&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

                &lt;span class="c1"&gt;// 2. Inject the mandatory WWW-Authenticate header&lt;/span&gt;
                &lt;span class="c1"&gt;// This tells the client (browser) to prompt for "Basic" credentials.&lt;/span&gt;
                &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="nf"&gt;.insert_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"WWW-Authenticate"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Basic realm=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;PingoraProxy&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="nf"&gt;.insert_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Content-Length"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"0"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

                &lt;span class="c1"&gt;// 3. Write response and close stream (true = End of Stream)&lt;/span&gt;
                &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="nf"&gt;.write_response_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Stop processing&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="c1"&gt;// Case B: Header Present -&amp;gt; Validate&lt;/span&gt;
            &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="nf"&gt;.as_bytes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;EXPECTED_AUTH&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nd"&gt;warn!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Auth Failed: Wrong Credentials"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="c1"&gt;// Return 403 to indicate credentials were received but rejected&lt;/span&gt;
                    &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="nf"&gt;.respond_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Case C: Success&lt;/span&gt;
        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Auth Success: admin:password verified"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_peer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;upstream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="na"&gt;.0&lt;/span&gt;
            &lt;span class="nf"&gt;.select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;b""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;.ok_or_else&lt;/span&gt;&lt;span class="p"&gt;(||&lt;/span&gt; &lt;span class="nn"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;explain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ErrorType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Custom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"NoUpstreamAvailable"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"Empty upstream pool"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;upstream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"basic-auth.cluster"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;upstreams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;LoadBalancer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;try_from_iter&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="s"&gt;"172.28.0.20:8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"172.28.0.21:8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;http_proxy_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;BasicAuthProxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
    &lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="nf"&gt;.add_tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:6186"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Basic Auth Proxy running on 0.0.0.0:6186"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Credentials: admin / password"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;We will verify the Challenge, the Failure, and the Success states.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Start the Proxy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 42_basic_auth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Test A: The Challenge (Missing Credentials)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-v&lt;/span&gt; http://172.28.0.10:6186/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Result:&lt;/strong&gt; &lt;code&gt;401 Unauthorized&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Crucial Header:&lt;/strong&gt; Look for &lt;code&gt;&amp;lt; WWW-Authenticate: Basic realm="PingoraProxy"&lt;/code&gt;. If this is missing, a browser will not show the login popup.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Test B: The Rejection (Wrong Password)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="s2"&gt;"admin:wrong"&lt;/span&gt; http://172.28.0.10:6186/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Result:&lt;/strong&gt; &lt;code&gt;403 Forbidden&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Log:&lt;/strong&gt; &lt;code&gt;[WARN] Auth Failed: Wrong Credentials&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Test C: The Success (Correct Password)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="s2"&gt;"admin:password"&lt;/span&gt; http://172.28.0.10:6186/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Result:&lt;/strong&gt; &lt;code&gt;200 OK&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Log:&lt;/strong&gt; &lt;code&gt;[INFO] Auth Success: admin:password verified&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Module 6: Caching
&lt;/h1&gt;

&lt;p&gt;The fastest network request is the one you never make.&lt;/p&gt;

&lt;p&gt;In high-scale systems, 80% of the traffic often hits just 20% of the content. Repeatedly asking your backend database to "Get User Profile 123" or "Render Home Page" burns CPU cycles and latency unnecessarily.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Module 6&lt;/strong&gt; introduces the &lt;strong&gt;Pingora Cache&lt;/strong&gt; system. We will transform our proxy from a simple router into a high-performance content delivery node.&lt;/p&gt;

&lt;p&gt;We will cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Storage Backends:&lt;/strong&gt; How to store responses in memory (RAM) to serve them in microseconds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache Control:&lt;/strong&gt; How to obey standard HTTP headers (&lt;code&gt;Cache-Control&lt;/code&gt;, &lt;code&gt;Expires&lt;/code&gt;) so the proxy knows &lt;em&gt;what&lt;/em&gt; to cache and for &lt;em&gt;how long&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Request Coalescing:&lt;/strong&gt; The "Thundering Herd" problem—what happens when 1,000 users ask for the same missing file at the exact same millisecond? We will learn how to "lock" the cache so only &lt;em&gt;one&lt;/em&gt; request hits the origin while the others wait.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Advanced Patterns:&lt;/strong&gt; Implementing &lt;code&gt;PURGE&lt;/code&gt; to instantly clear content and &lt;code&gt;stale-while-revalidate&lt;/code&gt; to update content in the background without slowing down users.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By the end of this module, you will have built a caching layer capable of drastically reducing the load on your upstream servers.&lt;/p&gt;

&lt;h1&gt;
  
  
  Lesson 43: In-Memory Caching
&lt;/h1&gt;

&lt;p&gt;The fastest way to serve a request is to avoid sending it to the backend at all. &lt;strong&gt;Caching&lt;/strong&gt; allows the proxy to store the response from an upstream server (like Blue or Green) and serve it to subsequent users directly from memory.&lt;/p&gt;

&lt;p&gt;In this lesson, we implement a &lt;strong&gt;Basic In-Memory Cache&lt;/strong&gt;.&lt;br&gt;
To demonstrate this reliably, we will spin up a small internal "Mock Upstream" that returns a response with &lt;code&gt;Cache-Control: max-age=60&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Key Concepts
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. The Caching Lifecycle
&lt;/h3&gt;

&lt;p&gt;Pingora does not cache by default. You must opt-in via two distinct hooks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;request_cache_filter&lt;/code&gt; (The Setup):&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Runs &lt;em&gt;before&lt;/em&gt; the upstream connection.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Action:&lt;/strong&gt; Call &lt;code&gt;session.cache.enable()&lt;/code&gt;. This selects the storage backend (e.g., Memory) and sets the &lt;strong&gt;Cache Key&lt;/strong&gt; (e.g., the URL path).&lt;/li&gt;
&lt;li&gt;If you skip this, caching is disabled for the request.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;response_cache_filter&lt;/code&gt; (The Decision):&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Runs &lt;em&gt;after&lt;/em&gt; the upstream responds but &lt;em&gt;before&lt;/em&gt; sending headers to the client.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Action:&lt;/strong&gt; Determine if the response is worth keeping. Usually, this involves parsing the &lt;code&gt;Cache-Control&lt;/code&gt; header.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  2. &lt;code&gt;MemCache&lt;/code&gt; Backend
&lt;/h3&gt;

&lt;p&gt;Pingora provides &lt;code&gt;pingora_cache::memory::MemCache&lt;/code&gt;, a high-performance in-memory storage. It is ephemeral (lost on restart) but extremely fast, making it ideal for hot content.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Code (&lt;code&gt;examples/43_memory_cache.rs&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;We include a helper function &lt;code&gt;run_mock_upstream&lt;/code&gt; that listens on port &lt;code&gt;6191&lt;/code&gt;. It mimics a backend that explicitly allows caching via headers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;once_cell&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Lazy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;http&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ResponseHeader&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Imports for Caching&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora_cache&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;MemCache&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CacheKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CacheMetaDefaults&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RespCacheable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CachePhase&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora_cache&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;cache_control&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CacheControl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;net&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;TcpListener&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;AsyncReadExt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AsyncWriteExt&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;borrow&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Cow&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// 1. Initialize Global Memory Storage&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;MEM_CACHE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Lazy&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MemCache&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Lazy&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;MemCache&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;CacheProxy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ProxyHttp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;CacheProxy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new_ctx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. Enable Caching for the Request&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;request_cache_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Define the lookup key: we use the URL path.&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;CacheKey&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="nf"&gt;.req_header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="py"&gt;.uri&lt;/span&gt;&lt;span class="nf"&gt;.path&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Enable cache using our MemCache backend.&lt;/span&gt;
        &lt;span class="c1"&gt;// Args: (Storage, EvictionManager, Predictor, Lock, Stats)&lt;/span&gt;
        &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="py"&gt;.cache&lt;/span&gt;&lt;span class="nf"&gt;.enable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;*&lt;/span&gt;&lt;span class="n"&gt;MEM_CACHE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="py"&gt;.cache&lt;/span&gt;&lt;span class="nf"&gt;.set_cache_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_peer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Route to our internal mock upstream&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="s"&gt;"127.0.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6191&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"cache.local"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// 3. Decide if Response is Cacheable&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;response_cache_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;ResponseHeader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RespCacheable&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Parse the upstream headers (Cache-Control, Expires, etc.)&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;cc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;CacheControl&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_resp_headers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Define defaults if headers are missing (e.g., cache 200 OK for 60s)&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;defaults&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;CacheMetaDefaults&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="nf"&gt;.as_u16&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_secs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Return the decision&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;pingora_cache&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;resp_cacheable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;cc&lt;/span&gt;&lt;span class="nf"&gt;.as_ref&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;defaults&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// 4. Log Cache Status&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="py"&gt;.cache&lt;/span&gt;&lt;span class="nf"&gt;.phase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nn"&gt;CachePhase&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Hit&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Cache Status: HIT (Served from Memory)"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nn"&gt;CachePhase&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Miss&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Cache Status: MISS (Fetched from Upstream)"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nn"&gt;CachePhase&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Expired&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Cache Status: EXPIRED (Revalidating)"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Internal Mock Server to ensure consistent Cache-Control headers&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;run_mock_upstream&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;listener&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;TcpListener&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"127.0.0.1:6191"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Mock Upstream started on 127.0.0.1:6191"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;listener&lt;/span&gt;&lt;span class="nf"&gt;.accept&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;move&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0u8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="nf"&gt;.read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Response from Local Mock"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="s"&gt;"HTTP/1.1 200 OK&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s"&gt;
                    Content-Length: {}&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s"&gt;
                    Cache-Control: public, max-age=60&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s"&gt;
                    Connection: close&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s"&gt;
                    &lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s"&gt;
                    {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                    &lt;span class="n"&gt;body&lt;/span&gt;
                &lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="nf"&gt;.write_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="nf"&gt;.as_bytes&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Required for cache metadata compression&lt;/span&gt;
    &lt;span class="nn"&gt;pingora_cache&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;set_compression_dict_content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Cow&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Borrowed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;b""&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Start the mock upstream in a background thread&lt;/span&gt;
    &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(||&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;rt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Runtime&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;rt&lt;/span&gt;&lt;span class="nf"&gt;.block_on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;run_mock_upstream&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;http_proxy_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CacheProxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="nf"&gt;.add_tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:6190"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Memory Cache Proxy running on 0.0.0.0:6190"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;We will verify the entire lifecycle: &lt;strong&gt;Miss&lt;/strong&gt; (First fetch) -&amp;gt; &lt;strong&gt;Hit&lt;/strong&gt; (Memory serve) -&amp;gt; &lt;strong&gt;Expired&lt;/strong&gt; (Re-fetch).&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Start the Proxy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 43_memory_cache
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. First Request (Miss)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec &lt;/span&gt;pingora_client_1 curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://172.28.0.10:6190/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Output:&lt;/strong&gt; &lt;code&gt;Response from Local Mock&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Log:&lt;/strong&gt; &lt;code&gt;Cache Status: MISS (Fetched from Upstream)&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Second Request (Hit)
&lt;/h3&gt;

&lt;p&gt;Run the same command immediately.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Output:&lt;/strong&gt; &lt;code&gt;Response from Local Mock&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Log:&lt;/strong&gt; &lt;code&gt;Cache Status: HIT (Served from Memory)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Observation:&lt;/strong&gt; The proxy did &lt;em&gt;not&lt;/em&gt; connect to the backend (port 6191). It served the data instantly from RAM.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Expired Request
&lt;/h3&gt;

&lt;p&gt;Wait 60 seconds (the &lt;code&gt;max-age&lt;/code&gt; defined in the mock), then run the command again.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Output:&lt;/strong&gt; &lt;code&gt;Response from Local Mock&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Log:&lt;/strong&gt; &lt;code&gt;Cache Status: EXPIRED (Revalidating)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Observation:&lt;/strong&gt; Pingora detected the TTL had passed, fetched a fresh copy from the backend, and updated the cache.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Theory
&lt;/h2&gt;

&lt;p&gt;The Pingora caching system transforms your proxy from a simple traffic router into a sophisticated content delivery engine. To effectively use it, we must understand the specific Rust structures that manage the "Hit or Miss" decision process.&lt;/p&gt;

&lt;p&gt;Here is a detailed breakdown of the key components and syntax used in our code.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The Core Components
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;MemCache&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Definition:&lt;/strong&gt; An in-memory implementation of the &lt;code&gt;Storage&lt;/code&gt; trait provided by &lt;code&gt;pingora-cache&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Role:&lt;/strong&gt; It acts as the backend "hard drive" for the cache, but stores data in RAM (using a specialized hash map) instead of on disk.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key Characteristics:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Volatile:&lt;/strong&gt; Since it lives in RAM, all cache data is lost if the proxy process restarts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fast:&lt;/strong&gt; Lookups avoid disk I/O entirely, offering microsecond-level latency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Concurrency:&lt;/strong&gt; It uses &lt;code&gt;RwLock&lt;/code&gt; (Read-Write Lock) mechanisms to safely allow multiple concurrent readers while ensuring data integrity during writes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Storage Logic:&lt;/strong&gt; It maps a hashed &lt;code&gt;CacheKey&lt;/code&gt; to a &lt;code&gt;CacheObject&lt;/code&gt;, which contains both the metadata header (expiry, headers) and the body payload.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;CacheKey&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Definition:&lt;/strong&gt; A struct that uniquely identifies a cacheable asset.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Role:&lt;/strong&gt; It functions as the lookup address for the storage backend. If two different user requests generate the same &lt;code&gt;CacheKey&lt;/code&gt;, they will map to the same cached object.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Composition:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Primary Key:&lt;/strong&gt; Usually derived from the URL path (e.g., &lt;code&gt;/images/logo.png&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Namespace:&lt;/strong&gt; An optional prefix to isolate distinct datasets (e.g., &lt;code&gt;v1&lt;/code&gt; vs &lt;code&gt;v2&lt;/code&gt; of an API).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User Tag:&lt;/strong&gt; An optional tag to shard storage by user (e.g., specific cache partitions for specific customers).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Variance:&lt;/strong&gt; Handles &lt;code&gt;Vary&lt;/code&gt; headers. For example, it ensures a request with &lt;code&gt;Accept-Encoding: gzip&lt;/code&gt; gets a compressed version, while &lt;code&gt;identity&lt;/code&gt; gets a plain version, even if the URL is the same.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hashing:&lt;/strong&gt; Internally, Pingora uses the &lt;strong&gt;Blake2b&lt;/strong&gt; algorithm to generate a 128-bit binary hash from these components for efficient storage lookup.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;CacheMetaDefaults&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Definition:&lt;/strong&gt; A configuration struct that defines the "fallback" behavior.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Role:&lt;/strong&gt; Upstream servers are often imperfect and forget to send &lt;code&gt;Cache-Control&lt;/code&gt; headers. This struct acts as the proxy's internal policy engine to decide what to do in those cases.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key Fields:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;fresh_sec_fn&lt;/code&gt;: A function mapping an HTTP Status Code to a Duration. (e.g., "If &lt;code&gt;200 OK&lt;/code&gt;, cache for 60s. If &lt;code&gt;500 Error&lt;/code&gt;, do not cache").&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;stale_while_revalidate_sec&lt;/code&gt;: Defines the window where it is acceptable to serve expired content while a background fetch refreshes the data.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;stale_if_error_sec&lt;/code&gt;: Defines how long to serve old content if the upstream server goes down.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;RespCacheable&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Definition:&lt;/strong&gt; An enum representing the final verdict on whether a specific response &lt;em&gt;can&lt;/em&gt; be cached.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Role:&lt;/strong&gt; This is the output of your logic in &lt;code&gt;response_cache_filter&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Variants:&lt;/strong&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Cacheable(CacheMeta)&lt;/code&gt;&lt;/strong&gt;: The verdict is &lt;strong&gt;Yes&lt;/strong&gt;. The struct contains the calculated metadata (expiry timestamp, headers to preserve).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Uncacheable(NoCacheReason)&lt;/code&gt;&lt;/strong&gt;: The verdict is &lt;strong&gt;No&lt;/strong&gt;. It includes the specific reason (e.g., &lt;code&gt;OriginNotCache&lt;/code&gt; if the upstream sent &lt;code&gt;no-store&lt;/code&gt;, or &lt;code&gt;ResponseTooLarge&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;CachePhase&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Definition:&lt;/strong&gt; An enum tracking the lifecycle state of a request relative to the cache system.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Role:&lt;/strong&gt; It acts as a state machine indicator, primarily useful for observability (logging/metrics) and debugging.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Common States:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Disabled&lt;/code&gt;: The cache was never enabled for this request.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Miss&lt;/code&gt;: Content was not found in storage; currently fetching from upstream.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Hit&lt;/code&gt;: Content was found and is fresh; serving directly from cache.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Stale&lt;/code&gt;: Content was found but has expired; a revalidation request is required.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Revalidated&lt;/code&gt;: Stale content was confirmed as still valid by the upstream (via a &lt;code&gt;304 Not Modified&lt;/code&gt; response).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;CacheControl&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Definition:&lt;/strong&gt; A parser for the standard HTTP &lt;code&gt;Cache-Control&lt;/code&gt; header.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Role:&lt;/strong&gt; It takes raw header strings (e.g., &lt;code&gt;public, max-age=3600, no-transform&lt;/code&gt;) and parses them into a structured &lt;code&gt;DirectiveMap&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Capabilities:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Extracts critical directives like &lt;code&gt;max-age&lt;/code&gt;, &lt;code&gt;s-maxage&lt;/code&gt;, &lt;code&gt;no-store&lt;/code&gt;, and &lt;code&gt;private&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Handles edge cases like quoting and case-insensitivity (e.g., &lt;code&gt;Max-Age="60"&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Implements &lt;code&gt;InterpretCacheControl&lt;/code&gt; to calculate actual Time-To-Live (TTL) durations according to strict RFC 9111 rules.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h3&gt;
  
  
  2. Explanation of &lt;code&gt;&amp;amp;*&lt;/code&gt; in &lt;code&gt;enable&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;You may have noticed this specific syntax in the code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;MEM_CACHE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Lazy&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MemCache&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Lazy&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;MemCache&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="py"&gt;.cache&lt;/span&gt;&lt;span class="nf"&gt;.enable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;*&lt;/span&gt;&lt;span class="n"&gt;MEM_CACHE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This &lt;code&gt;&amp;amp;*&lt;/code&gt; pattern is a Rust idiom used to satisfy type coercion and lifetime requirements when dealing with &lt;code&gt;Lazy&lt;/code&gt; static variables.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Type of &lt;code&gt;MEM_CACHE&lt;/code&gt;:&lt;/strong&gt;
&lt;code&gt;MEM_CACHE&lt;/code&gt; is declared as &lt;code&gt;Lazy&amp;lt;MemCache&amp;gt;&lt;/code&gt;. It is not a &lt;code&gt;MemCache&lt;/code&gt; struct directly; it is a wrapper that ensures initialization happens only on first access.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Dereference (&lt;code&gt;*&lt;/code&gt;):&lt;/strong&gt;
&lt;code&gt;Lazy&amp;lt;T&amp;gt;&lt;/code&gt; implements the &lt;code&gt;Deref&lt;/code&gt; trait.

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;*MEM_CACHE&lt;/code&gt; dereferences the &lt;code&gt;Lazy&lt;/code&gt; wrapper to access the underlying value.&lt;/li&gt;
&lt;li&gt;Effectively, this operation extracts the actual &lt;code&gt;MemCache&lt;/code&gt; instance inside the static variable.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Reference (&lt;code&gt;&amp;amp;&lt;/code&gt;):&lt;/strong&gt;
The &lt;code&gt;&amp;amp;&lt;/code&gt; operator then takes a reference to that dereferenced value.

&lt;ul&gt;
&lt;li&gt;So, &lt;code&gt;&amp;amp;*MEM_CACHE&lt;/code&gt; results in a reference of type &lt;code&gt;&amp;amp;MemCache&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Target Type (&lt;code&gt;dyn Storage&lt;/code&gt;):&lt;/strong&gt;
The &lt;code&gt;enable&lt;/code&gt; function signature expects a reference to a Trait Object:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;   &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;enable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;'static&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="n"&gt;Storage&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Sync&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Since &lt;code&gt;MemCache&lt;/code&gt; implements the &lt;code&gt;Storage&lt;/code&gt; trait, Rust automatically coerces the concrete &lt;code&gt;&amp;amp;MemCache&lt;/code&gt; into the dynamic &lt;code&gt;&amp;amp;dyn Storage&lt;/code&gt; interface.

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Why not just pass &lt;code&gt;&amp;amp;MEM_CACHE&lt;/code&gt;?&lt;/strong&gt;
If you tried to pass &lt;code&gt;&amp;amp;MEM_CACHE&lt;/code&gt;, you would be passing a reference to the wrapper itself (&lt;code&gt;&amp;amp;Lazy&amp;lt;MemCache&amp;gt;&lt;/code&gt;). The &lt;code&gt;Lazy&lt;/code&gt; struct does &lt;em&gt;not&lt;/em&gt; implement the &lt;code&gt;Storage&lt;/code&gt; trait; only the inner &lt;code&gt;MemCache&lt;/code&gt; does. Therefore, you must manually peel off the wrapper (&lt;code&gt;*&lt;/code&gt;) and then reference the inner object (&lt;code&gt;&amp;amp;&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Summary:&lt;/strong&gt; &lt;code&gt;&amp;amp;*MEM_CACHE&lt;/code&gt; translates to: "Give me a reference to the &lt;em&gt;actual initialized cache&lt;/em&gt; inside the global wrapper."&lt;/p&gt;

&lt;h1&gt;
  
  
  Lesson 44: Respecting Cache-Control Headers
&lt;/h1&gt;

&lt;p&gt;In Lesson 43, we manually forced the proxy to cache everything for 60 seconds. While useful for testing, a production proxy should never do this. It should respect the &lt;strong&gt;origin server's&lt;/strong&gt; instructions.&lt;/p&gt;

&lt;p&gt;If an upstream server sends &lt;code&gt;Cache-Control: no-store&lt;/code&gt; (e.g., banking data) or &lt;code&gt;max-age=5&lt;/code&gt; (e.g., stock tickers), the proxy must obey. Pingora provides built-in parsers to handle strict RFC compliance out of the box.&lt;/p&gt;

&lt;p&gt;In this lesson, we implement a &lt;strong&gt;Smart Caching Proxy&lt;/strong&gt; that adapts its behavior based on the URL:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/short&lt;/code&gt; -&amp;gt; Cache for 5 seconds.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/long&lt;/code&gt; -&amp;gt; Cache for 60 seconds.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/no_store&lt;/code&gt; -&amp;gt; Do not cache at all.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key Concepts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. The &lt;code&gt;Cache-Control&lt;/code&gt; Header
&lt;/h3&gt;

&lt;p&gt;This is the standard mechanism for web caching.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;max-age=N&lt;/code&gt;: The content is fresh for N seconds.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;no-store&lt;/code&gt;: The content must &lt;strong&gt;never&lt;/strong&gt; be stored in non-volatile storage.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;private&lt;/code&gt;: The content is for a specific user (e.g., a profile page) and should not be stored by a shared proxy.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. RFC Compliance via &lt;code&gt;resp_cacheable&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Pingora provides a helper function &lt;code&gt;pingora_cache::filters::resp_cacheable&lt;/code&gt;.&lt;br&gt;
Instead of writing complex &lt;code&gt;if&lt;/code&gt; statements (e.g., "if header contains 'no-store'..."), you pass the parsed headers to this function. It returns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;RespCacheable::Cacheable(meta)&lt;/code&gt;: If valid, with the calculated TTL.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;RespCacheable::Uncacheable(reason)&lt;/code&gt;: If invalid (e.g., &lt;code&gt;origin_no_store&lt;/code&gt;, &lt;code&gt;response_too_large&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  The Code (&lt;code&gt;examples/44_cache_control.rs&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;We update our mock upstream to be "dynamic"—it reads the request path and changes the response headers accordingly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;once_cell&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Lazy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;http&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ResponseHeader&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora_cache&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;MemCache&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CacheKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CacheMetaDefaults&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RespCacheable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CachePhase&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora_cache&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;cache_control&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CacheControl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;borrow&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Cow&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;net&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;TcpListener&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;AsyncReadExt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AsyncWriteExt&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;MEM_CACHE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Lazy&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MemCache&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Lazy&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;MemCache&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;CacheControlProxy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ProxyHttp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;CacheControlProxy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new_ctx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="c1"&gt;// 1. Setup Storage&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;request_cache_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;CacheKey&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="nf"&gt;.req_header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="py"&gt;.uri&lt;/span&gt;&lt;span class="nf"&gt;.path&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="py"&gt;.cache&lt;/span&gt;&lt;span class="nf"&gt;.enable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;*&lt;/span&gt;&lt;span class="n"&gt;MEM_CACHE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="py"&gt;.cache&lt;/span&gt;&lt;span class="nf"&gt;.set_cache_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_peer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Connect to our dynamic mock upstream&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="s"&gt;"127.0.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6193&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"header.test.local"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. The Decision Logic&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;response_cache_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;ResponseHeader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RespCacheable&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Parse headers using Pingora's built-in parser&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;cc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;CacheControl&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_resp_headers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Define empty defaults (Cache nothing if headers are missing)&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;defaults&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;CacheMetaDefaults&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Let Pingora decide based on RFC rules&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;pingora_cache&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;resp_cacheable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;cc&lt;/span&gt;&lt;span class="nf"&gt;.as_ref&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;defaults&lt;/span&gt;
        &lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;where&lt;/span&gt;
        &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Send&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Sync&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="nf"&gt;.req_header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="py"&gt;.uri&lt;/span&gt;&lt;span class="nf"&gt;.path&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="py"&gt;.cache&lt;/span&gt;&lt;span class="nf"&gt;.phase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nn"&gt;CachePhase&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Hit&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Path {}, Status: HIT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nn"&gt;CachePhase&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Miss&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Path {}, Status: MISS"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nn"&gt;CachePhase&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Expired&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Path {}, Status: EXPIRED"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nn"&gt;CachePhase&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Disabled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Path {}, Status: SKIP (Uncacheable)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// 3. Dynamic Mock Upstream&lt;/span&gt;
&lt;span class="c1"&gt;// Returns different Cache-Control headers based on the request URL.&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;run_dynamic_upstream&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;listener&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;TcpListener&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"127.0.0.1:6193"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Dynamic Upstream running on 127.0.0.1:6193"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;listener&lt;/span&gt;&lt;span class="nf"&gt;.accept&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;move&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0u8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="nf"&gt;.read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="nf"&gt;.unwrap_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_utf8_lossy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

                &lt;span class="c1"&gt;// Determine Header based on Path&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;cc_header&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="nf"&gt;.contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GET /short"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="s"&gt;"max-age=5"&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="nf"&gt;.contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GET /long"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="s"&gt;"max-age=60"&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="nf"&gt;.contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GET /no_store"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="s"&gt;"no-store"&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="s"&gt;"max-age=10"&lt;/span&gt;
                &lt;span class="p"&gt;};&lt;/span&gt;

                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Content for {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cc_header&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="s"&gt;"HTTP/1.1 200 OK&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s"&gt;
                    Content-Length: {}&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s"&gt;
                    Cache-Control: {}&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s"&gt;
                    Connection: close&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s"&gt;
                    &lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s"&gt;
                    {}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;cc_header&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;body&lt;/span&gt;
                &lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="nf"&gt;.write_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="nf"&gt;.as_bytes&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nn"&gt;pingora_cache&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;set_compression_dict_content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Cow&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Borrowed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;b""&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(||&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;rt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Runtime&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;rt&lt;/span&gt;&lt;span class="nf"&gt;.block_on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;run_dynamic_upstream&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;http_proxy_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CacheControlProxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="nf"&gt;.add_tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:6192"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Cache Control Proxy running on 0.0.0.0:6192"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;We will verify that Pingora adapts its caching strategy dynamically.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Start the Proxy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 44_cache_control
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Test A: &lt;code&gt;no-store&lt;/code&gt; (Security Check)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec &lt;/span&gt;pingora_client_1 curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://172.28.0.10:6192/no_store
docker &lt;span class="nb"&gt;exec &lt;/span&gt;pingora_client_1 curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://172.28.0.10:6192/no_store
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Result:&lt;/strong&gt; Both requests are fetched from the upstream.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Log:&lt;/strong&gt; &lt;code&gt;Path /no_store, Status: SKIP (Uncacheable)&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Test B: &lt;code&gt;max-age=5&lt;/code&gt; (Short Lived)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Request 1 (Miss)&lt;/span&gt;
docker &lt;span class="nb"&gt;exec &lt;/span&gt;pingora_client_1 curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://172.28.0.10:6192/short
&lt;span class="c"&gt;# Request 2 (Hit) - Immediate&lt;/span&gt;
docker &lt;span class="nb"&gt;exec &lt;/span&gt;pingora_client_1 curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://172.28.0.10:6192/short
&lt;span class="c"&gt;# Wait 6 seconds...&lt;/span&gt;
&lt;span class="nb"&gt;sleep &lt;/span&gt;6
&lt;span class="c"&gt;# Request 3 (Expired)&lt;/span&gt;
docker &lt;span class="nb"&gt;exec &lt;/span&gt;pingora_client_1 curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://172.28.0.10:6192/short
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Log:&lt;/strong&gt; &lt;code&gt;MISS&lt;/code&gt; -&amp;gt; &lt;code&gt;HIT&lt;/code&gt; -&amp;gt; &lt;code&gt;EXPIRED&lt;/code&gt; (because 6s &amp;gt; 5s).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Test C: &lt;code&gt;max-age=60&lt;/code&gt; (Long Lived)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Request 1 (Miss)&lt;/span&gt;
docker &lt;span class="nb"&gt;exec &lt;/span&gt;pingora_client_1 curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://172.28.0.10:6192/long
&lt;span class="c"&gt;# Wait 6 seconds...&lt;/span&gt;
&lt;span class="nb"&gt;sleep &lt;/span&gt;6
&lt;span class="c"&gt;# Request 2 (Hit)&lt;/span&gt;
docker &lt;span class="nb"&gt;exec &lt;/span&gt;pingora_client_1 curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://172.28.0.10:6192/long
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Log:&lt;/strong&gt; &lt;code&gt;MISS&lt;/code&gt; -&amp;gt; &lt;code&gt;HIT&lt;/code&gt; (because 6s &amp;lt; 60s).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This confirms the proxy is correctly parsing and enforcing the upstream's caching policies.&lt;/p&gt;

&lt;h1&gt;
  
  
  Lesson 45: Cache Locking (Request Coalescing)
&lt;/h1&gt;

&lt;p&gt;Imagine you are running a news site. A major story breaks. 1,000 users click the link at the exact same millisecond. The story is not in your cache yet.&lt;/p&gt;

&lt;p&gt;Without protection, your proxy sends &lt;strong&gt;1,000 requests&lt;/strong&gt; to your database simultaneously. Your database crashes. This is called the &lt;strong&gt;Thundering Herd&lt;/strong&gt; problem (or "Cache Stampede").&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cache Locking&lt;/strong&gt; (Request Coalescing) solves this.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Request 1 arrives:&lt;/strong&gt; The proxy sees a cache miss. It acquires a "Write Lock" on the URL. It contacts the backend.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Requests 2-1000 arrive:&lt;/strong&gt; The proxy sees a cache miss &lt;em&gt;but&lt;/em&gt; sees the lock is held. These requests &lt;strong&gt;wait&lt;/strong&gt; (sleep) at the proxy layer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Request 1 returns:&lt;/strong&gt; The response is stored in the cache. The lock is released.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Requests 2-1000 wake up:&lt;/strong&gt; They instantly find the fresh data in the cache and return.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Result: &lt;strong&gt;1 backend request.&lt;/strong&gt; 1,000 happy users.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Concepts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. &lt;code&gt;CacheLock&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Pingora provides &lt;code&gt;pingora_cache::lock::CacheLock&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It is a concurrent hash table of semaphores.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Writer:&lt;/strong&gt; The thread fetching from upstream.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reader:&lt;/strong&gt; The threads waiting for the writer.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Enabling the Lock
&lt;/h3&gt;

&lt;p&gt;We pass the lock instance to &lt;code&gt;session.cache.enable()&lt;/code&gt;. It is the 4th argument.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="py"&gt;.cache&lt;/span&gt;&lt;span class="nf"&gt;.enable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;*&lt;/span&gt;&lt;span class="n"&gt;MEM_CACHE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;*&lt;/span&gt;&lt;span class="n"&gt;CACHE_LOCK&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Observable Metrics
&lt;/h3&gt;

&lt;p&gt;Pingora tracks how long a request waited for a lock. We can log this using &lt;code&gt;session.cache.lock_duration()&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Writer:&lt;/strong&gt; Wait time is &lt;code&gt;0ns&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reader:&lt;/strong&gt; Wait time is roughly equal to the upstream response time.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Code (&lt;code&gt;examples/45_cache_lock.rs&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;We simulate a "Heavy" upstream that sleeps for &lt;strong&gt;2 seconds&lt;/strong&gt; before responding. This allows us to manually fire multiple requests during that window and watch them coalesce.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;once_cell&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Lazy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;http&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ResponseHeader&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora_cache&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;MemCache&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CacheKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CacheMetaDefaults&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RespCacheable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CachePhase&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora_cache&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;cache_control&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CacheControl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora_cache&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CacheLock&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;borrow&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Cow&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;net&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;TcpListener&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;AsyncReadExt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AsyncWriteExt&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// 1. Initialize Global Lock Manager&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;MEM_CACHE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Lazy&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MemCache&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Lazy&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;MemCache&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;CACHE_LOCK&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Lazy&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CacheLock&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Lazy&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(||&lt;/span&gt; &lt;span class="nn"&gt;CacheLock&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_secs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;CacheLockProxy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ProxyHttp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;CacheLockProxy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new_ctx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. Enable Caching WITH Locking&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;request_cache_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;CacheKey&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="nf"&gt;.req_header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="py"&gt;.uri&lt;/span&gt;&lt;span class="nf"&gt;.path&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="py"&gt;.cache&lt;/span&gt;&lt;span class="nf"&gt;.enable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="o"&gt;&amp;amp;*&lt;/span&gt;&lt;span class="n"&gt;MEM_CACHE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;*&lt;/span&gt;&lt;span class="n"&gt;CACHE_LOCK&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// Pass the lock here&lt;/span&gt;
            &lt;span class="nb"&gt;None&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="py"&gt;.cache&lt;/span&gt;&lt;span class="nf"&gt;.set_cache_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_peer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="s"&gt;"127.0.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6195&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"heavy.local"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;response_cache_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;ResponseHeader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RespCacheable&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;cc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;CacheControl&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_resp_headers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;defaults&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;CacheMetaDefaults&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;pingora_cache&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;resp_cacheable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cc&lt;/span&gt;&lt;span class="nf"&gt;.as_ref&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;defaults&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;where&lt;/span&gt;
        &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Send&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Sync&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// 3. Log Who was Writer and Who was Reader&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="py"&gt;.cache&lt;/span&gt;&lt;span class="nf"&gt;.phase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nn"&gt;CachePhase&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Hit&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"HIT (READER)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nn"&gt;CachePhase&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Miss&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"MISS (WRITER)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"OTHER"&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;wait_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="py"&gt;.cache&lt;/span&gt;&lt;span class="nf"&gt;.lock_duration&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ZERO&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"Client Finished. Status: {}. Waited for lock: {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;wait_time&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// 4. Heavy Mock Upstream&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;run_slow_upstream&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;listener&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;TcpListener&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"127.0.0.1:6195"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Slow Upstream running on 127.0.0.1:6195"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;listener&lt;/span&gt;&lt;span class="nf"&gt;.accept&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;move&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0u8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="nf"&gt;.read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

                &lt;span class="c1"&gt;// Simulate heavy work&lt;/span&gt;
                &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&amp;gt;&amp;gt; Processing expensive request (2s delay)..."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_secs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Expensive Content Generated"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="s"&gt;"HTTP/1.1 200 OK&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s"&gt;
                    Content-Length: {}&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s"&gt;
                    Cache-Control: public, max-age=60&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s"&gt;
                    Connection: close&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s"&gt;
                    &lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s"&gt;
                    {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                    &lt;span class="n"&gt;body&lt;/span&gt;
                &lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="nf"&gt;.write_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="nf"&gt;.as_bytes&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nn"&gt;pingora_cache&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;set_compression_dict_content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Cow&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Borrowed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;b""&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(||&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;rt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Runtime&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;rt&lt;/span&gt;&lt;span class="nf"&gt;.block_on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;run_slow_upstream&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;http_proxy_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CacheLockProxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="nf"&gt;.add_tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:6194"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Cache Lock Proxy running on 0.0.0.0:6194"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;We will simulate a mini "Thundering Herd" using a bash loop.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Start the Proxy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 45_cache_lock
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Fire Concurrent Requests
&lt;/h3&gt;

&lt;p&gt;Run this command to spawn 5 requests in the background simultaneously.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;1..5&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do 
  &lt;/span&gt;docker &lt;span class="nb"&gt;exec &lt;/span&gt;pingora_client_1 curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://172.28.0.10:6194/resource &amp;amp;
&lt;span class="k"&gt;done&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;wait&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Observe the Logs
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Upstream:&lt;/strong&gt; You will see &lt;code&gt;&amp;gt;&amp;gt; Processing expensive request&lt;/code&gt; exactly &lt;strong&gt;once&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proxy:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;1 log entry: &lt;code&gt;Status: MISS (WRITER). Waited for lock: 0ns&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;4 log entries: &lt;code&gt;Status: HIT (READER). Waited for lock: 2.00s&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;This confirms the lock worked perfectly. The backend was protected, and all clients got the correct data.&lt;/p&gt;

&lt;h1&gt;
  
  
  Lesson 46: Cache Purging
&lt;/h1&gt;

&lt;p&gt;Sometimes, caching works &lt;em&gt;too&lt;/em&gt; well. You fix a typo on your homepage, but the cache is set to expire in 5 minutes. Do you wait 5 minutes while users see the typo?&lt;/p&gt;

&lt;p&gt;No. You &lt;strong&gt;Purge&lt;/strong&gt; the cache.&lt;/p&gt;

&lt;p&gt;In this lesson, we implement the &lt;strong&gt;HTTP PURGE&lt;/strong&gt; method.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Standard Flow:&lt;/strong&gt; &lt;code&gt;GET /file&lt;/code&gt; -&amp;gt; Returns cached content.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Admin Flow:&lt;/strong&gt; &lt;code&gt;PURGE /file&lt;/code&gt; -&amp;gt; Deletes the content from the cache immediately.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key Concepts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. The &lt;code&gt;PURGE&lt;/code&gt; Verb
&lt;/h3&gt;

&lt;p&gt;It is important to note that &lt;code&gt;PURGE&lt;/code&gt; is &lt;strong&gt;not a standard HTTP method&lt;/strong&gt; (like GET, POST, DELETE). It is not defined in the core HTTP RFCs.&lt;br&gt;
However, it has become a de-facto standard in the proxy world (popularized by Varnish, Squid, and Fastly) for administrative cache deletion. Browsers never send this method; it is strictly a tool for developers and sysadmins.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. The &lt;code&gt;is_purge&lt;/code&gt; Hook
&lt;/h3&gt;

&lt;p&gt;Pingora abstracts the complexity of locking and deleting items. You simply implement the &lt;code&gt;is_purge&lt;/code&gt; method in your trait.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Return &lt;code&gt;true&lt;/code&gt;:&lt;/strong&gt; Pingora will take the &lt;code&gt;CacheKey&lt;/code&gt; (configured in &lt;code&gt;request_cache_filter&lt;/code&gt;), remove it from the storage backend, and return a &lt;code&gt;200 OK&lt;/code&gt; to the client. The request &lt;strong&gt;stops&lt;/strong&gt; here; it is never sent upstream.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Return &lt;code&gt;false&lt;/code&gt;:&lt;/strong&gt; Pingora processes the request normally (fetch or serve).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  3. Key Consistency
&lt;/h3&gt;

&lt;p&gt;For a purge to work, the &lt;strong&gt;Cache Key&lt;/strong&gt; generated during the &lt;code&gt;PURGE&lt;/code&gt; request must match the key generated during the original &lt;code&gt;GET&lt;/code&gt; request &lt;strong&gt;byte-for-byte&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If your GET logic includes the &lt;code&gt;Accept-Encoding&lt;/code&gt; header in the key...&lt;/li&gt;
&lt;li&gt;But your PURGE request doesn't send that header...&lt;/li&gt;
&lt;li&gt;The keys won't match, and the purge will fail (nothing gets deleted).
In this lesson, we use the simple URL path for the key, ensuring consistency.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  The Code (&lt;code&gt;examples/46_cache_purge.rs&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;Notice how &lt;code&gt;request_cache_filter&lt;/code&gt; runs for &lt;em&gt;every&lt;/em&gt; request. This is crucial because we need to calculate the key &lt;em&gt;before&lt;/em&gt; we decide whether to fetch (GET) or delete (PURGE).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;once_cell&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Lazy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;http&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ResponseHeader&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora_cache&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;MemCache&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CacheKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CacheMetaDefaults&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RespCacheable&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora_cache&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;cache_control&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CacheControl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;borrow&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Cow&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;net&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;TcpListener&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;AsyncReadExt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AsyncWriteExt&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;MEM_CACHE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Lazy&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MemCache&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Lazy&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;MemCache&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;PurgeProxy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ProxyHttp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;PurgeProxy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new_ctx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="c1"&gt;// 1. Setup the Key (Crucial: Must be identical for GET and PURGE)&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;request_cache_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="nf"&gt;.req_header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="py"&gt;.uri&lt;/span&gt;&lt;span class="nf"&gt;.path&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"(1) Setup: Configuring CacheKey for path: '{}'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;CacheKey&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="py"&gt;.cache&lt;/span&gt;&lt;span class="nf"&gt;.enable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;*&lt;/span&gt;&lt;span class="n"&gt;MEM_CACHE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="py"&gt;.cache&lt;/span&gt;&lt;span class="nf"&gt;.set_cache_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. The Purge Decision&lt;/span&gt;
    &lt;span class="c1"&gt;// If we return true, Pingora deletes the key and stops processing.&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;is_purge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="nf"&gt;.req_header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="py"&gt;.method&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="nf"&gt;.as_str&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"PURGE"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"(2) Decision: Method is PURGE. Hijacking request to delete item."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"(2) Decision: Method is {}. Proceeding to Standard Cache Flow."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;false&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_peer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"(3) Cache Miss: Connecting to Upstream to fetch fresh content..."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="s"&gt;"127.0.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6197&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"purge.local"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;response_cache_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;ResponseHeader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RespCacheable&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"(4) Validation: Examining Upstream Headers for Cache-Control..."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;cc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;CacheControl&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_resp_headers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;defaults&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;CacheMetaDefaults&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;pingora_cache&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;resp_cacheable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cc&lt;/span&gt;&lt;span class="nf"&gt;.as_ref&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;defaults&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Mock Upstream: Returns content valid for 5 minutes (300s)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;run_upstream&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;listener&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;TcpListener&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"127.0.0.1:6197"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Upstream running on 127.0.0.1:6197"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;listener&lt;/span&gt;&lt;span class="nf"&gt;.accept&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;move&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0u8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="nf"&gt;.read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Cached Content (TTL 300s)"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="s"&gt;"HTTP/1.1 200 OK&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s"&gt;
                    Content-Length: {}&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s"&gt;
                    Cache-Control: public, max-age=300&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s"&gt;
                    Connection: close&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s"&gt;
                    &lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s"&gt;
                    {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                    &lt;span class="n"&gt;body&lt;/span&gt;
                &lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="nf"&gt;.write_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="nf"&gt;.as_bytes&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nn"&gt;pingora_cache&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;set_compression_dict_content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Cow&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Borrowed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;b""&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(||&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;rt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Runtime&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;rt&lt;/span&gt;&lt;span class="nf"&gt;.block_on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;run_upstream&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;http_proxy_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PurgeProxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="nf"&gt;.add_tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:6196"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Purge Proxy running on 0.0.0.0:6196"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;We will cache an item, verify it is cached, delete it, and verify it is gone.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Start the Proxy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 46_cache_purge
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Prime the Cache (Miss)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec &lt;/span&gt;pingora_client_1 curl &lt;span class="nt"&gt;-v&lt;/span&gt; http://172.28.0.10:6196/file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Log:&lt;/strong&gt; &lt;code&gt;(3) Cache Miss: Connecting to Upstream...&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Response Headers:&lt;/strong&gt; &lt;code&gt;Cache-Control: public, max-age=300&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Verify Hit
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec &lt;/span&gt;pingora_client_1 curl &lt;span class="nt"&gt;-v&lt;/span&gt; http://172.28.0.10:6196/file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Log:&lt;/strong&gt; (No upstream connection log).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Response Headers:&lt;/strong&gt; &lt;code&gt;Age: 6&lt;/code&gt; (Served from memory).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Execute Purge
&lt;/h3&gt;

&lt;p&gt;We use &lt;code&gt;-X PURGE&lt;/code&gt; to change the HTTP verb.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec &lt;/span&gt;pingora_client_1 curl &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; PURGE http://172.28.0.10:6196/file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Log:&lt;/strong&gt; &lt;code&gt;(2) Decision: Method is PURGE. Hijacking request to delete item.&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Response:&lt;/strong&gt; &lt;code&gt;200 OK&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. Verify Deletion (Miss)
&lt;/h3&gt;

&lt;p&gt;Run the GET command again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec &lt;/span&gt;pingora_client_1 curl &lt;span class="nt"&gt;-v&lt;/span&gt; http://172.28.0.10:6196/file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Log:&lt;/strong&gt; &lt;code&gt;(3) Cache Miss: Connecting to Upstream...&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Result:&lt;/strong&gt; The proxy went back to the upstream, proving the cache entry was deleted.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Lesson 47: Stale-While-Revalidate (SWR)
&lt;/h1&gt;

&lt;p&gt;In a standard caching setup, when an item expires (TTL hits 0), the very next user to request it suffers a "Cache Miss." They must wait for the backend to generate a fresh response. If your backend takes 2 seconds, that user waits 2 seconds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stale-While-Revalidate (SWR)&lt;/strong&gt; eliminates this latency.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Logic:&lt;/strong&gt; If an item is expired but &lt;em&gt;recently&lt;/em&gt; expired (e.g., within 10 seconds), serve the old data &lt;strong&gt;immediately&lt;/strong&gt; (0ms latency).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Magic:&lt;/strong&gt; While the user walks away happy with their (slightly old) data, the proxy triggers a &lt;strong&gt;background fetch&lt;/strong&gt; to update the cache.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Result:&lt;/strong&gt; The next user gets the fresh data, and &lt;em&gt;nobody&lt;/em&gt; ever waited for the backend.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key Concepts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. The SWR Window
&lt;/h3&gt;

&lt;p&gt;SWR introduces a "grace period" after the &lt;code&gt;max-age&lt;/code&gt; expires.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fresh (0-5s):&lt;/strong&gt; Serve from cache.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stale Window (5-15s):&lt;/strong&gt; Serve stale content + Background Update.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dead (&amp;gt;15s):&lt;/strong&gt; Content is too old. Block and fetch.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. &lt;code&gt;should_serve_stale&lt;/code&gt; Hook
&lt;/h3&gt;

&lt;p&gt;This is the most critical part of the implementation. By default, Pingora is conservative; it will &lt;em&gt;not&lt;/em&gt; serve stale data just because you configured a window. You must explicitly authorize it by implementing the &lt;code&gt;should_serve_stale&lt;/code&gt; method in the &lt;code&gt;ProxyHttp&lt;/code&gt; trait.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Return &lt;code&gt;true&lt;/code&gt;:&lt;/strong&gt; "Yes, I accept the risk of serving old data to keep latency low."&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Cache Locking
&lt;/h3&gt;

&lt;p&gt;SWR relies heavily on the &lt;code&gt;CacheLock&lt;/code&gt;. When the item enters the Stale Window, Pingora needs to coordinate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Thread A:&lt;/strong&gt; Gets the lock, triggers the background fetch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Thread B:&lt;/strong&gt; Sees the lock is busy, sees that stale data is available, and serves it immediately.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Code (&lt;code&gt;examples/47_stale_while_revalidate.rs&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;We configure a &lt;strong&gt;5s Fresh&lt;/strong&gt; window and a &lt;strong&gt;10s SWR&lt;/strong&gt; window. We also use a slow mock upstream (2s latency) to prove that the client does not wait during the SWR phase.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;once_cell&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Lazy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;http&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ResponseHeader&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora_cache&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;MemCache&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CacheKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CacheMetaDefaults&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RespCacheable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CachePhase&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora_cache&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;cache_control&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CacheControl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora_cache&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CacheLock&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;borrow&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Cow&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;atomic&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;AtomicUsize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Ordering&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;net&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;TcpListener&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;AsyncReadExt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AsyncWriteExt&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;MEM_CACHE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Lazy&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MemCache&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Lazy&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;MemCache&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;CACHE_LOCK&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Lazy&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CacheLock&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Lazy&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(||&lt;/span&gt; &lt;span class="nn"&gt;CacheLock&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_secs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;CONTENT_VERSION&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AtomicUsize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;AtomicUsize&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;SWRProxy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ProxyHttp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;SWRProxy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new_ctx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="c1"&gt;// 1. Enable Cache with Locking&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;request_cache_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;CacheKey&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="nf"&gt;.req_header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="py"&gt;.uri&lt;/span&gt;&lt;span class="nf"&gt;.path&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="py"&gt;.cache&lt;/span&gt;&lt;span class="nf"&gt;.enable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="o"&gt;&amp;amp;*&lt;/span&gt;&lt;span class="n"&gt;MEM_CACHE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;*&lt;/span&gt;&lt;span class="n"&gt;CACHE_LOCK&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// Lock is required for SWR coordination&lt;/span&gt;
            &lt;span class="nb"&gt;None&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="py"&gt;.cache&lt;/span&gt;&lt;span class="nf"&gt;.set_cache_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_peer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="s"&gt;"127.0.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6199&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"swr.local"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;response_cache_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;ResponseHeader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RespCacheable&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;cc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;CacheControl&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_resp_headers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// 2. Configure Defaults&lt;/span&gt;
        &lt;span class="c1"&gt;// Fresh: 5s. &lt;/span&gt;
        &lt;span class="c1"&gt;// Stale-While-Revalidate: 10s.&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;defaults&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;CacheMetaDefaults&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="nf"&gt;.as_u16&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_secs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;pingora_cache&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;resp_cacheable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;cc&lt;/span&gt;&lt;span class="nf"&gt;.as_ref&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;defaults&lt;/span&gt;
        &lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// 3. The Critical Hook: Authorize Serving Stale&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;should_serve_stale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// If error is None, it means we are revalidating (expired but clean).&lt;/span&gt;
        &lt;span class="c1"&gt;// Return true to allow SWR.&lt;/span&gt;
        &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nb"&gt;None&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;where&lt;/span&gt;
        &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Send&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Sync&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;phase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="py"&gt;.cache&lt;/span&gt;&lt;span class="nf"&gt;.phase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;phase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nn"&gt;CachePhase&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Hit&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Status: HIT (Fresh) - Instant Response"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nn"&gt;CachePhase&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Miss&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Status: MISS (Fetching) - Slow Response"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="c1"&gt;// This is the SWR State&lt;/span&gt;
            &lt;span class="nn"&gt;CachePhase&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Stale&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nn"&gt;CachePhase&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;StaleUpdating&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Status: SWR ACTIVATED (Serving Stale while Updating)"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nn"&gt;CachePhase&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Expired&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Status: EXPIRED (Too Old) - Blocking Fetch"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Status: {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;phase&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// 4. Slow Upstream (2s latency)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;run_slow_upstream&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;listener&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;TcpListener&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"127.0.0.1:6199"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Slow Upstream running on 127.0.0.1:6199"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;listener&lt;/span&gt;&lt;span class="nf"&gt;.accept&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;move&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0u8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="nf"&gt;.read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

                &lt;span class="c1"&gt;// Simulate slow backend&lt;/span&gt;
                &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_secs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CONTENT_VERSION&lt;/span&gt;&lt;span class="nf"&gt;.fetch_add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;Ordering&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;SeqCst&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Content Version {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="s"&gt;"HTTP/1.1 200 OK&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s"&gt;
                    Content-Length: {}&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s"&gt;
                    Cache-Control: public, max-age=5, stale-while-revalidate=10&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s"&gt;
                    Connection: close&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s"&gt;
                    &lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s"&gt;
                    {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                    &lt;span class="n"&gt;body&lt;/span&gt;
                &lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="nf"&gt;.write_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="nf"&gt;.as_bytes&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nn"&gt;pingora_cache&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;set_compression_dict_content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Cow&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Borrowed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;b""&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(||&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;rt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Runtime&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;rt&lt;/span&gt;&lt;span class="nf"&gt;.block_on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;run_slow_upstream&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;http_proxy_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SWRProxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="nf"&gt;.add_tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:6198"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SWR Proxy running on 0.0.0.0:6198"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_proxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;We will observe the transition from &lt;strong&gt;Miss&lt;/strong&gt; (Slow) -&amp;gt; &lt;strong&gt;Hit&lt;/strong&gt; (Fast) -&amp;gt; &lt;strong&gt;SWR&lt;/strong&gt; (Fast &amp;amp; Stale) -&amp;gt; &lt;strong&gt;Hit&lt;/strong&gt; (Fast &amp;amp; Updated).&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Start the Proxy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 47_stale_while_revalidate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Initial Fetch (Miss)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec &lt;/span&gt;pingora_client_1 curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://172.28.0.10:6198/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Time:&lt;/strong&gt; ~2 seconds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Output:&lt;/strong&gt; &lt;code&gt;Content Version 1&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Log:&lt;/strong&gt; &lt;code&gt;Status: MISS (Fetching)&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Immediate Retry (Hit)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec &lt;/span&gt;pingora_client_1 curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://172.28.0.10:6198/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Time:&lt;/strong&gt; Instant.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Output:&lt;/strong&gt; &lt;code&gt;Content Version 1&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Log:&lt;/strong&gt; &lt;code&gt;Status: HIT (Fresh)&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Trigger SWR (Wait 7s)
&lt;/h3&gt;

&lt;p&gt;The content is now 7 seconds old (Fresh limit was 5s).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sleep &lt;/span&gt;7
docker &lt;span class="nb"&gt;exec &lt;/span&gt;pingora_client_1 curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://172.28.0.10:6198/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Time:&lt;/strong&gt; &lt;strong&gt;Instant!&lt;/strong&gt; (This is the key victory).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Output:&lt;/strong&gt; &lt;code&gt;Content Version 1&lt;/code&gt; (Stale data).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Log:&lt;/strong&gt; &lt;code&gt;Status: SWR ACTIVATED&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. Verify Background Update (Wait 3s)
&lt;/h3&gt;

&lt;p&gt;Wait for the background fetch to finish, then check again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sleep &lt;/span&gt;3
docker &lt;span class="nb"&gt;exec &lt;/span&gt;pingora_client_1 curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://172.28.0.10:6198/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Time:&lt;/strong&gt; Instant.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Output:&lt;/strong&gt; &lt;code&gt;Content Version 2&lt;/code&gt; (Fresh data).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Log:&lt;/strong&gt; &lt;code&gt;Status: HIT (Fresh)&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Module 7: Production Readiness &amp;amp; Architecture
&lt;/h1&gt;

&lt;p&gt;We have spent the last six modules building individual capabilities: Load Balancing, TLS termination, Upstream Health Checks, Traffic Control, and Caching. We have a collection of powerful scripts, but we don't yet have a cohesive &lt;strong&gt;platform&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In this final module, we transition from writing "example code" to building &lt;strong&gt;production software&lt;/strong&gt;. We will refactor our logic into reusable libraries, instrument the system with industry-standard observability tools, and finally assemble every concept we've learned into a single, unified API Gateway.&lt;/p&gt;

&lt;p&gt;We will cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Observability:&lt;/strong&gt; Implementing Prometheus metrics to visualize request rates, latency, and error codes in real-time. You can't manage what you can't measure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Modularity:&lt;/strong&gt; Refactoring our monolithic &lt;code&gt;main.rs&lt;/code&gt; files into a clean, reusable &lt;code&gt;gateway&lt;/code&gt; library. This is how you structure Rust projects for scale.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Capstone:&lt;/strong&gt; We will build the &lt;strong&gt;Final API Gateway&lt;/strong&gt;. It will feature:

&lt;ul&gt;
&lt;li&gt;Zero-downtime reconfiguration.&lt;/li&gt;
&lt;li&gt;Weighted Load Balancing with Health Checks.&lt;/li&gt;
&lt;li&gt;Rate Limiting &amp;amp; Authentication.&lt;/li&gt;
&lt;li&gt;In-Memory Caching with Stale-While-Revalidate.&lt;/li&gt;
&lt;li&gt;Prometheus Metrics export.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;By the end of this module, you will have a template for a high-performance Rust proxy that is ready for deployment.&lt;/p&gt;

&lt;h1&gt;
  
  
  Lesson 48: Observability
&lt;/h1&gt;

&lt;p&gt;You cannot optimize what you cannot measure. In a production environment, knowing &lt;em&gt;that&lt;/em&gt; the server is running isn't enough; you need to know &lt;em&gt;how&lt;/em&gt; it is running. Are requests taking 10ms or 10s? Is the error rate 0.1% or 5%?&lt;/p&gt;

&lt;p&gt;In this lesson, we add &lt;strong&gt;Prometheus Metrics&lt;/strong&gt; to our proxy. We will implement two planes of operation:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Traffic Plane (Port 6199):&lt;/strong&gt; Where user traffic flows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Control Plane (Port 6200):&lt;/strong&gt; Where metrics are scraped by your monitoring system.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Key Concepts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. The "Two-Registry" Trap
&lt;/h3&gt;

&lt;p&gt;This is the most common pitfall when adding metrics to Pingora.&lt;br&gt;
Pingora uses the &lt;code&gt;prometheus&lt;/code&gt; crate internally to expose its built-in server metrics. It exposes these via &lt;code&gt;Service::prometheus_http_service()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Danger:&lt;/strong&gt; If your &lt;code&gt;Cargo.toml&lt;/code&gt; includes a version of the &lt;code&gt;prometheus&lt;/code&gt; crate that differs from the one Pingora uses, Rust may compile &lt;strong&gt;two separate copies&lt;/strong&gt; of the library.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Copy A (Pingora's):&lt;/strong&gt; Connected to the HTTP endpoint on port 6200.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Copy B (Yours):&lt;/strong&gt; Where you register your custom counters.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The Result:&lt;/strong&gt; You register metrics, but the endpoint returns nothing.&lt;br&gt;
&lt;strong&gt;The Fix:&lt;/strong&gt; Always check which version Pingora depends on:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cargo tree &lt;span class="nt"&gt;-p&lt;/span&gt; pingora | &lt;span class="nb"&gt;grep &lt;/span&gt;prometheus
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ensure your &lt;code&gt;Cargo.toml&lt;/code&gt; matches this version exactly.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The &lt;code&gt;CTX&lt;/code&gt; Timer
&lt;/h3&gt;

&lt;p&gt;To measure latency, we need state that persists from the &lt;em&gt;start&lt;/em&gt; of the request to the &lt;em&gt;end&lt;/em&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;new_ctx&lt;/code&gt;:&lt;/strong&gt; Runs when the request starts. We save &lt;code&gt;Instant::now()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;logging&lt;/code&gt;:&lt;/strong&gt; Runs when the request finishes (even if it failed). We calculate &lt;code&gt;elapsed()&lt;/code&gt; and update the Histogram.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Code (&lt;code&gt;examples/48_observability.rs&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;We simulate two upstreams: "Blue" (Fast, ~10ms) and "Green" (Slow, ~100ms) to generate interesting histogram data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;async_trait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;server&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="nn"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;upstreams&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;pingora&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;services&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;listening&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;prometheus&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;register_histogram&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;register_int_counter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Histogram&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IntCounter&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;atomic&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;AtomicUsize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Ordering&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Instant&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;net&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;TcpListener&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;AsyncReadExt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AsyncWriteExt&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;REQUEST_COUNTER&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AtomicUsize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;AtomicUsize&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// 1. Context to track timing per-request&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;MetricsContext&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;start_time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Instant&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// 2. Struct holding our metric handles&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;ObservabilityProxy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;req_counter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IntCounter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;req_histogram&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Histogram&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[async_trait]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ProxyHttp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;ObservabilityProxy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MetricsContext&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new_ctx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;MetricsContext&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;start_time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;Instant&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;upstream_peer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpPeer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Round Robin between Blue (Fast) and Green (Slow)&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;REQUEST_COUNTER&lt;/span&gt;&lt;span class="nf"&gt;.fetch_add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;Ordering&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Relaxed&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"127.0.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6201&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Blue"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"127.0.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6202&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Green"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Forwarding request #{} to {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;HttpPeer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"metrics.local"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// 3. Record Metrics on Completion&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;where&lt;/span&gt;
        &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTX&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Send&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Sync&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;duration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.start_time&lt;/span&gt;&lt;span class="nf"&gt;.elapsed&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;duration_secs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="nf"&gt;.as_secs_f64&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// Update Prometheus Registries&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.req_counter&lt;/span&gt;&lt;span class="nf"&gt;.inc&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.req_histogram&lt;/span&gt;&lt;span class="nf"&gt;.observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;duration_secs&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"Request finished. Path: {}, Latency: {:.4}s, Total Request: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="nf"&gt;.req_header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="py"&gt;.uri&lt;/span&gt;&lt;span class="nf"&gt;.path&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;duration_secs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.req_counter&lt;/span&gt;&lt;span class="nf"&gt;.get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Mock Upstream Helper (Blue/Green)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;run_mock_upstream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;delay_ms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;listener&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;TcpListener&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"127.0.0.1:{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{} Upstream started on port {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;listener&lt;/span&gt;&lt;span class="nf"&gt;.accept&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;move&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0u8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="nf"&gt;.read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_millis&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delay_ms&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Response from {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="s"&gt;"HTTP/1.1 200 OK&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s"&gt;Content-Length: {}&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s"&gt;Connection: close&lt;/span&gt;&lt;span class="se"&gt;\r\n\r\n&lt;/span&gt;&lt;span class="s"&gt;{}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;
                &lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="nf"&gt;.write_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="nf"&gt;.as_bytes&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;env_logger&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Opt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;my_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.bootstrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Start background mocks&lt;/span&gt;
    &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(||&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;rt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Runtime&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;rt&lt;/span&gt;&lt;span class="nf"&gt;.block_on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;join!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nf"&gt;run_mock_upstream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6201&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Blue"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="nf"&gt;run_mock_upstream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6202&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Green"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// 4. Initialize and Register Metrics&lt;/span&gt;
    &lt;span class="c1"&gt;// We register them once here. The `ObservabilityProxy` will hold the handles.&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ObservabilityProxy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;req_counter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nd"&gt;register_int_counter!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"http_requests_total"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"Total number of HTTP requests processed."&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;req_histogram&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nd"&gt;register_histogram!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"http_request_duration_seconds"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"The HTTP request latency in seconds."&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;proxy_service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;http_proxy_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="py"&gt;.configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;proxy_service&lt;/span&gt;&lt;span class="nf"&gt;.add_tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:6199"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;proxy_service&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 5. Add the Prometheus Endpoint Service&lt;/span&gt;
    &lt;span class="c1"&gt;// This exposes the /metrics endpoint on port 6200&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;prometheus_service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;prometheus_http_service&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;prometheus_service&lt;/span&gt;&lt;span class="nf"&gt;.add_tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0:6200"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.add_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prometheus_service&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Proxy running on 6199, Metrics on 6200"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;my_server&lt;/span&gt;&lt;span class="nf"&gt;.run_forever&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;We will verify that traffic on the proxy port generates data on the metrics port.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Start the Proxy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RUST_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info cargo run &lt;span class="nt"&gt;--example&lt;/span&gt; 48_observability
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Check Empty Metrics
&lt;/h3&gt;

&lt;p&gt;Before sending traffic, ask Prometheus what it sees.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec &lt;/span&gt;pingora_client_1 curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://172.28.0.10:6200/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Result:&lt;/strong&gt; You should see the HELP and TYPE lines, but the counter should be &lt;code&gt;0&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Generate Traffic
&lt;/h3&gt;

&lt;p&gt;Send 3 requests. 2 will go to Blue (Fast), 1 to Green (Slow).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec &lt;/span&gt;pingora_client_1 curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://172.28.0.10:6199/
docker &lt;span class="nb"&gt;exec &lt;/span&gt;pingora_client_1 curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://172.28.0.10:6199/
docker &lt;span class="nb"&gt;exec &lt;/span&gt;pingora_client_1 curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://172.28.0.10:6199/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Verify Metrics
&lt;/h3&gt;

&lt;p&gt;Check the metrics endpoint again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec &lt;/span&gt;pingora_client_1 curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://172.28.0.10:6200/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Analyze the Output:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;http_requests_total 3&lt;/code&gt;: Confirms 3 requests were tracked.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Histograms:&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;http_request_duration_seconds_bucket{le="0.025"} 2&lt;/code&gt;: Two requests (Blue) finished in under 25ms.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;http_request_duration_seconds_bucket{le="0.25"} 3&lt;/code&gt;: All three requests finished in under 250ms (since Green takes ~100ms).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This confirms we have granular visibility into the performance of our upstreams.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>programming</category>
      <category>tutorial</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Vanilla HTML, CSS, JavaScript Markdown, Syntax Highlighting, Mathjax Article Viewer</title>
      <dc:creator>Warren Jitsing</dc:creator>
      <pubDate>Mon, 05 Jan 2026 07:56:52 +0000</pubDate>
      <link>https://dev.to/warren_jitsing_dd1c1d6fc6/vanilla-html-css-javascript-markdown-syntax-highlighting-mathjax-article-viewer-3f3e</link>
      <guid>https://dev.to/warren_jitsing_dd1c1d6fc6/vanilla-html-css-javascript-markdown-syntax-highlighting-mathjax-article-viewer-3f3e</guid>
      <description>&lt;p&gt;GitHub: &lt;a href="https://github.com/InfiniteConsult/0003_html_css_javascript_article_viewer/" rel="noopener noreferrer"&gt;https://github.com/InfiniteConsult/0003_html_css_javascript_article_viewer/&lt;/a&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;Writing deeply technical content for general-purpose platforms often feels like a compromise. Code snippets become unreadable blocks of poorly formatted text, complex mathematical equations are impossible to typeset, and the overall presentation can lack the clarity that the subject matter deserves. These limitations aren't just an inconvenience; they are a barrier to effective communication.&lt;/p&gt;

&lt;p&gt;This article rejects that compromise. Our goal is to build a high-quality, self-hosted, single-page application from first principles, giving us complete control over the reading experience. We will construct a professional-grade article viewer that renders Markdown, code, and mathematics exactly as they were intended to be seen.&lt;/p&gt;

&lt;p&gt;To achieve this, we will use a carefully selected, modern toolchain. For the backend, we'll use &lt;strong&gt;FastAPI&lt;/strong&gt;, a high-performance Python framework perfect for building robust and efficient APIs. For the frontend, we will use &lt;strong&gt;vanilla HTML, CSS, and JavaScript&lt;/strong&gt;. This framework-free approach will allow us to create a lean, fast, and universally understandable user interface while focusing on the fundamental building blocks of the web.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Not Use an Existing Tool?
&lt;/h2&gt;

&lt;p&gt;Before building a system from scratch, it's worth asking if a tool already exists for the job. The closest solutions are &lt;strong&gt;static site generators&lt;/strong&gt;, and for a Python developer, the most well-known is &lt;strong&gt;Sphinx&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;While Sphinx is the gold standard for Python project documentation, it's a heavyweight tool designed primarily for reStructuredText (rST). For our purposes, it presents a few challenges: rendering from Markdown requires extensions like MyST, and configuring beautiful math typesetting can be complex. Other excellent tools like &lt;strong&gt;MkDocs&lt;/strong&gt;, &lt;strong&gt;Jekyll&lt;/strong&gt;, or the incredibly fast &lt;strong&gt;Hugo&lt;/strong&gt; are Markdown-native but still introduce their own build steps, plugins, and thematic abstractions.&lt;/p&gt;

&lt;p&gt;The goal of this article is not just to create a viewer, but to understand &lt;em&gt;how&lt;/em&gt; such a system works from first principles. By building our own, we gain complete control and deconstruct the fundamental mechanics of a modern, API-driven web application, a skill that is far more valuable than learning the configuration for any single tool.&lt;/p&gt;

&lt;h1&gt;
  
  
  Foundations
&lt;/h1&gt;

&lt;h2&gt;
  
  
  The Anatomy of a Modern Web Application
&lt;/h2&gt;

&lt;p&gt;Before we write a single line of code, it's essential to understand the architectural patterns that govern modern web applications. Our viewer is built on three fundamental concepts: the client-server model, the single-page application, and the core technologies of the frontend.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Client-Server Model
&lt;/h3&gt;

&lt;p&gt;At its heart, our application uses a &lt;strong&gt;client-server model&lt;/strong&gt;. The easiest way to visualize this is with an analogy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;backend&lt;/strong&gt; (our FastAPI server) is the &lt;strong&gt;librarian&lt;/strong&gt;. It sits in a vast library, managing the entire collection of books (the articles). The librarian doesn't care how the books are read; its sole job is to protect the collection, know where every book is, and provide a specific one when an authorized request is made.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;frontend&lt;/strong&gt; (our browser application) is the &lt;strong&gt;reading room&lt;/strong&gt;. It has the tables, chairs, and lights—the entire user interface—but it has no books of its own. To show something to the user, it must send a request to the librarian.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This separation is powerful. It allows the backend to focus purely on data management and business logic, while the frontend is free to focus entirely on creating the best possible user experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Single-Page Application (SPA)
&lt;/h3&gt;

&lt;p&gt;In a traditional website, clicking a link causes the browser to request an entirely new HTML page from the server, resulting in a full, disruptive page reload. Our viewer, however, is a &lt;strong&gt;Single-Page Application (SPA)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This means the user loads a single, lightweight HTML "shell" only once. From that point on, JavaScript takes over. When you click on a different article, the JavaScript code intercepts the action. Instead of requesting a whole new page, it makes a quiet, background data request to our backend API. When the new content arrives, the JavaScript dynamically rewrites the relevant parts of the current page. The result is a fast, fluid experience with no jarring reloads, feeling more like a native desktop application than a traditional website.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Frontend Trinity: HTML, CSS, and JavaScript
&lt;/h3&gt;

&lt;p&gt;The entire user interface—our "reading room"—is constructed from three core technologies. The best way to understand their distinct roles is with the "House Analogy."&lt;/p&gt;

&lt;h4&gt;
  
  
  HTML: The Structure &amp;amp; Framing
&lt;/h4&gt;

&lt;p&gt;HTML (HyperText Markup Language) is the &lt;strong&gt;blueprint and the physical structure&lt;/strong&gt; of the house. It defines all the essential components and their hierarchy: the foundation, the walls that form the rooms, the ceilings, the doorways, and the window openings. It’s the raw, unadorned structure—the nouns of the house. Without it, there's nothing to see or interact with.&lt;/p&gt;

&lt;h4&gt;
  
  
  CSS: The Presentation &amp;amp; Styling
&lt;/h4&gt;

&lt;p&gt;CSS (Cascading Style Sheets) is the &lt;strong&gt;interior and exterior design&lt;/strong&gt;. It’s the paint on the walls, the type of flooring, the style of the window frames, the furniture, the light fixtures, and the landscaping outside. It makes the structure visually appealing but doesn't change what the structure &lt;em&gt;is&lt;/em&gt;. You can completely redecorate (change the CSS) without knocking down any walls (changing the HTML).&lt;/p&gt;

&lt;h4&gt;
  
  
  JavaScript: The Interactivity &amp;amp; Utilities
&lt;/h4&gt;

&lt;p&gt;JavaScript is the &lt;strong&gt;electricity, plumbing, and appliances&lt;/strong&gt;. It’s what makes the house functional and interactive. It’s the light switches, the running water, the garage door opener, the security system, and the dishwasher. It allows the inhabitants to &lt;em&gt;do things&lt;/em&gt; that change the state of the house—opening doors, turning on lights, and making things happen in response to an action. It's the verbs that bring the house to life.&lt;/p&gt;

&lt;h2&gt;
  
  
  Digging Deeper into the Frontend
&lt;/h2&gt;

&lt;h3&gt;
  
  
  HTML: The Vocabulary of Structure
&lt;/h3&gt;

&lt;p&gt;HTML tags are the vocabulary we use to describe the structure and meaning of a document. If HTML is the blueprint for our house, these tags are the specific labels for components like "wall," "window," and "door." They give the browser the instructions it needs to assemble the page correctly.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Document Shell
&lt;/h4&gt;

&lt;p&gt;Every HTML document has a fundamental structure.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt;&lt;/strong&gt;: The root of the entire document. It's the plot of land upon which our entire house is built.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt;&lt;/strong&gt;: This tag contains all the content that is visible to the user—the text, images, and links. In our house analogy, this is the entire physical house that sits on the plot of land.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Metadata and External Resources
&lt;/h4&gt;

&lt;p&gt;These tags typically live inside a &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; tag (which is implicitly present) and provide information &lt;em&gt;about&lt;/em&gt; the page or link to external files. They are like the house's blueprints, address, and utility connections—essential for the house to function but not part of the visible structure itself.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;&amp;lt;meta&amp;gt;&lt;/code&gt;&lt;/strong&gt;: Provides metadata about the page, like its character encoding (&lt;code&gt;charset="UTF-8"&lt;/code&gt;) and how it should be displayed on mobile devices (&lt;code&gt;viewport&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt;&lt;/strong&gt;: Sets the title of the browser tab. It's the name on the mailbox.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt;&lt;/strong&gt;: Connects external resources. In our project, we use it exclusively to link our CSS stylesheet—the "interior design" plans for our house.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;&lt;/strong&gt;: Loads and executes scripts. We use this to bring in our "electricity and plumbing"—the external JavaScript libraries (&lt;code&gt;marked.js&lt;/code&gt;, &lt;code&gt;highlight.js&lt;/code&gt;) and our own &lt;code&gt;app.js&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Semantic Sections
&lt;/h4&gt;

&lt;p&gt;These tags define the major, meaningful regions of our user interface, much like naming the rooms in a house.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;&amp;lt;nav&amp;gt;&lt;/code&gt;&lt;/strong&gt;: Represents a section dedicated to navigation. In our viewer, it's the sidebar containing the list of articles. It's the "hallway" that connects to all the other rooms.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt;&lt;/strong&gt;: Represents the primary, dominant content of the page. For us, this is the area where the rendered article is displayed. It's the "living room" of our house.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Text and Content Structuring
&lt;/h4&gt;

&lt;p&gt;These are the block-level tags used to organize the text and content that the user reads.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;h2&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;h3&amp;gt;&lt;/code&gt;&lt;/strong&gt;: Heading tags. They create a document outline and a visual hierarchy, acting as the chapter titles and section headings of a book.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt;&lt;/strong&gt;: The paragraph tag, used for standard blocks of text.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;&amp;lt;ol&amp;gt;&lt;/code&gt;&lt;/strong&gt;, and &lt;strong&gt;&lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt;&lt;/strong&gt;: Used to create lists. &lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt; is an &lt;strong&gt;u&lt;/strong&gt;nordered &lt;strong&gt;l&lt;/strong&gt;ist (bullet points), while &lt;code&gt;&amp;lt;ol&amp;gt;&lt;/code&gt; is an &lt;strong&gt;o&lt;/strong&gt;rdered &lt;strong&gt;l&lt;/strong&gt;ist (numbers). In both cases, &lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt; represents a single &lt;strong&gt;l&lt;/strong&gt;ist &lt;strong&gt;i&lt;/strong&gt;tem.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;&amp;lt;hr&amp;gt;&lt;/code&gt;&lt;/strong&gt;: A &lt;strong&gt;h&lt;/strong&gt;orizontal &lt;strong&gt;r&lt;/strong&gt;ule, used to create a thematic break or a dividing line between sections of content.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Code and Preformatted Text
&lt;/h4&gt;

&lt;p&gt;These tags are specialized for displaying source code.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;&amp;lt;code&amp;gt;&lt;/code&gt;&lt;/strong&gt;: An inline tag that semantically marks a piece of text as being a snippet of computer code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;&amp;lt;pre&amp;gt;&lt;/code&gt;&lt;/strong&gt;: A block-level tag that represents &lt;strong&gt;pre&lt;/strong&gt;formatted text. The browser will render the content inside it exactly as it is written, preserving all whitespace, line breaks, and indentation. The combination &lt;code&gt;&amp;lt;pre&amp;gt;&amp;lt;code&amp;gt;...&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;&lt;/code&gt; is the standard way to display a block of code.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Generic Containers
&lt;/h4&gt;

&lt;p&gt;Sometimes, we need to group elements for styling or layout purposes without implying any specific meaning.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;&lt;/strong&gt;: The generic block-level container. It's an "empty box" used to group larger sections of content. Our two-column layout is created by placing the &lt;code&gt;&amp;lt;nav&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt; elements inside a parent &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt;&lt;/strong&gt;: The generic inline container. It's used to wrap a small piece of text &lt;em&gt;within&lt;/em&gt; a larger block (like a paragraph) to apply specific styling, without creating a line break. It's like using a highlighter on a single word in a sentence.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  CSS: The Language of Presentation
&lt;/h3&gt;

&lt;p&gt;If HTML provides the structure of our house, CSS provides the interior and exterior design. It's a set of rules that tells the browser how every element—from the largest &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; to the smallest &lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt;—should look. A CSS rule has two main parts: a &lt;strong&gt;selector&lt;/strong&gt; to target an element, and a set of &lt;strong&gt;declarations&lt;/strong&gt; (properties and their values) to style it.&lt;/p&gt;

&lt;h4&gt;
  
  
  Selectors: Targeting the Right Elements
&lt;/h4&gt;

&lt;p&gt;Before you can style something, you must select it. We've used simple selectors like tag names (&lt;code&gt;body&lt;/code&gt;), IDs (&lt;code&gt;#content&lt;/code&gt;), and classes (&lt;code&gt;.container&lt;/code&gt;), but CSS also provides powerful "pseudo-selectors" that target elements based on their state or position.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;:hover&lt;/code&gt; (Pseudo-class)&lt;/strong&gt;: This applies styles only when the user's cursor is over an element. It's the primary way to create interactive feedback, like changing a link's color when you mouse over it.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--link-hover-color&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;:nth-child(n)&lt;/code&gt; (Pseudo-selector)&lt;/strong&gt;: This selects elements based on their order within a parent. It's incredibly powerful for styling lists or tables without adding extra classes. For example, you could style every even-numbered list item:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="nd"&gt;:nth-child&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;even&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#333&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  The Box Model: The Foundation of All Layout
&lt;/h4&gt;

&lt;p&gt;Every single element on a webpage is a rectangular box. The &lt;strong&gt;CSS Box Model&lt;/strong&gt; is the rule that governs how the size of that box and the space around it are calculated. Understanding this is the key to mastering CSS layout.&lt;/p&gt;

&lt;p&gt;Imagine a framed picture on a wall. The box is made of four layers, from the inside out:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Content&lt;/strong&gt;: The actual picture (or text/image). Its size is controlled by &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;padding&lt;/code&gt;&lt;/strong&gt;: The transparent space between the content and its border. This is the matting inside the picture frame. We use properties like &lt;code&gt;padding&lt;/code&gt;, &lt;code&gt;padding-top&lt;/code&gt;, &lt;code&gt;padding-left&lt;/code&gt;, etc.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;border&lt;/code&gt;&lt;/strong&gt;: A line that goes around the padding. This is the physical picture frame itself. We use properties like &lt;code&gt;border&lt;/code&gt;, &lt;code&gt;border-left&lt;/code&gt;, &lt;code&gt;border-radius&lt;/code&gt;, etc.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;margin&lt;/code&gt;&lt;/strong&gt;: The transparent space &lt;em&gt;outside&lt;/em&gt; the border. This is what pushes other elements away. It's the space between this picture frame and the other frames on the wall. We use properties like &lt;code&gt;margin&lt;/code&gt;, &lt;code&gt;margin-top&lt;/code&gt;, etc.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Custom Properties (CSS Variables)
&lt;/h4&gt;

&lt;p&gt;CSS Variables are a modern feature that allows us to define reusable values, which is essential for creating clean and maintainable stylesheets. In our &lt;code&gt;style.css&lt;/code&gt;, we define them in the &lt;code&gt;:root&lt;/code&gt; selector, making them globally available.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Defining a variable&lt;/strong&gt;: You use a double-dash prefix, like &lt;code&gt;--bg-color: #282c34;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Using a variable&lt;/strong&gt;: You use the &lt;code&gt;var()&lt;/code&gt; function, like &lt;code&gt;background-color: var(--bg-color);&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The primary benefit is theming. If we want to change the entire site's color scheme, we only need to edit the values in the &lt;code&gt;:root&lt;/code&gt; block, and the changes will apply everywhere the variables are used.&lt;/p&gt;

&lt;h4&gt;
  
  
  Typography: Styling the Text
&lt;/h4&gt;

&lt;p&gt;Typography is the art of arranging type to make written language legible, readable, and appealing when displayed. These CSS properties give you full control over the appearance of your text.&lt;/p&gt;

&lt;h4&gt;
  
  
  Core Font Properties
&lt;/h4&gt;

&lt;p&gt;These properties define the fundamental characteristics of the font itself.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;font-family&lt;/code&gt;&lt;/strong&gt;: Sets the typeface for the text (e.g., &lt;code&gt;Arial&lt;/code&gt;, &lt;code&gt;Times New Roman&lt;/code&gt;). It's best practice to provide a comma-separated list, known as a "font stack." The browser will try the first font, and if it's not available, it will fall back to the next one in the list.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;font-size&lt;/code&gt;&lt;/strong&gt;: Controls the size of the text, typically set in pixels (&lt;code&gt;px&lt;/code&gt;), ems (&lt;code&gt;em&lt;/code&gt;), or rems (&lt;code&gt;rem&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;font-weight&lt;/code&gt;&lt;/strong&gt;: Controls the thickness of the font characters. Common values are &lt;code&gt;normal&lt;/code&gt;, &lt;code&gt;bold&lt;/code&gt;, or numeric values like &lt;code&gt;400&lt;/code&gt; (normal) and &lt;code&gt;700&lt;/code&gt; (bold).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;font-style&lt;/code&gt;&lt;/strong&gt;: Used to set text to &lt;code&gt;italic&lt;/code&gt; or &lt;code&gt;oblique&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;color&lt;/code&gt;&lt;/strong&gt;: Sets the color of the text itself.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Paragraph and Spacing Properties
&lt;/h4&gt;

&lt;p&gt;These properties control the arrangement and spacing of text within its container.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;line-height&lt;/code&gt;&lt;/strong&gt;: Controls the vertical distance between lines of text. A value of around &lt;code&gt;1.5&lt;/code&gt; or &lt;code&gt;1.6&lt;/code&gt; (meaning 1.5 times the font size) is generally considered optimal for readability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;text-align&lt;/code&gt;&lt;/strong&gt;: Sets the horizontal alignment of text. Common values are &lt;code&gt;left&lt;/code&gt;, &lt;code&gt;right&lt;/code&gt;, &lt;code&gt;center&lt;/code&gt;, and &lt;code&gt;justify&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;letter-spacing&lt;/code&gt;&lt;/strong&gt;: Adjusts the space between individual characters.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;word-spacing&lt;/code&gt;&lt;/strong&gt;: Adjusts the space between whole words.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;text-indent&lt;/code&gt;&lt;/strong&gt;: Indents the first line of text in a block element.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Text Decoration and Transformation
&lt;/h4&gt;

&lt;p&gt;These properties apply decorative effects to the text.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;text-decoration&lt;/code&gt;&lt;/strong&gt;: Adds a decorative line to text. It's most commonly used for &lt;code&gt;underline&lt;/code&gt;, but can also be &lt;code&gt;overline&lt;/code&gt; or &lt;code&gt;line-through&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;text-transform&lt;/code&gt;&lt;/strong&gt;: Changes the capitalization of text without altering the source HTML. Values include &lt;code&gt;uppercase&lt;/code&gt;, &lt;code&gt;lowercase&lt;/code&gt;, and &lt;code&gt;capitalize&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Layout and Positioning: Arranging the Boxes
&lt;/h4&gt;

&lt;p&gt;These properties are the tools of the architect, used to define the floor plan of our webpage. They control how elements flow, where they are placed, and how they interact with each other.&lt;/p&gt;

&lt;h4&gt;
  
  
  The &lt;code&gt;display&lt;/code&gt; Property
&lt;/h4&gt;

&lt;p&gt;This is the most fundamental layout property. It dictates how an element should behave and how it interacts with the elements around it. The most common values are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;block&lt;/code&gt;&lt;/strong&gt;: The element starts on a new line and takes up the full width available. Our &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt;, and &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; tags are block-level by default.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;inline&lt;/code&gt;&lt;/strong&gt;: The element sits within the flow of text and does not start on a new line. &lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt;, and &lt;code&gt;&amp;lt;code&amp;gt;&lt;/code&gt; are inline elements.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;grid&lt;/code&gt;&lt;/strong&gt;: This turns an element into a grid container, allowing you to create complex, two-dimensional layouts. We use this on our main &lt;code&gt;.container&lt;/code&gt; to create the sidebar and content areas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;flex&lt;/code&gt;&lt;/strong&gt;: Another powerful value that turns an element into a "flexbox" container, designed for creating flexible, one-dimensional layouts.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  CSS Grid
&lt;/h4&gt;

&lt;p&gt;We use CSS Grid for our main page layout. When you set &lt;code&gt;display: grid&lt;/code&gt; on a container, you unlock a suite of properties to control its children.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;grid-template-columns&lt;/code&gt;&lt;/strong&gt;: This property defines the number and width of the columns in the grid. In our stylesheet, &lt;code&gt;grid-template-columns: 280px 1fr;&lt;/code&gt; creates two columns: the first is a fixed &lt;code&gt;280px&lt;/code&gt; wide, and the second (&lt;code&gt;1fr&lt;/code&gt;) takes up the remaining "one fraction" of the available space.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  The &lt;code&gt;position&lt;/code&gt; Property
&lt;/h4&gt;

&lt;p&gt;By default, all elements are &lt;code&gt;position: static&lt;/code&gt;, meaning they sit in the normal document flow. The &lt;code&gt;position&lt;/code&gt; property allows you to change this behavior.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;relative&lt;/code&gt;&lt;/strong&gt;: The element is positioned &lt;em&gt;relative to its normal position&lt;/em&gt;. You can then use &lt;code&gt;top&lt;/code&gt;, &lt;code&gt;left&lt;/code&gt;, etc., to shift it without affecting the layout of other elements.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;absolute&lt;/code&gt;&lt;/strong&gt;: The element is completely removed from the normal flow and is positioned &lt;em&gt;relative to its nearest positioned ancestor&lt;/em&gt;. This is the key to creating overlays, tooltips, and pop-ups.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;fixed&lt;/code&gt;&lt;/strong&gt;: The element is positioned &lt;em&gt;relative to the browser viewport&lt;/em&gt;. It will stay in the same place even when the user scrolls the page, which is perfect for fixed headers or "Back to Top" buttons.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;top&lt;/code&gt;, &lt;code&gt;right&lt;/code&gt;, &lt;code&gt;bottom&lt;/code&gt;, &lt;code&gt;left&lt;/code&gt;&lt;/strong&gt;: These properties are used to specify the exact coordinates for any element with a position other than &lt;code&gt;static&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;z-index&lt;/code&gt;&lt;/strong&gt;: When elements overlap, &lt;code&gt;z-index&lt;/code&gt; controls their stacking order (which one is on top). A higher &lt;code&gt;z-index&lt;/code&gt; value brings an element closer to the viewer. It only works on positioned elements.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Visibility
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;visibility&lt;/code&gt;&lt;/strong&gt;: Controls whether an element is visible. The value &lt;code&gt;hidden&lt;/code&gt; makes the element invisible, but it still occupies its space in the layout. This is different from &lt;code&gt;display: none&lt;/code&gt;, which completely removes the element from the page.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Historical Context: Older Layout Methods
&lt;/h4&gt;

&lt;p&gt;Before Grid and Flexbox, layouts were built with clever workarounds. You will still encounter these in older code.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;float&lt;/code&gt;&lt;/strong&gt;: With values &lt;code&gt;left&lt;/code&gt; or &lt;code&gt;right&lt;/code&gt;, this property was used to push an element to one side of its container, allowing text and other elements to wrap around it. It was the primary method for creating columns for many years.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;clear&lt;/code&gt;&lt;/strong&gt;: This property was used to control the behavior of floats, forcing an element to appear "clear" of (below) any floated elements above it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Visual Effects and Advanced Topics
&lt;/h4&gt;

&lt;p&gt;These properties control the finer details of an element's appearance, interactivity, and how the browser should handle it.&lt;/p&gt;

&lt;h4&gt;
  
  
  Backgrounds, Borders, and Shadows
&lt;/h4&gt;

&lt;p&gt;These properties style the "surface" of an element's box.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;background-color&lt;/code&gt;&lt;/strong&gt;: Sets a solid background color for an element.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;background&lt;/code&gt;&lt;/strong&gt;: A shorthand property that allows you to set all background styles (like &lt;code&gt;background-color&lt;/code&gt;, &lt;code&gt;background-image&lt;/code&gt;, etc.) in a single declaration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;border-radius&lt;/code&gt;&lt;/strong&gt;: Used to round the corners of an element's border. A value of &lt;code&gt;50%&lt;/code&gt; on a square element will turn it into a circle.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;box-shadow&lt;/code&gt;&lt;/strong&gt;: Adds shadow effects around an element's frame, which can be used to create a sense of depth or to highlight an element.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Overflow and Clipping
&lt;/h4&gt;

&lt;p&gt;These properties control what happens when an element's content is larger than its container.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;overflow&lt;/code&gt;&lt;/strong&gt;: Defines the behavior for overflowing content. The main values are:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;visible&lt;/code&gt;: The default. The content overflows the box and is visible.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;hidden&lt;/code&gt;: The content is clipped, and the overflowing part is invisible.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;scroll&lt;/code&gt;: The content is clipped, and scrollbars are always visible, allowing the user to see the rest of the content.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;auto&lt;/code&gt;: The browser decides. Scrollbars are only added if the content actually overflows.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;&lt;code&gt;overflow-x&lt;/code&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;code&gt;overflow-y&lt;/code&gt;&lt;/strong&gt;: Allow you to control overflow behavior independently for the horizontal and vertical axes.&lt;/li&gt;

&lt;/ul&gt;

&lt;h4&gt;
  
  
  Transitions and Animations
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;transition&lt;/code&gt;&lt;/strong&gt;: Applies a smooth animation to a property change over a given duration. For example, instead of a link's color changing instantly on hover, a &lt;code&gt;transition: color 0.2s;&lt;/code&gt; will make it fade smoothly between the two colors over 0.2 seconds.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Interactivity and User Experience
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;cursor&lt;/code&gt;&lt;/strong&gt;: Controls the appearance of the mouse pointer when it is over an element. Common values include &lt;code&gt;pointer&lt;/code&gt; (a hand), &lt;code&gt;text&lt;/code&gt; (an I-beam), and &lt;code&gt;wait&lt;/code&gt; (a loading icon).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;user-select&lt;/code&gt;&lt;/strong&gt;: Controls whether the user can select or highlight the text in an element. Setting it to &lt;code&gt;none&lt;/code&gt; can be useful for interactive UI elements.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;outline&lt;/code&gt;&lt;/strong&gt;: An outline is a line drawn &lt;em&gt;outside&lt;/em&gt; an element's border. It is most often used to show that an element (like a button or link) has keyboard focus, which is a critical accessibility feature.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  A Note on Vendor Prefixes
&lt;/h4&gt;

&lt;p&gt;There are several properties with prefixes that you may notice from inspection of the rendered webpage code like &lt;strong&gt;&lt;code&gt;-webkit-&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;-moz-&lt;/code&gt;&lt;/strong&gt;, and &lt;strong&gt;&lt;code&gt;-ms-&lt;/code&gt;&lt;/strong&gt;. These are known as &lt;strong&gt;vendor prefixes&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Years ago, browser makers (like Chrome/Safari using WebKit, Mozilla using Gecko) used these prefixes to implement experimental CSS features before they were officially standardized. For example, to use &lt;code&gt;border-radius&lt;/code&gt;, you once had to write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;-webkit-border-radius&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;10&lt;/span&gt;&lt;span class="nt"&gt;px&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;-moz-border-radius&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;10&lt;/span&gt;&lt;span class="nt"&gt;px&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;border-radius&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;10&lt;/span&gt;&lt;span class="nt"&gt;px&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;For modern web development, vendor prefixes are rarely needed.&lt;/strong&gt; Properties like &lt;code&gt;border-radius&lt;/code&gt;, &lt;code&gt;box-shadow&lt;/code&gt;, and &lt;code&gt;transition&lt;/code&gt; have long been standardized and are supported prefix-free in all current browsers. They are important to recognize when working on older codebases, but you should avoid using them for new projects unless you are using a truly cutting-edge, experimental feature.&lt;/p&gt;

&lt;h3&gt;
  
  
  JavaScript: Adding Interactivity
&lt;/h3&gt;

&lt;p&gt;If HTML provides the structure and CSS the styling, JavaScript provides the &lt;strong&gt;interactivity&lt;/strong&gt;. It is the engine that transforms our static document into a dynamic, responsive application. It handles user input, fetches data from our server, and updates the page in real-time.&lt;/p&gt;

&lt;p&gt;While the JavaScript language is vast, our approach will be practical and focused. Rather than creating an exhaustive reference, the following five parts will deconstruct the specific concepts used to build our article viewer. We will journey from the fundamental grammar of the language to the powerful asynchronous patterns that define modern web applications, giving you a complete, first-principles understanding of how our code works.&lt;/p&gt;

&lt;h4&gt;
  
  
  Part 1: JavaScript First Principles: The Core Language
&lt;/h4&gt;

&lt;p&gt;Before we can make a webpage interactive, we must first understand the fundamental grammar and building blocks of the JavaScript language itself. This section covers the core components required to write any basic script, focusing on how we define variables, what types of data they can hold, and how we can use functions and logic to manipulate them.&lt;/p&gt;

&lt;h5&gt;
  
  
  Variables and Constants
&lt;/h5&gt;

&lt;p&gt;A variable is a named container for storing a value that can be used and changed throughout a program. In modern JavaScript, we primarily use &lt;code&gt;const&lt;/code&gt; to declare variables.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;const&lt;/code&gt;&lt;/strong&gt;: Declares a &lt;strong&gt;constant&lt;/strong&gt;, which is a variable whose value cannot be reassigned after it is first defined. This is a best practice for values that we don't intend to change, as it prevents accidental modifications and makes the code's intent clearer. In our &lt;code&gt;app.js&lt;/code&gt;, we use it to store references to our HTML elements, which will not change:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sidebar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;article-list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  Data Types
&lt;/h5&gt;

&lt;p&gt;Every value in JavaScript has a type. The basic "primitive" types include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;string&lt;/code&gt;: For text, enclosed in quotes (e.g., &lt;code&gt;'Hello World'&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;number&lt;/code&gt;: For all numbers, including integers and decimals (e.g., &lt;code&gt;42&lt;/code&gt;, &lt;code&gt;3.14&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;boolean&lt;/code&gt;: Represents a logical value of either &lt;code&gt;true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt;.
The &lt;code&gt;typeof&lt;/code&gt; operator can be used to check a variable's type.&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  Objects and Arrays: Storing Collections of Data
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Objects&lt;/strong&gt;: An object is a collection of related data stored in &lt;code&gt;key: value&lt;/code&gt; pairs. It's like a dictionary or a file cabinet drawer. In our code, each article's data is an object.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// A conceptual article object&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0001_basic_fastapi&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Basic Fastapi&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;We access the data inside an object using dot notation, such as &lt;code&gt;article.slug&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Arrays&lt;/strong&gt;: An array is an ordered list of values. The list of articles we receive from our API is an array.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// An array of article objects&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;...&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}, {slug: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;...&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;, title: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;...&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;} ]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  Control Flow: Making Decisions
&lt;/h5&gt;

&lt;p&gt;Control flow statements allow us to make decisions in our code.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;if&lt;/code&gt; statements&lt;/strong&gt;: The most common control flow statement. It executes a block of code only if a specified condition evaluates to &lt;code&gt;true&lt;/code&gt;. In our viewer, we use it to check if a slug was provided before trying to load an article:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// If no slug exists, show a message and stop.&lt;/span&gt;
    &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;p&amp;gt;Select an article...&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  Functions: Reusable Blocks of Code
&lt;/h5&gt;

&lt;p&gt;A function is a named, reusable set of instructions designed to perform a specific task. You define a function once and can then "call" or "invoke" it as many times as needed. Our &lt;code&gt;app.js&lt;/code&gt; is built around two main functions: &lt;code&gt;loadArticleList()&lt;/code&gt; and &lt;code&gt;loadArticleContent()&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The &lt;code&gt;return&lt;/code&gt; statement&lt;/strong&gt;: This statement ends the execution of a function. As seen in the &lt;code&gt;if&lt;/code&gt; statement example above, &lt;code&gt;return;&lt;/code&gt; is used to exit the &lt;code&gt;loadArticleContent&lt;/code&gt; function early if no slug is provided.&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  Scope: Where Variables Live
&lt;/h5&gt;

&lt;p&gt;Scope determines the accessibility of variables. In JavaScript, variables declared inside a function are generally "local" to that function and cannot be accessed from the outside. This is a crucial feature that prevents different parts of a program from accidentally interfering with each other. For example, the &lt;code&gt;articles&lt;/code&gt; variable inside our &lt;code&gt;loadArticleList&lt;/code&gt; function is only accessible within that function.&lt;/p&gt;

&lt;h4&gt;
  
  
  Part 2: JavaScript in the Browser: The DOM and Events
&lt;/h4&gt;

&lt;p&gt;Now that we understand the core grammar of JavaScript, we can explore how it breathes life into a static HTML file. This is achieved by interacting with a live representation of the webpage called the Document Object Model (DOM) and by reacting to user actions through events.&lt;/p&gt;

&lt;h5&gt;
  
  
  The Document Object Model (DOM)
&lt;/h5&gt;

&lt;p&gt;When a browser loads your HTML file, it doesn't just display it as static text. It creates an in-memory, tree-like structure that represents the document. This live, interactive model is the &lt;strong&gt;DOM&lt;/strong&gt;. Every HTML tag, attribute, and piece of text becomes an "object" that JavaScript can access and manipulate. This is the fundamental mechanism that allows us to dynamically change what the user sees without ever reloading the page.&lt;/p&gt;

&lt;h5&gt;
  
  
  Selecting and Manipulating Elements
&lt;/h5&gt;

&lt;p&gt;To make a page dynamic, our script first needs to get a reference to the HTML elements it wants to change.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;document.getElementById()&lt;/code&gt;&lt;/strong&gt;: The most direct way to select a single element is by its unique &lt;code&gt;id&lt;/code&gt; attribute. In our &lt;code&gt;app.js&lt;/code&gt;, we use this to get a handle on our main layout components:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sidebar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;article-list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;content&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;document.createElement()&lt;/code&gt;&lt;/strong&gt;: This creates a new HTML element object in memory, which is not yet visible on the page. We use this to build our list items for the sidebar: &lt;code&gt;const li = document.createElement('li');&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;.textContent&lt;/code&gt; vs. &lt;code&gt;.innerHTML&lt;/code&gt;&lt;/strong&gt;: These properties control the content inside an element.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;.textContent&lt;/code&gt; sets or gets only the raw text. It is a safe and efficient way to change text.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.innerHTML&lt;/code&gt; sets or gets the full HTML structure within an element. We use this to render the article content returned by &lt;code&gt;marked.parse()&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;.appendChild()&lt;/code&gt;&lt;/strong&gt;: This method takes an element we created in memory and attaches it as a new child to an existing element in the DOM, making it visible on the page. We use this to add each new list item to our sidebar.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;h5&gt;
  
  
  Storing Data on Elements
&lt;/h5&gt;

&lt;p&gt;Sometimes we need to associate data with an element for our script to use later.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;.href&lt;/code&gt;&lt;/strong&gt;: This is a standard property of an anchor (&lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt;) tag that gets or sets the URL it points to. We set this to a hash fragment like &lt;code&gt;href = '#0001_basic_fastapi'&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;.dataset&lt;/code&gt;&lt;/strong&gt;: This provides access to custom &lt;code&gt;data-*&lt;/code&gt; attributes in HTML. It is a clean, modern way to store data directly on an element. We use it to keep a reference to an article's slug for our click handler.&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  Event Handling: Reacting to the User
&lt;/h5&gt;

&lt;p&gt;A dynamic webpage must respond to user actions. These actions—like mouse clicks, key presses, or the page finishing its initial load—are called &lt;strong&gt;events&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;addEventListener()&lt;/code&gt;&lt;/strong&gt;: This is the primary method for listening for events on an element. It takes the name of the event (e.g., &lt;code&gt;'click'&lt;/code&gt; or &lt;code&gt;'DOMContentLoaded'&lt;/code&gt;) and a "callback function" to execute when that event occurs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;event.preventDefault()&lt;/code&gt;&lt;/strong&gt;: When an event occurs, the browser often has a default action. For a click on a link (&lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt;), the default action is to navigate to the &lt;code&gt;href&lt;/code&gt; URL. &lt;code&gt;event.preventDefault()&lt;/code&gt; is a command we use inside our event listener to stop this default browser behavior, allowing our script to take full control and handle the navigation dynamically.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Part 3: Modern Web Applications: Asynchronous JavaScript and APIs
&lt;/h4&gt;

&lt;p&gt;The defining feature of a modern web application is its ability to communicate with a server in the background to fetch or send data without interrupting the user. This is made possible by JavaScript's asynchronous capabilities. Understanding this concept is the key to building fast, responsive applications.&lt;/p&gt;

&lt;h5&gt;
  
  
  Asynchronous by Nature
&lt;/h5&gt;

&lt;p&gt;JavaScript in the browser runs on a single thread. If it were to execute tasks synchronously, a long-running operation like a network request would freeze the entire user interface until it completed. The user wouldn't be able to click, scroll, or interact with the page.&lt;/p&gt;

&lt;p&gt;To prevent this, JavaScript is &lt;strong&gt;asynchronous&lt;/strong&gt; and &lt;strong&gt;non-blocking&lt;/strong&gt;. It can start a time-consuming task (like fetching an article), and while it waits for the result, it can continue to handle other tasks, like responding to user input.&lt;/p&gt;

&lt;h5&gt;
  
  
  Fetching Data with Promises
&lt;/h5&gt;

&lt;p&gt;The modern browser API for making network requests is &lt;strong&gt;&lt;code&gt;fetch()&lt;/code&gt;&lt;/strong&gt;. When you call &lt;code&gt;fetch()&lt;/code&gt;, it does not immediately return the data. Instead, it returns a &lt;strong&gt;Promise&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A Promise is a placeholder object representing the eventual result of an asynchronous operation. It's an "IOU" from the browser that says, "I've started your request, and I promise to give you a value back in the future." A Promise can be in one of three states: &lt;code&gt;pending&lt;/code&gt; (the operation hasn't finished), &lt;code&gt;fulfilled&lt;/code&gt; (it succeeded), or &lt;code&gt;rejected&lt;/code&gt; (it failed).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;fetch()&lt;/code&gt;&lt;/strong&gt;: Initiates a network request and returns a Promise that resolves to a &lt;code&gt;Response&lt;/code&gt; object.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;response.ok&lt;/code&gt;&lt;/strong&gt;: A simple boolean property on the &lt;code&gt;Response&lt;/code&gt; object that is &lt;code&gt;true&lt;/code&gt; if the HTTP status code was successful (e.g., 200 OK).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;.json()&lt;/code&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;code&gt;.text()&lt;/code&gt;&lt;/strong&gt;: These methods are used to read the body of the response. They are also asynchronous and return a new Promise that resolves with the content parsed as either JSON or plain text.&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  &lt;code&gt;async/await&lt;/code&gt;: Writing Readable Asynchronous Code
&lt;/h5&gt;

&lt;p&gt;While you can work with Promises directly, modern JavaScript provides a much cleaner syntax called &lt;strong&gt;&lt;code&gt;async/await&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;async&lt;/code&gt;&lt;/strong&gt;: Placing this keyword before a function declaration allows that function to use &lt;code&gt;await&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;await&lt;/code&gt;&lt;/strong&gt;: This keyword can be placed before any expression that returns a Promise. It pauses the execution of the &lt;code&gt;async&lt;/code&gt; function until the Promise is fulfilled and "unwraps" the value, allowing you to write asynchronous code that looks and reads like synchronous code. Our &lt;code&gt;loadArticleList&lt;/code&gt; function is a perfect example:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loadArticleList&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/articles&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;articles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without &lt;code&gt;async/await&lt;/code&gt;, this would require more complex, nested code.&lt;/p&gt;

&lt;h5&gt;
  
  
  Graceful Error Handling
&lt;/h5&gt;

&lt;p&gt;Things can go wrong, especially with network requests. &lt;code&gt;async/await&lt;/code&gt; allows us to handle these errors using the standard &lt;code&gt;try...catch&lt;/code&gt; block.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;try...catch&lt;/code&gt;&lt;/strong&gt;: You wrap the code that might fail in a &lt;code&gt;try&lt;/code&gt; block. If an error occurs (e.g., the network is down, or the server returns an error), the code execution jumps to the &lt;code&gt;catch&lt;/code&gt; block, where you can handle the error gracefully instead of crashing the application.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;throw new Error()&lt;/code&gt;&lt;/strong&gt;: We can manually trigger an error. In our code, &lt;code&gt;if (!response.ok)&lt;/code&gt; we &lt;code&gt;throw&lt;/code&gt; a new &lt;code&gt;Error&lt;/code&gt;, which will immediately be caught by our &lt;code&gt;catch&lt;/code&gt; block.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;console.error()&lt;/code&gt;&lt;/strong&gt;: This is the standard way to log error messages to the browser's developer console, which is an invaluable tool for debugging.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Part 4: Writing Clean Code: Modern ES6+ Syntax and Idioms
&lt;/h4&gt;

&lt;p&gt;The previous sections covered the core mechanics of how JavaScript works. This section focuses on the modern syntax—often called "syntactic sugar"—that allows us to write that same logic in a cleaner, more expressive, and more efficient way. These features were introduced in a major update to the language called ES6 (ECMAScript 2015) and are now standard in all modern browsers.&lt;/p&gt;

&lt;h5&gt;
  
  
  Template Literals
&lt;/h5&gt;

&lt;p&gt;Template literals provide an enhanced way to create strings, using backticks (&lt;code&gt;`&lt;/code&gt;) instead of single or double quotes. Their main advantage is &lt;strong&gt;string interpolation&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This allows you to embed variables and expressions directly into a string using the &lt;code&gt;${...}&lt;/code&gt; syntax. This is much cleaner than traditional string concatenation with the &lt;code&gt;+&lt;/code&gt; operator. We use this in our error handling:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// The modern way with template literals&lt;/span&gt;
&lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`HTTP error! Status: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// The old way with concatenation&lt;/span&gt;
&lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;HTTP error! Status: &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  Arrow Functions
&lt;/h5&gt;

&lt;p&gt;Arrow functions (&lt;code&gt;=&amp;gt;&lt;/code&gt;) offer a more concise syntax for writing function expressions. They are especially useful for short, anonymous functions, such as the callbacks used in event listeners or array methods.&lt;/p&gt;

&lt;p&gt;Consider the event listener in our &lt;code&gt;app.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// The modern way with an arrow function&lt;/span&gt;
&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;loadArticleContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// The old way with a traditional function expression&lt;/span&gt;
&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;loadArticleContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The arrow function is more compact and is the standard in modern JavaScript codebases.&lt;/p&gt;

&lt;h5&gt;
  
  
  The Ternary Operator
&lt;/h5&gt;

&lt;p&gt;The ternary operator is a compact, inline &lt;code&gt;if/else&lt;/code&gt; statement. It takes three operands: a condition, an expression to execute if the condition is true, and an expression to execute if the condition is false.&lt;/p&gt;

&lt;p&gt;The syntax is: &lt;code&gt;condition ? expressionIfTrue : expressionIfFalse&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;While not used in our &lt;code&gt;app.js&lt;/code&gt;, it is common in the libraries we depend on and is useful for simple conditional assignments. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Using an if/else statement&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isLoggedIn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Welcome back&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Please log in&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Using the equivalent ternary operator&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;isLoggedIn&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Welcome back&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Please log in&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  Modern Array Methods
&lt;/h5&gt;

&lt;p&gt;Instead of using traditional &lt;code&gt;for&lt;/code&gt; loops to iterate over arrays, modern JavaScript provides a powerful set of built-in methods that are more declarative and readable.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;.forEach()&lt;/code&gt;&lt;/strong&gt;: This method executes a provided function once for each element in an array. It is a clean way to loop over an array when you want to perform an action for each item but do not need to create a new array. We use this to build our sidebar navigation:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;articles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;article&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Create and append an &amp;lt;li&amp;gt; for each article&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Other common methods include &lt;code&gt;.map()&lt;/code&gt; (which creates a new array by transforming each element) and &lt;code&gt;.filter()&lt;/code&gt; (which creates a new array containing only the elements that pass a certain test).&lt;/p&gt;

&lt;h4&gt;
  
  
  Part 5: Under the Hood: How Professional Libraries are Built
&lt;/h4&gt;

&lt;p&gt;Our application's ability to render Markdown, code, and math is powered by sophisticated third-party libraries. By briefly examining the patterns used to build these tools, we can gain insight into how robust, portable, and professional JavaScript is written.&lt;/p&gt;

&lt;h5&gt;
  
  
  Leveraging the Ecosystem: Using Third-Party Libraries
&lt;/h5&gt;

&lt;p&gt;A core principle of modern development is to not "reinvent the wheel." Complex, solved problems like parsing Markdown are best handled by specialized, community-vetted tools. In our code, we leverage this principle by delegating tasks to these libraries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;marked&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;markdown&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Handles Markdown parsing&lt;/span&gt;
&lt;span class="nx"&gt;hljs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;highlightAll&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;                       &lt;span class="c1"&gt;// Handles syntax highlighting&lt;/span&gt;
&lt;span class="nx"&gt;MathJax&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;typeset&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;                         &lt;span class="c1"&gt;// Handles math typesetting&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Knowing when to build from scratch and when to use a well-tested library is a critical skill for any developer.&lt;/p&gt;

&lt;h5&gt;
  
  
  Code Organization and Portability
&lt;/h5&gt;

&lt;p&gt;When you include multiple scripts on a webpage, they all share the same "global namespace." If two different libraries create a variable with the same name, they can conflict and break the application. Professional libraries use several patterns to prevent this.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;IIFE (Immediately Invoked Function Expression)&lt;/strong&gt;: Most libraries wrap their entire code in a special function that they immediately execute, like &lt;code&gt;(function() { ... })();&lt;/code&gt;. This pattern creates a private scope for all the library's variables, preventing them from "leaking" out and polluting the global namespace.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UMD (Universal Module Definition)&lt;/strong&gt;: A library needs to work in different environments (like the browser, or in a backend Node.js server). The UMD pattern is a set of &lt;code&gt;if/else&lt;/code&gt; checks that allows the code to detect which module system is being used (e.g., CommonJS, AMD, or none) and export its functionality in the correct way for that environment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;"use strict";&lt;/code&gt;&lt;/strong&gt;: This is a directive placed at the top of a script that enables a stricter set of rules for the JavaScript interpreter. It helps prevent common coding errors by changing "silent errors" into thrown errors and disallowing the use of some unsafe language features.&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  Regular Expressions: The Language of Pattern Matching
&lt;/h5&gt;

&lt;p&gt;&lt;strong&gt;Regular Expressions&lt;/strong&gt; (or RegExp) are a powerful mini-language for finding and manipulating patterns in text. Libraries like &lt;code&gt;marked.js&lt;/code&gt; are powered by complex regular expressions that can identify the syntax for headings, lists, links, code blocks, and other Markdown features. They can be created with a literal syntax (&lt;code&gt;/pattern/&lt;/code&gt;) or a constructor (&lt;code&gt;new RegExp()&lt;/code&gt;).&lt;/p&gt;

&lt;h5&gt;
  
  
  Advanced and Safe Object Handling
&lt;/h5&gt;

&lt;p&gt;Libraries must be written defensively to handle any kind of input without crashing.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Object.prototype.hasOwnProperty.call(obj, 'prop')&lt;/code&gt;&lt;/strong&gt;: This is the safest way to check if an object truly has its own property. It is used instead of the simpler &lt;code&gt;obj.hasOwnProperty()&lt;/code&gt; to protect against edge cases where an object might have been created with a conflicting property of the same name.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Object.defineProperty()&lt;/code&gt;&lt;/strong&gt;: This method gives a developer precise control over the properties of an object. It can be used to create read-only properties, prevent properties from being deleted, or hide them from loops, which is essential for creating robust APIs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Digging Deeper into the Backend
&lt;/h2&gt;

&lt;p&gt;Our &lt;a href="https://www.linkedin.com/pulse/first-principles-guide-http-websockets-fastapi-warren-jitsing-yoiaf/" rel="noopener noreferrer"&gt;previous article on FastAPI&lt;/a&gt; established the first principles of building high-performance APIs. In this section, we will apply those core concepts to build the backend for our article viewer.&lt;/p&gt;

&lt;p&gt;We will demonstrate how to use foundational features like the &lt;code&gt;lifespan&lt;/code&gt; handler for startup logic and then expand on that knowledge to tackle new, practical challenges, such as discovering content from the filesystem and serving a complete frontend application with Jinja2 templates.&lt;/p&gt;

&lt;h3&gt;
  
  
  Part 1: Architecting the Application: Serving a Full Frontend
&lt;/h3&gt;

&lt;p&gt;A modern web application server must often do more than just respond to data queries with JSON. It also needs to deliver the client-side application itself—the initial HTML document, the CSS stylesheets, and the JavaScript logic. This section covers the high-level FastAPI features that allow our server to act as both a data API and a web server.&lt;/p&gt;

&lt;h4&gt;
  
  
  Serving Static Assets with &lt;code&gt;StaticFiles&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Our frontend application consists of several "static" files that do not change, such as &lt;code&gt;style.css&lt;/code&gt; and &lt;code&gt;app.js&lt;/code&gt;. The browser needs a way to request these files. The most efficient way to handle this in FastAPI is to "mount" an entire directory of files to a specific URL path.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# In server.py
&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/static&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;StaticFiles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;directory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;STATIC_DIR&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;static&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This single line of code instructs FastAPI to handle any incoming request whose URL begins with &lt;code&gt;/static&lt;/code&gt;. The request is passed to a special &lt;code&gt;StaticFiles&lt;/code&gt; application, which knows how to securely find and serve the corresponding file from the &lt;code&gt;STATIC_DIR&lt;/code&gt; on our filesystem.&lt;/p&gt;

&lt;h4&gt;
  
  
  Serving the HTML Shell with &lt;code&gt;Jinja2Templates&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;In addition to assets, we must serve the main &lt;code&gt;index.html&lt;/code&gt; file, which is the entry point for our single-page application. While we could use a simple &lt;code&gt;FileResponse&lt;/code&gt;, a more powerful and professional pattern is to use a templating engine.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# In server.py
&lt;/span&gt;&lt;span class="n"&gt;templates&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Jinja2Templates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;directory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;TEMPLATES_DIR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;serve_frontend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Serves the main index.html single-page application.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;templates&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TemplateResponse&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;request&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;index.html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While our current &lt;code&gt;index.html&lt;/code&gt; is a simple static file, using a templating engine like &lt;strong&gt;Jinja2&lt;/strong&gt; is a deliberate choice for future-proofing. It establishes a pattern that would allow us to easily inject dynamic, server-generated data into our HTML if the application's requirements were to grow. The &lt;code&gt;TemplateResponse&lt;/code&gt; renders the specified file and returns it as a standard HTML response.&lt;/p&gt;

&lt;h4&gt;
  
  
  Managing Application State with &lt;code&gt;app.state&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Our server needs to know which articles are available, but scanning the filesystem on every single API request would be extremely inefficient. The ideal solution is to scan the filesystem once when the server starts and then store that list of articles in memory.&lt;/p&gt;

&lt;p&gt;FastAPI provides a clean, dedicated object for this purpose: &lt;code&gt;app.state&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# From the lifespan function in server.py
&lt;/span&gt;&lt;span class="nd"&gt;@asynccontextmanager&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lifespan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# ... logic to scan filesystem and build 'articles' list ...
&lt;/span&gt;    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;articles_catalog&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;slug&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;article&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;article&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;articles&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;app.state&lt;/code&gt; object is a simple namespace that persists for the entire lifecycle of the application. By attaching our &lt;code&gt;articles_catalog&lt;/code&gt; to it during the startup phase of the &lt;code&gt;lifespan&lt;/code&gt; handler, we make this data instantly and efficiently available to all subsequent API requests without any performance penalty.&lt;/p&gt;

&lt;h3&gt;
  
  
  Part 2: Advanced Routing and Responses
&lt;/h3&gt;

&lt;p&gt;With the overall application structure in place, we can now zoom in on the specific techniques used within our API endpoints. These features allow us to handle more complex URL structures and provide the browser with precise information about the data being served, which are hallmarks of a robust API.&lt;/p&gt;

&lt;h4&gt;
  
  
  Capturing Complex URL Paths with Path Converters
&lt;/h4&gt;

&lt;p&gt;A standard URL path parameter in FastAPI, like &lt;code&gt;{param}&lt;/code&gt;, will match any text up to the next slash (&lt;code&gt;/&lt;/code&gt;). This presents a problem for our static file endpoint, as a file could be nested in a subdirectory (e.g., &lt;code&gt;images/diagram.png&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;To solve this, FastAPI supports &lt;strong&gt;path converters&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# In server.py
&lt;/span&gt;&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/articles/{article_slug}/static/{file_path:path}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By adding &lt;code&gt;:path&lt;/code&gt; after the parameter name (&lt;code&gt;file_path:path&lt;/code&gt;), we instruct FastAPI to match the entire remaining URL segment, including any slashes it may contain. This allows our endpoint to correctly capture the full relative path to any static file, no matter how deeply it is nested.&lt;/p&gt;

&lt;h4&gt;
  
  
  Accessing the &lt;code&gt;Request&lt;/code&gt; Object
&lt;/h4&gt;

&lt;p&gt;While endpoints primarily deal with processing and returning data, they sometimes need access to information about the incoming HTTP request itself, such as its headers or the client's IP address. FastAPI provides this through the &lt;code&gt;Request&lt;/code&gt; object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# In server.py
&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;serve_frontend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;templates&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TemplateResponse&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;request&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;index.html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By adding a parameter to our endpoint and type-hinting it as &lt;code&gt;request: Request&lt;/code&gt;, FastAPI will automatically "inject" the full request object into that parameter. In our case, this is a requirement for the Jinja2 templating engine, which needs the request context to function correctly.&lt;/p&gt;

&lt;h4&gt;
  
  
  Controlling Response Headers with &lt;code&gt;media_type&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Every HTTP response includes a &lt;code&gt;Content-Type&lt;/code&gt; header (also known as a MIME type). This header is the "label on the box" that tells the browser what kind of data it is receiving (e.g., &lt;code&gt;text/html&lt;/code&gt;, &lt;code&gt;image/png&lt;/code&gt;, &lt;code&gt;application/json&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;While FastAPI is excellent at inferring the correct &lt;code&gt;Content-Type&lt;/code&gt;, sometimes we need to be explicit. When we serve our raw &lt;code&gt;README.md&lt;/code&gt; files, we want to ensure the browser interprets them as plain text intended as Markdown. &lt;code&gt;FileResponse&lt;/code&gt; allows us to set this header directly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# In server.py
&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;FileResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;media_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text/markdown; charset=utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;media_type&lt;/code&gt; argument gives us precise control over the response headers. This ensures that clients, browsers, and proxies all handle the data correctly, which is a fundamental aspect of building a well-behaved and predictable web server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Part 3: Pythonic Power-Ups
&lt;/h3&gt;

&lt;p&gt;Beyond the web framework, the elegance of our backend comes from leveraging modern Python features and its powerful standard library. This section highlights a few of these "Pythonic" patterns that help us write clean, readable, and efficient code.&lt;/p&gt;

&lt;h4&gt;
  
  
  Advanced Filesystem Navigation with &lt;code&gt;pathlib&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Instead of working with filesystem paths as simple strings, modern Python encourages the use of the &lt;code&gt;pathlib&lt;/code&gt; library, which provides an object-oriented interface for paths.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# In server.py
&lt;/span&gt;&lt;span class="n"&gt;HOME_DIR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;home&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;# ...
&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ARTICLES_DIR&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;iterdir&lt;/span&gt;&lt;span class="p"&gt;()):&lt;/span&gt;
    &lt;span class="c1"&gt;# ...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two key features are used here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Path.home()&lt;/code&gt;&lt;/strong&gt;: A clean, cross-platform method to get the path to the current user's home directory.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;.iterdir()&lt;/code&gt;&lt;/strong&gt;: An elegant way to iterate over all items within a directory. It returns &lt;code&gt;Path&lt;/code&gt; objects, which we can then easily inspect with methods like &lt;code&gt;.is_dir()&lt;/code&gt; and &lt;code&gt;.is_file()&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Powerful Text Manipulation with the &lt;code&gt;re&lt;/code&gt; Module
&lt;/h4&gt;

&lt;p&gt;For complex text processing, Python's built-in &lt;code&gt;re&lt;/code&gt; module provides access to regular expressions. We use this in our &lt;code&gt;format_title&lt;/code&gt; function to create clean titles from directory names.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# In server.py
&lt;/span&gt;&lt;span class="n"&gt;cleaned_slug&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;^\d+_&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The function &lt;code&gt;re.sub(pattern, replacement, string)&lt;/code&gt; finds all substrings that match the &lt;code&gt;pattern&lt;/code&gt; and replaces them. The pattern &lt;code&gt;r"^\d+_"&lt;/code&gt; is a regular expression that means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;^&lt;/code&gt;&lt;/strong&gt;: Match at the beginning of the string.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;\d+&lt;/code&gt;&lt;/strong&gt;: Match one or more digits (0-9).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;_&lt;/code&gt;&lt;/strong&gt;: Match a literal underscore.
This pattern finds and removes the &lt;code&gt;0001_&lt;/code&gt; prefix from our article slugs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Elegant Data Transformation with List Comprehensions
&lt;/h4&gt;

&lt;p&gt;List comprehensions are a concise and highly readable way to create a list based on an existing iterable. They are a hallmark of idiomatic Python code. We use one to prepare our article index for the API response.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# In server.py
&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;slug&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;slug&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;articles_catalog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This single line of code iterates through all the article data stored in our application state and builds a new, cleaned-up list of dictionaries. This is a much more compact and expressive alternative to a traditional &lt;code&gt;for&lt;/code&gt; loop.&lt;/p&gt;

&lt;h4&gt;
  
  
  Safe Attribute Access with &lt;code&gt;hasattr&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;hasattr()&lt;/code&gt; is a defensive programming function that checks if an object has a given attribute before you try to access it, preventing potential &lt;code&gt;AttributeError&lt;/code&gt; exceptions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# In server.py
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;hasattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;articles_catalog&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;articles_catalog&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our &lt;code&gt;get_articles_index&lt;/code&gt; endpoint, we use &lt;code&gt;hasattr()&lt;/code&gt; to safely check that the &lt;code&gt;lifespan&lt;/code&gt; function has successfully created the &lt;code&gt;articles_catalog&lt;/code&gt; on our &lt;code&gt;app.state&lt;/code&gt; object. This makes the code more robust against potential startup issues.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Blueprint: A Dual-Component Architecture
&lt;/h1&gt;

&lt;p&gt;Before implementing our application, we'll first create its architectural blueprint. This is a high-level plan that defines the responsibilities of each component and the contract for how they will communicate.&lt;/p&gt;

&lt;p&gt;The core of our design is a &lt;strong&gt;decoupled architecture&lt;/strong&gt;. The backend server will act as a standalone data provider, and the frontend will be a standalone data consumer, responsible for all presentation logic. This separation of concerns is a fundamental principle that makes the application clean, scalable, and easy to maintain.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part I: The Backend Blueprint (The Librarian)
&lt;/h2&gt;

&lt;p&gt;The FastAPI server's primary job is to find content on the filesystem and expose it to the world through a well-defined API.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Dynamic Content Discovery&lt;/strong&gt;: The server will not have a hardcoded list of articles. Instead, on startup (using the &lt;code&gt;lifespan&lt;/code&gt; handler), it will dynamically scan the filesystem for any directories that match the &lt;code&gt;0xxx_&amp;lt;name&amp;gt;&lt;/code&gt; pattern and contain a &lt;code&gt;README.md&lt;/code&gt; file. The metadata for these articles will be loaded into an in-memory "catalog" for high-performance access.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The API Contract&lt;/strong&gt;: The server will provide three crucial endpoints:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Article Index (&lt;code&gt;GET /api/articles&lt;/code&gt;)&lt;/strong&gt;: This endpoint will return a JSON list of all discovered articles, containing only the &lt;code&gt;slug&lt;/code&gt; and &lt;code&gt;title&lt;/code&gt; for each.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Article Content (&lt;code&gt;GET /api/articles/{slug}&lt;/code&gt;)&lt;/strong&gt;: This endpoint will return the raw, unmodified Markdown content for a specific article.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Article-Specific Assets (&lt;code&gt;GET /articles/{slug}/static/{path}&lt;/code&gt;)&lt;/strong&gt;: This endpoint is our solution for handling relative image paths. It will serve static files (like images) from within a specific article's own &lt;code&gt;static&lt;/code&gt; sub-directory.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Frontend Serving Role&lt;/strong&gt;: In addition to its data API, the backend is also responsible for serving the initial &lt;code&gt;index.html&lt;/code&gt; shell and the core static assets (&lt;code&gt;app.js&lt;/code&gt;, &lt;code&gt;style.css&lt;/code&gt;) that make up the frontend application.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Part II: The Frontend Blueprint (The Reading Room)
&lt;/h2&gt;

&lt;p&gt;The client-side application's primary job is to provide the user interface, fetch data from the backend, and render it for the user.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Application Shell&lt;/strong&gt;: The frontend is a Single-Page Application (SPA) built from a single, static &lt;code&gt;index.html&lt;/code&gt; file. This file provides the basic layout structure—a sidebar and a main content area—and uses &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags to load all necessary third-party libraries and our own application logic.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Dynamic Navigation&lt;/strong&gt;: On page load, the frontend will first make an API call to the &lt;code&gt;GET /api/articles&lt;/code&gt; endpoint. It will then use the returned JSON data to dynamically build the navigation links in the sidebar. This ensures the viewer automatically updates whenever new articles are added to the repository.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The Rendering Pipeline&lt;/strong&gt;: When a user selects an article, the frontend will execute a precise, four-step rendering pipeline:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Fetch&lt;/strong&gt;: Make an API call to &lt;code&gt;GET /api/articles/{slug}&lt;/code&gt; to retrieve the raw Markdown text.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Parse&lt;/strong&gt;: Pass the Markdown string to the &lt;strong&gt;&lt;code&gt;marked.js&lt;/code&gt;&lt;/strong&gt; library to convert it into an HTML string. We will configure &lt;code&gt;marked.js&lt;/code&gt; to correctly resolve relative image and link paths using our dedicated asset endpoint.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Inject&lt;/strong&gt;: Place the generated HTML into the main content area of the page.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Enhance&lt;/strong&gt;: After the new content is in the DOM, make two final calls: first to &lt;strong&gt;&lt;code&gt;highlight.js&lt;/code&gt;&lt;/strong&gt; to apply syntax highlighting to all code blocks, and second to &lt;strong&gt;&lt;code&gt;MathJax&lt;/code&gt;&lt;/strong&gt; to find and typeset any mathematical formulas.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Environment Setup
&lt;/h1&gt;

&lt;p&gt;Before we dive into the implementation, we must prepare a clean, isolated Python environment for our viewer application. All of the following commands should be run from a terminal inside the running Docker container.&lt;/p&gt;

&lt;p&gt;The goal is to use Python's built-in &lt;code&gt;venv&lt;/code&gt; module to create a self-contained environment. This ensures that the packages we install for this project will not conflict with system-wide packages or other projects you may be working on inside the container.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Define Project Dependencies
&lt;/h2&gt;

&lt;p&gt;First, we will list all the required Python packages in a &lt;code&gt;requirements.txt&lt;/code&gt; file. This file allows us to install all dependencies with a single command and ensures a reproducible setup.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Action&lt;/strong&gt;: Create the file &lt;code&gt;articles/0003_html_css_javascript_article_viewer/requirements.txt&lt;/code&gt; with the following content.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fastapi
uvicorn
httpx
pytest
pytest-cov
jinja2
python-multipart
pytest-playwright
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Create and Activate the Virtual Environment
&lt;/h2&gt;

&lt;p&gt;We will create our virtual environment inside the &lt;code&gt;articles/0003_html_css_javascript_article_viewer&lt;/code&gt; directory using the custom-built Python 3.12 that exists in our container.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Action&lt;/strong&gt;: Navigate to the &lt;code&gt;articles/0003_html_css_javascript_article_viewer&lt;/code&gt; directory and run the following commands.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Navigate to the live application's directory&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;articles/0003_html_css_javascript_article_viewer

&lt;span class="c"&gt;# Create a virtual environment named .venv using our custom Python 3.12&lt;/span&gt;
python3.12 &lt;span class="nt"&gt;-m&lt;/span&gt; venv .venv

&lt;span class="c"&gt;# Activate the environment. Your shell prompt should now change.&lt;/span&gt;
&lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Install Python Dependencies
&lt;/h2&gt;

&lt;p&gt;With the virtual environment active, we can now install all the packages from our &lt;code&gt;requirements.txt&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Action&lt;/strong&gt;: Run the following command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;A Note on &lt;code&gt;python3 -m pip&lt;/code&gt;&lt;/strong&gt;: Using &lt;code&gt;python3 -m pip&lt;/code&gt; is a robust and explicit way to run &lt;code&gt;pip&lt;/code&gt;. It guarantees that we are using the &lt;code&gt;pip&lt;/code&gt; executable associated with the &lt;code&gt;python3&lt;/code&gt; interpreter from our currently active virtual environment, which prevents common issues related to system path configurations.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Install Playwright Browsers and Dependencies
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;pytest-playwright&lt;/code&gt; package requires a final setup step to download the browser binaries (Chromium, Firefox, WebKit) and their system-level dependencies. We can encapsulate this in a simple shell script.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Action&lt;/strong&gt;: Create the file &lt;code&gt;playwright-install.sh&lt;/code&gt; and then run it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="nb"&gt;.&lt;/span&gt; .venv/bin/activate
playwright install-deps
playwright &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, make the script executable and run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x playwright-install.sh
bash playwright-install.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This script will first activate our virtual environment, then run &lt;code&gt;playwright install-deps&lt;/code&gt; (which may ask for your &lt;code&gt;sudo&lt;/code&gt; password to install system libraries), and finally run &lt;code&gt;playwright install&lt;/code&gt; to download the browser binaries.&lt;/p&gt;

&lt;p&gt;With our isolated environment fully configured and all dependencies installed, we are now ready to proceed with the implementation.&lt;/p&gt;

&lt;h1&gt;
  
  
  Implementation
&lt;/h1&gt;

&lt;p&gt;With our architectural blueprint and environment complete, we can now translate it into functional code. This section will walk through the implementation of each file, connecting the code directly back to the design decisions we made. We will build the application from the backend to the frontend.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part I: The FastAPI Backend (&lt;code&gt;server.py&lt;/code&gt;)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Imports and Configuration
&lt;/h3&gt;

&lt;p&gt;Our entire backend logic is contained within a single file, &lt;code&gt;server.py&lt;/code&gt;. We will begin by examining its foundational components: the necessary imports, path configurations, and helper functions that make the application aware of its environment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;uvicorn&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;contextlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asynccontextmanager&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HTTPException&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi.responses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FileResponse&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi.staticfiles&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;StaticFiles&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi.templating&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Jinja2Templates&lt;/span&gt;

&lt;span class="c1"&gt;# --- Configuration ---
# All paths are relative to the user's home directory inside the container.
&lt;/span&gt;&lt;span class="n"&gt;HOME_DIR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;home&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;ARTICLES_DIR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HOME_DIR&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;articles&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;VIEWER_DIR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HOME_DIR&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;articles&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0003_html_css_javascript_article_viewer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;DATA_DIR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HOME_DIR&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;TEMPLATES_DIR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;VIEWER_DIR&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;templates&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;STATIC_DIR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;VIEWER_DIR&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;static&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;CERT_FILE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DATA_DIR&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cert.pem&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;KEY_FILE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DATA_DIR&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;key.pem&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;format_title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Converts a directory slug into a human-readable title.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="c1"&gt;# Remove leading digits and underscores, replace underscores with spaces, and title case.
&lt;/span&gt;    &lt;span class="c1"&gt;# e.g., "0001_basic_fastapi" -&amp;gt; "Basic Fastapi"
&lt;/span&gt;    &lt;span class="n"&gt;cleaned_slug&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;^\d+_&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;cleaned_slug&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;_&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Deconstruction: Imports and Configuration
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Imports&lt;/strong&gt;: We begin by importing all the necessary tools. From Python's standard library, we import &lt;code&gt;re&lt;/code&gt; for regular expressions and &lt;code&gt;pathlib&lt;/code&gt; for an object-oriented way of handling filesystem paths. From our installed packages, we import &lt;code&gt;FastAPI&lt;/code&gt; itself, along with specific classes for handling responses (&lt;code&gt;FileResponse&lt;/code&gt;), static files, and templates (&lt;code&gt;Jinja2Templates&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Path Constants&lt;/strong&gt;: We define a set of global constants for all important directory paths. Using the &lt;code&gt;pathlib&lt;/code&gt; library instead of simple strings is a modern best practice that makes path manipulation safer and more readable. &lt;code&gt;Path.home()&lt;/code&gt; provides a reliable way to get the current user's home directory, which serves as the root for all our operations inside the container.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The &lt;code&gt;format_title&lt;/code&gt; Helper&lt;/strong&gt;: This utility function is a practical example of text manipulation. It takes a directory name like &lt;code&gt;0001_basic_fastapi&lt;/code&gt; and uses a regular expression (&lt;code&gt;re.sub(r"^\d+_", "", slug)&lt;/code&gt;) to strip the leading numbers and underscore. It then replaces the remaining underscores with spaces and title-cases the result to produce a clean, human-readable title like "Basic Fastapi".&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Lifespan
&lt;/h3&gt;

&lt;p&gt;We will now examine the &lt;code&gt;lifespan&lt;/code&gt; handler, which is the heart of the server's content discovery mechanism.&lt;/p&gt;

&lt;p&gt;This function implements the "Dynamic Content Discovery" from our blueprint. Its purpose is to scan the filesystem once when the server starts, creating an in-memory catalog of all available articles. This is far more efficient than re-scanning the disk every time a user makes a request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@asynccontextmanager&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lifespan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    On startup, scan the articles directory and build a catalog of available articles.
    This catalog is stored in the application&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s state for quick access.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Server is starting up, building article catalog...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;articles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ARTICLES_DIR&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_dir&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ARTICLES_DIR&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;iterdir&lt;/span&gt;&lt;span class="p"&gt;()):&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_dir&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;README.md&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;is_file&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
                &lt;span class="n"&gt;slug&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;
                &lt;span class="n"&gt;articles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;slug&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;format_title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;path&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;README.md&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;articles_catalog&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;slug&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;article&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;article&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;articles&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Found &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;articles&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; articles.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Server is shutting down.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="c1"&gt;# --- FastAPI Application ---
&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lifespan&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;lifespan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Deconstruction: The &lt;code&gt;lifespan&lt;/code&gt; Handler
&lt;/h4&gt;

&lt;p&gt;A &lt;code&gt;lifespan&lt;/code&gt; handler is a special function that FastAPI executes during its startup and shutdown sequence.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@asynccontextmanager&lt;/code&gt;&lt;/strong&gt;: This decorator from Python's standard library turns our function into an "asynchronous context manager," which is the required format for a &lt;code&gt;lifespan&lt;/code&gt; handler.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Startup Logic&lt;/strong&gt;: All the code &lt;strong&gt;before the &lt;code&gt;yield&lt;/code&gt; statement&lt;/strong&gt; is executed once when the server starts. Our logic here iterates through the &lt;code&gt;ARTICLES_DIR&lt;/code&gt;, validates that each item is a directory containing a &lt;code&gt;README.md&lt;/code&gt; file, and builds a list of dictionaries containing the article metadata.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Storing the Catalog&lt;/strong&gt;: The crucial line is &lt;code&gt;app.state.articles_catalog = ...&lt;/code&gt;. This takes our list of articles and stores it in the &lt;code&gt;app.state&lt;/code&gt; object, a shared space where we can hold data for the entire lifetime of the application.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The &lt;code&gt;yield&lt;/code&gt; Statement&lt;/strong&gt;: This is where the &lt;code&gt;lifespan&lt;/code&gt; handler pauses. The server is now running and will process requests until it receives a shutdown signal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shutdown Logic&lt;/strong&gt;: All the code &lt;strong&gt;after the &lt;code&gt;yield&lt;/code&gt; statement&lt;/strong&gt; is executed once when the server is shutting down. While we only have a &lt;code&gt;print&lt;/code&gt; statement, this is the correct place for cleanup logic, such as closing database connections.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Connecting to the App&lt;/strong&gt;: Finally, we pass our function to the &lt;code&gt;FastAPI&lt;/code&gt; constructor: &lt;code&gt;app = FastAPI(lifespan=lifespan)&lt;/code&gt;. This tells FastAPI to use our handler to manage its lifecycle.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Application and API Endpoints
&lt;/h3&gt;

&lt;p&gt;With the article catalog loaded into &lt;code&gt;app.state&lt;/code&gt; by the &lt;code&gt;lifespan&lt;/code&gt; handler, we can now define the application's core logic. This involves two primary responsibilities: serving the frontend application itself (the HTML, CSS, and JavaScript files) and exposing the data API that the frontend will use to fetch article content.&lt;/p&gt;

&lt;h4&gt;
  
  
  Serving the Frontend Application
&lt;/h4&gt;

&lt;p&gt;First, we must configure the server to deliver the static files that constitute our single-page application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Mount the static directory to serve CSS and JavaScript files.
&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/static&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;StaticFiles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;directory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;STATIC_DIR&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;static&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Configure Jinja2 for HTML templating.
&lt;/span&gt;&lt;span class="n"&gt;templates&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Jinja2Templates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;directory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;TEMPLATES_DIR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="c1"&gt;# --- Frontend Serving ---
&lt;/span&gt;&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;serve_frontend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Serves the main index.html single-page application.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;templates&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TemplateResponse&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;request&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;index.html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  Deconstruction: Serving the Frontend
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;app.mount()&lt;/code&gt;&lt;/strong&gt;: This operation attaches a self-contained application to a specific URL path. Here, we mount a &lt;code&gt;StaticFiles&lt;/code&gt; instance to the &lt;code&gt;/static&lt;/code&gt; path. This tells FastAPI that any request starting with &lt;code&gt;/static&lt;/code&gt; should be handled by serving a file directly from our &lt;code&gt;STATIC_DIR&lt;/code&gt; on the filesystem.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Jinja2Templates&lt;/code&gt;&lt;/strong&gt;: This line initializes the Jinja2 templating engine, telling it where to find our HTML files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@app.get("/")&lt;/code&gt;&lt;/strong&gt;: This is the root endpoint for our application. When a user navigates to the base URL, this function is called. It uses &lt;code&gt;templates.TemplateResponse&lt;/code&gt; to render our &lt;code&gt;index.html&lt;/code&gt; file and send it to the browser. This single file is the entire shell for our application.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  The Data API
&lt;/h4&gt;

&lt;p&gt;Next, we define the data endpoints that our JavaScript frontend will call to get the article index and content.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# --- API Endpoints ---
&lt;/span&gt;&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/api/articles&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_articles_index&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Returns a list of all discovered articles (slug and title).&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;hasattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;articles_catalog&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;articles_catalog&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="c1"&gt;# Return a list of dicts, excluding the server-side file path.
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;slug&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;slug&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;articles_catalog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;


&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/articles/{article_slug}/static/{file_path:path}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;serve_article_static&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;article_slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Serves a static file from within a specific article&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s directory.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;article_slug&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;articles_catalog&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;HTTPException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;detail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Article not found&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Construct the full path to the static file
&lt;/span&gt;    &lt;span class="n"&gt;article_dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;articles_catalog&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;article_slug&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;path&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;
    &lt;span class="n"&gt;static_file_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;article_dir&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;static&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;file_path&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;static_file_path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_file&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;HTTPException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;detail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Static file not found&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;FileResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;static_file_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/api/articles/{article_slug}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_article_content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;article_slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Returns the raw Markdown content of a specific article.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;article_slug&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;articles_catalog&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;HTTPException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;detail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Article not found&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;file_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;articles_catalog&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;article_slug&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;path&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;FileResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;media_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text/markdown; charset=utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  Deconstruction: The Data API
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;/api/articles&lt;/code&gt;&lt;/strong&gt;: This endpoint serves as the index. It reads the article catalog from &lt;code&gt;app.state&lt;/code&gt;, uses a list comprehension to format it into a clean list of slugs and titles, and returns it as a JSON response.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;/api/articles/{article_slug}&lt;/code&gt;&lt;/strong&gt;: This endpoint retrieves the content for a single article. It looks up the provided &lt;code&gt;article_slug&lt;/code&gt; in our in-memory catalog to find the path to the &lt;code&gt;README.md&lt;/code&gt; file and then uses &lt;code&gt;FileResponse&lt;/code&gt; to efficiently stream that file's content to the client.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;/articles/{article_slug}/static/{file_path:path}&lt;/code&gt;&lt;/strong&gt;: This is the endpoint that solves our relative image path problem. It takes both an &lt;code&gt;article_slug&lt;/code&gt; and a &lt;code&gt;file_path&lt;/code&gt; (which, thanks to the &lt;code&gt;:path&lt;/code&gt; converter, can contain slashes) to construct the exact location of a static asset within an article's subdirectory and serves it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Main Entry Point
&lt;/h3&gt;

&lt;p&gt;The final piece of our &lt;code&gt;server.py&lt;/code&gt; file is a special block that allows the application to be run directly as a script. This makes our server self-contained and easy to launch.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# --- Main Entry Point ---
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Allows the server to be run with `python3 server.py`.
    Configures Uvicorn to use the shared TLS certificates.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;uvicorn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;server:app&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.0.0.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8889&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# Matches the port exposed in the Dockerfile
&lt;/span&gt;        &lt;span class="nb"&gt;reload&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ssl_keyfile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;KEY_FILE&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;ssl_certfile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CERT_FILE&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Deconstruction: The &lt;code&gt;__main__&lt;/code&gt; Block
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;if __name__ == "__main__":&lt;/code&gt;&lt;/strong&gt;: This is a standard Python construct. The code inside this &lt;code&gt;if&lt;/code&gt; block will only run when the script is executed directly from the command line (e.g., &lt;code&gt;python3 server.py&lt;/code&gt;). It will not run if the script is imported as a module into another file, such as our test suite.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;uvicorn.run(...)&lt;/code&gt;&lt;/strong&gt;: This function programmatically starts the Uvicorn ASGI server, which is responsible for running our FastAPI application. We provide it with several key configuration arguments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;"server:app"&lt;/code&gt;&lt;/strong&gt;: This string tells Uvicorn how to find our application. It means: "in the file named &lt;code&gt;server.py&lt;/code&gt;, find the instance of the FastAPI application named &lt;code&gt;app&lt;/code&gt;."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;host="0.0.0.0"&lt;/code&gt;&lt;/strong&gt;: This is a critical setting for running inside a Docker container. It tells the server to listen for connections on all available network interfaces, which is necessary for the port mapping from the host machine to reach the container.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;reload=True&lt;/code&gt;&lt;/strong&gt;: This is a convenience feature for development. It tells Uvicorn to monitor the source files for changes and automatically restart the server when a file is saved.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ssl_keyfile&lt;/code&gt; and &lt;code&gt;ssl_certfile&lt;/code&gt;&lt;/strong&gt;: These arguments enable HTTPS by pointing Uvicorn to the TLS certificate and private key files that our &lt;code&gt;entrypoint.sh&lt;/code&gt; script generates.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;This concludes the deconstruction of our backend server. We now have a complete understanding of how it discovers content, serves a data API, and delivers the frontend application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part II: The Vanilla JS Frontend
&lt;/h2&gt;

&lt;p&gt;The frontend is responsible for everything the user sees and interacts with. It's the "reading room" of our application, built from three core files: &lt;code&gt;index.html&lt;/code&gt; for structure, &lt;code&gt;style.css&lt;/code&gt; for presentation, and &lt;code&gt;app.js&lt;/code&gt; for interactivity.&lt;/p&gt;

&lt;h3&gt;
  
  
  The HTML Shell (&lt;code&gt;index.html&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;index.html&lt;/code&gt; file is the skeleton of our single-page application. Its primary role is not to contain the article content itself, but to provide the basic page structure and to load all the CSS and JavaScript assets that will bring the application to life.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1.0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Article Viewer&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/marked/marked.min.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/marked-base-url"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/python.min.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/bash.min.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
        &lt;span class="nx"&gt;MathJax&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... configuration ... */&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"MathJax-script"&lt;/span&gt; &lt;span class="na"&gt;async&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/static/style.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/static/app.js"&lt;/span&gt; &lt;span class="na"&gt;defer&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;nav&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"sidebar"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Articles&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;ul&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"article-list"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;main&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"content"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Select an article from the sidebar to begin reading.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Deconstruction: The HTML Shell
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; Section&lt;/strong&gt;: This section is the "control panel" for our page. It contains metadata and, most importantly, links to all the external resources our application needs.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; Tags&lt;/strong&gt;: These pull in our stylesheets. We load the &lt;code&gt;atom-one-dark.min.css&lt;/code&gt; theme for our code blocks, followed by our own &lt;code&gt;style.css&lt;/code&gt; for the main layout.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; Tags&lt;/strong&gt;: These load the JavaScript. The order is critical: the third-party libraries (&lt;code&gt;marked&lt;/code&gt;, &lt;code&gt;highlight.js&lt;/code&gt;, &lt;code&gt;MathJax&lt;/code&gt;, etc.) must be loaded &lt;em&gt;before&lt;/em&gt; our own &lt;code&gt;app.js&lt;/code&gt; script, which depends on them. The &lt;code&gt;defer&lt;/code&gt; attribute on &lt;code&gt;app.js&lt;/code&gt; tells the browser to wait until the HTML document is fully parsed before executing our script.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;The &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; Section&lt;/strong&gt;: The body defines the visible structure. It is kept minimal and semantic.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;&amp;lt;nav id="sidebar"&amp;gt;&lt;/code&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;code&gt;&amp;lt;main id="content"&amp;gt;&lt;/code&gt;&lt;/strong&gt;: We define two primary containers for our layout. Their &lt;code&gt;id&lt;/code&gt; attributes are the crucial "hooks" that our &lt;code&gt;app.js&lt;/code&gt; script will use to select these elements and dynamically inject content into them. They begin with simple placeholder text, which will be replaced as soon as our JavaScript runs.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  The CSS Styling (&lt;code&gt;style.css&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;style.css&lt;/code&gt; file is the "interior design" for our HTML structure. Its purpose is to control the layout, colors, and typography, transforming the raw HTML into a clean, readable, and polished user interface. Our stylesheet is built on a few key, modern CSS principles.&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Theming with CSS Variables
&lt;/h4&gt;

&lt;p&gt;At the top of the file, we define all our recurring colors and fonts inside a &lt;code&gt;:root&lt;/code&gt; block.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;--bg-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#282c34&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;--sidebar-bg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#21252b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;--text-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#abb2bf&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;--header-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#ffffff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;--link-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#61afef&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;--font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;-apple-system&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BlinkMacSystemFont&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;"Segoe UI"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Roboto&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Helvetica&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Arial&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a powerful feature known as &lt;strong&gt;CSS Custom Properties&lt;/strong&gt;, or variables. By defining these values once and reusing them throughout the stylesheet (e.g., &lt;code&gt;color: var(--text-color);&lt;/code&gt;), we make our application's theme incredibly easy to manage. To change the entire color scheme, we only need to edit the values in this single location.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Two-Column Layout with CSS Grid
&lt;/h4&gt;

&lt;p&gt;The core of our page layout is achieved with a simple but powerful CSS Grid definition.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.container&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;grid-template-columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;280px&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;fr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100vh&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By setting &lt;code&gt;display: grid&lt;/code&gt; on our main container, we unlock the two-dimensional layout capabilities of CSS Grid. The &lt;code&gt;grid-template-columns&lt;/code&gt; property defines our two columns: the first is a fixed &lt;code&gt;280px&lt;/code&gt; wide (for the sidebar), and the second (&lt;code&gt;1fr&lt;/code&gt;) is a flexible unit that automatically takes up the remaining "one fraction" of the available space.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Handling Overflow and Scrolling
&lt;/h4&gt;

&lt;p&gt;A crucial detail for a good user experience is managing content that is longer than the screen.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nf"&gt;#sidebar&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;overflow-y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nf"&gt;#content&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;overflow-y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By setting &lt;code&gt;overflow-y: auto&lt;/code&gt; on both our sidebar and main content areas, we ensure that if either the navigation list or the article content becomes too long, it will get its own independent, vertical scrollbar without breaking the overall page layout.&lt;/p&gt;

&lt;h4&gt;
  
  
  4. Responsive Content
&lt;/h4&gt;

&lt;p&gt;To prevent large images from breaking our layout, we apply a simple, global rule for all images within the content area.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nf"&gt;#content&lt;/span&gt; &lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This rule ensures that an image will never be wider than its container. If a large image is loaded, it will automatically scale down to fit, while &lt;code&gt;height: auto&lt;/code&gt; maintains its original aspect ratio.&lt;/p&gt;

&lt;h3&gt;
  
  
  The JavaScript Brain (&lt;code&gt;app.js&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;This file contains all the client-side logic that transforms our static HTML shell into a dynamic application. It is responsible for fetching data from our backend API, manipulating the DOM to display that data, and responding to user interaction.&lt;/p&gt;

&lt;h4&gt;
  
  
  Initialization and Event Listeners
&lt;/h4&gt;

&lt;p&gt;The script begins by setting up a primary event listener that waits for the entire HTML document to be loaded and parsed before any of our code runs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DOMContentLoaded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sidebar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;article-list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;content&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// ... all functions are defined here ...&lt;/span&gt;

    &lt;span class="c1"&gt;// --- Initial Load ---&lt;/span&gt;
    &lt;span class="nf"&gt;loadArticleList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;initialSlug&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;initialSlug&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;loadArticleContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;initialSlug&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  Deconstruction: Initialization
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;DOMContentLoaded&lt;/code&gt;&lt;/strong&gt;: By wrapping our code in this event listener, we ensure that we don't try to access elements like &lt;code&gt;#sidebar&lt;/code&gt; before they are available, which prevents errors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Initial Calls&lt;/strong&gt;: Once the page is ready, the script immediately calls &lt;code&gt;loadArticleList()&lt;/code&gt; to populate the navigation menu. It then checks if the URL has a hash fragment (e.g., &lt;code&gt;#0001_basic_fastapi&lt;/code&gt;) and, if so, calls &lt;code&gt;loadArticleContent()&lt;/code&gt; to support deeplinking.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Building the Navigation (&lt;code&gt;loadArticleList&lt;/code&gt;)
&lt;/h4&gt;

&lt;p&gt;This function is responsible for communicating with our server to discover which articles are available and then building the sidebar navigation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loadArticleList&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/articles&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`HTTP error! Status: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;articles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="nx"&gt;sidebar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Clear any existing content&lt;/span&gt;
        &lt;span class="nx"&gt;articles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;article&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;li&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;li&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`#&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="nf"&gt;loadArticleContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;

            &lt;span class="nx"&gt;li&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;sidebar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;li&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// ... error handling ...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  Deconstruction: Building Navigation
&lt;/h5&gt;

&lt;p&gt;Using &lt;code&gt;async/await&lt;/code&gt;, the function first &lt;code&gt;fetch&lt;/code&gt;es the article index from our &lt;code&gt;/api/articles&lt;/code&gt; endpoint and parses the &lt;code&gt;json&lt;/code&gt; response. It then iterates over the returned array using &lt;code&gt;forEach&lt;/code&gt;, dynamically creating an &lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; element for each article. A &lt;code&gt;'click'&lt;/code&gt; event listener is attached to each link, which prevents the default browser navigation and instead calls our &lt;code&gt;loadArticleContent&lt;/code&gt; function.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Rendering Pipeline (&lt;code&gt;loadArticleContent&lt;/code&gt;)
&lt;/h4&gt;

&lt;p&gt;This function is the core of the user experience. It takes an article slug, fetches its content, and orchestrates the multi-step process of rendering it to the screen.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loadArticleContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;p&amp;gt;Loading...&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/api/articles/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;markdown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// The Rendering Pipeline&lt;/span&gt;
        &lt;span class="nx"&gt;marked&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;markedBaseUrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/articles/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/`&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;marked&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;markdown&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nx"&gt;hljs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;highlightAll&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nx"&gt;MathJax&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;typeset&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// ... error handling ...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  Deconstruction: The Rendering Pipeline
&lt;/h5&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Fetch&lt;/strong&gt;: It makes an &lt;code&gt;async&lt;/code&gt; call to &lt;code&gt;/api/articles/{slug}&lt;/code&gt; to get the raw Markdown text.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Configure&lt;/strong&gt;: It calls &lt;code&gt;marked.use(markedBaseUrl.baseUrl(...))&lt;/code&gt; to configure the renderer. This crucial step ensures that any relative image paths in the Markdown are correctly rewritten to point to our article-specific asset endpoint.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Parse &amp;amp; Inject&lt;/strong&gt;: It calls &lt;code&gt;marked.parse(markdown)&lt;/code&gt; to convert the Markdown to an HTML string and immediately injects it into the main content area's &lt;code&gt;innerHTML&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Enhance&lt;/strong&gt;: After the new HTML is in the DOM, it makes two final calls: &lt;code&gt;hljs.highlightAll()&lt;/code&gt; to apply syntax highlighting to any code blocks, and &lt;code&gt;MathJax.typeset()&lt;/code&gt; to find and render any LaTeX formulas.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This completes the walkthrough of our application's implementation. We have seen how the Python backend and vanilla JavaScript frontend work together to create a seamless user experience.&lt;/p&gt;

&lt;h1&gt;
  
  
  Testing and Verification
&lt;/h1&gt;

&lt;h2&gt;
  
  
  A Professional's Workflow: Why We Test
&lt;/h2&gt;

&lt;p&gt;Writing code that works is only the first step. Professional software engineering requires us to ensure that the code is correct and, just as importantly, that it &lt;em&gt;stays&lt;/em&gt; correct as we add new features or refactor it in the future. Automated testing is the practice of writing code to verify our application's code, creating a safety net that catches regressions before they reach users.&lt;/p&gt;

&lt;p&gt;Our project employs a comprehensive, two-tiered testing strategy:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Backend Unit Tests&lt;/strong&gt;: To verify the API logic in a fast, isolated environment.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Frontend End-to-End Tests&lt;/strong&gt;: To verify the complete user experience in a real browser.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Part I: Backend Unit Testing with pytest (&lt;code&gt;test_server.py&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;The goal of our backend tests is to confirm that each API endpoint behaves as expected—returning the correct data for valid requests and the correct errors for invalid ones. We achieve this without needing to run a real web server or browser.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Tools and The Challenge of Isolation
&lt;/h3&gt;

&lt;p&gt;Our primary tools are &lt;strong&gt;&lt;code&gt;pytest&lt;/code&gt;&lt;/strong&gt;, a powerful and popular Python test runner, and FastAPI's built-in &lt;strong&gt;&lt;code&gt;TestClient&lt;/code&gt;&lt;/strong&gt;. The &lt;code&gt;TestClient&lt;/code&gt; allows us to send simulated HTTP requests directly to our application in memory, which is incredibly fast and efficient.&lt;/p&gt;

&lt;p&gt;However, this presents a challenge: our server is designed to scan a real filesystem directory (&lt;code&gt;~/articles&lt;/code&gt;) to discover content. How can we test this logic without our tests becoming fragile and dependent on the actual articles in the repository? If we added a new article, we wouldn't want our existing tests to suddenly fail. The solution is to create a temporary, isolated "laboratory" environment for each test.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Solution: The &lt;code&gt;mock_fs&lt;/code&gt; Fixture
&lt;/h3&gt;

&lt;p&gt;To solve the isolation problem, we use &lt;code&gt;pytest&lt;/code&gt;'s powerful &lt;strong&gt;fixture&lt;/strong&gt; system. A fixture is a function that runs before each test function, providing it with data or setting up a specific state. Our &lt;code&gt;mock_fs&lt;/code&gt; fixture creates a completely separate, temporary "laboratory" filesystem for each test to run in.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@pytest.fixture&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;mock_fs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tmp_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;monkeypatch&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    A pytest fixture to create a temporary, isolated filesystem for testing.
    It mocks the directory structure and patches the constants in the server
    module to use these temporary paths. This ensures tests are hermetic.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="c1"&gt;# 1. Create mock directories based on the real structure
&lt;/span&gt;    &lt;span class="n"&gt;mock_home&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tmp_path&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;home&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;testuser&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;mock_articles_dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mock_home&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;articles&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;mock_viewer_dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mock_articles_dir&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0003_html_css_javascript_article_viewer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;mock_templates_dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mock_viewer_dir&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;templates&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="n"&gt;mock_articles_dir&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mkdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parents&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;mock_viewer_dir&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mkdir&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;mock_templates_dir&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mkdir&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;# 2. Monkeypatch the server's global path constants to use our mock paths
&lt;/span&gt;    &lt;span class="n"&gt;monkeypatch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;viewer_server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;HOME_DIR&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mock_home&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;monkeypatch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;viewer_server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ARTICLES_DIR&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mock_articles_dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;monkeypatch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;viewer_server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;VIEWER_DIR&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mock_viewer_dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;monkeypatch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;viewer_server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;TEMPLATES_DIR&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mock_templates_dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 3. Create a dummy index.html needed by the serve_frontend endpoint
&lt;/span&gt;    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mock_templates_dir&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;index.html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;write_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;html&amp;gt;Hello Test&amp;lt;/html&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;mock_articles_dir&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Deconstruction: How the Fixture Works
&lt;/h4&gt;

&lt;p&gt;This fixture uses two powerful, built-in &lt;code&gt;pytest&lt;/code&gt; fixtures to achieve its goal:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Creating a Temporary Filesystem with &lt;code&gt;tmp_path&lt;/code&gt;&lt;/strong&gt;: The &lt;code&gt;tmp_path&lt;/code&gt; fixture provides a &lt;code&gt;pathlib.Path&lt;/code&gt; object pointing to a unique temporary directory. This directory is created before the test runs and is completely destroyed after it finishes. We use this to build a clean, predictable mock directory structure (&lt;code&gt;mock_home&lt;/code&gt;, &lt;code&gt;mock_articles_dir&lt;/code&gt;, etc.) from scratch.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Rerouting the Server with &lt;code&gt;monkeypatch&lt;/code&gt;&lt;/strong&gt;: The &lt;code&gt;monkeypatch&lt;/code&gt; fixture allows us to safely modify variables, functions, or classes during a test and automatically restores the original state afterward. The crucial line is &lt;code&gt;monkeypatch.setattr(viewer_server, "ARTICLES_DIR", mock_articles_dir)&lt;/code&gt;. This line dynamically changes the &lt;code&gt;ARTICLES_DIR&lt;/code&gt; constant inside our &lt;code&gt;server.py&lt;/code&gt; module, effectively "tricking" the server into looking at our temporary directory instead of the real one.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By combining these two tools, our &lt;code&gt;mock_fs&lt;/code&gt; fixture provides every test function with a perfectly clean, isolated, and predictable environment. This makes our tests fast, reliable, and completely independent of any external state.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deconstructing the Test Cases
&lt;/h3&gt;

&lt;p&gt;With the &lt;code&gt;mock_fs&lt;/code&gt; fixture handling the complex setup, our test functions become clean, readable, and focused. Each test follows the standard &lt;strong&gt;Arrange-Act-Assert&lt;/strong&gt; pattern.&lt;/p&gt;

&lt;h4&gt;
  
  
  Example 1: The Success Case
&lt;/h4&gt;

&lt;p&gt;This test verifies that the main &lt;code&gt;/api/articles&lt;/code&gt; endpoint works correctly when there are valid articles on the filesystem.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_get_articles_index_success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mock_fs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Tests that the index endpoint returns a correctly formatted list of articles.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="c1"&gt;# Arrange: Create some dummy articles in the mocked filesystem
&lt;/span&gt;    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mock_fs&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0002_second_article&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;mkdir&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mock_fs&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0002_second_article&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;README.md&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;touch&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mock_fs&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0001_first_article&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;mkdir&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mock_fs&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0001_first_article&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;README.md&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;touch&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;TestClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;viewer_server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Act
&lt;/span&gt;        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/api/articles&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Assert
&lt;/span&gt;        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;slug&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0001_first_article&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;First Article&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  Deconstruction:
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Arrange&lt;/strong&gt;: This is the setup phase. We take the empty &lt;code&gt;mock_fs&lt;/code&gt; directory provided by our fixture and populate it with the specific subdirectories and dummy &lt;code&gt;README.md&lt;/code&gt; files that this particular test requires.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Act&lt;/strong&gt;: This is where we perform the action we want to test. We instantiate the &lt;code&gt;TestClient&lt;/code&gt; and send a &lt;code&gt;GET&lt;/code&gt; request to the &lt;code&gt;/api/articles&lt;/code&gt; endpoint. When the &lt;code&gt;TestClient&lt;/code&gt; starts, it triggers our server's &lt;code&gt;lifespan&lt;/code&gt; function, which now scans our temporary, arranged directory.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Assert&lt;/strong&gt;: This is the verification phase. We check that the &lt;code&gt;response.status_code&lt;/code&gt; is &lt;code&gt;200 OK&lt;/code&gt;, that the returned JSON data contains two articles, and that their content (slug and title) is correctly formatted and sorted.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Example 2: The Failure Case
&lt;/h4&gt;

&lt;p&gt;It is just as important to test that our application handles errors correctly. This test verifies that requesting a non-existent article returns a &lt;code&gt;404 Not Found&lt;/code&gt; error.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_get_article_content_not_found&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mock_fs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Tests that requesting a non-existent article slug returns a 404.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;TestClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;viewer_server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/api/articles/non_existent_slug&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  Deconstruction:
&lt;/h5&gt;

&lt;p&gt;This test is simpler. The &lt;strong&gt;Arrange&lt;/strong&gt; step is handled entirely by the &lt;code&gt;mock_fs&lt;/code&gt; fixture, which provides a clean slate. The &lt;strong&gt;Act&lt;/strong&gt; step is to request a slug that we know does not exist. The &lt;strong&gt;Assert&lt;/strong&gt; step is a single, clear check that the server responded with the correct &lt;code&gt;404&lt;/code&gt; status code.&lt;/p&gt;

&lt;p&gt;The rest of the tests in &lt;code&gt;test_server.py&lt;/code&gt; follow these same patterns, covering every success and failure branch for each endpoint. This gives us high confidence in our backend's reliability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part II: Frontend End-to-End Testing with Playwright (&lt;code&gt;test_e2e.py&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;While our backend unit tests give us confidence in our API's logic, they don't tell us if the application actually works from a user's perspective. To verify the complete system—from the JavaScript in the browser to the Python on the server—we use &lt;strong&gt;End-to-End (E2E) tests&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The goal of E2E testing is to write a script that automates a real web browser to simulate a user's journey. Our tool for this is &lt;strong&gt;Playwright&lt;/strong&gt;, a modern browser automation framework that we can control entirely from Python.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Challenge of a Live Server
&lt;/h3&gt;

&lt;p&gt;Unlike the &lt;code&gt;TestClient&lt;/code&gt; which runs our app in memory, a browser-based test needs to connect to a real, live web server over the network. Our test suite needs a way to start our server before the tests run and shut it down cleanly afterward.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Solution: The &lt;code&gt;live_server_process&lt;/code&gt; Fixture
&lt;/h3&gt;

&lt;p&gt;We solve this by creating a session-scoped &lt;code&gt;pytest&lt;/code&gt; fixture that manages the server's lifecycle.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@pytest.fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;session&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;live_server_process&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    A session-scoped fixture that starts the server in a detached tmux
    session and uses a robust polling loop to wait for it to be ready.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;start_cmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tmux new -s test_server -d &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;. .venv/bin/activate &amp;amp;&amp;amp; python3 server.py&lt;/span&gt;&lt;span class="sh"&gt;'"&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start_cmd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shell&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cwd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;..&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Wait for the server to be ready with a robust polling loop.
&lt;/span&gt;        &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;127.0.0.1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8889&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;  &lt;span class="c1"&gt;# Poll for up to 10 seconds
&lt;/span&gt;            &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_connection&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                    &lt;span class="k"&gt;break&lt;/span&gt;
            &lt;span class="nf"&gt;except &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;ConnectionRefusedError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;pytest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Server at &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; did not start within 10 seconds.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;BASE_URL&lt;/span&gt;  &lt;span class="c1"&gt;# The tests run at this point
&lt;/span&gt;
    &lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Teardown: Cleanly kill the tmux session.
&lt;/span&gt;        &lt;span class="n"&gt;kill_cmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tmux kill-session -t test_server&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kill_cmd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shell&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DEVNULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Deconstruction:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;scope="session"&lt;/code&gt;&lt;/strong&gt;: This decorator tells &lt;code&gt;pytest&lt;/code&gt; to run this fixture only &lt;strong&gt;once&lt;/strong&gt; for the entire test session, which is efficient.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;subprocess&lt;/code&gt; and &lt;code&gt;tmux&lt;/code&gt;&lt;/strong&gt;: The fixture uses Python's &lt;code&gt;subprocess&lt;/code&gt; library to execute a &lt;code&gt;tmux&lt;/code&gt; command, which starts our &lt;code&gt;server.py&lt;/code&gt; in a detached, background session.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Polling Loop&lt;/strong&gt;: Because the server takes a moment to start, the script immediately enters a polling loop. It repeatedly tries to open a socket connection to &lt;code&gt;127.0.0.1:8889&lt;/code&gt;. This loop continues until a connection is successful, proving the server is up and ready. This is a robust way to handle process startup.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;yield&lt;/code&gt; and Teardown&lt;/strong&gt;: The &lt;code&gt;yield&lt;/code&gt; keyword passes control to the tests. The &lt;code&gt;finally&lt;/code&gt; block guarantees that after all tests have completed, the &lt;code&gt;tmux kill-session&lt;/code&gt; command is run to cleanly terminate the server process.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Deconstructing an E2E Test Case
&lt;/h3&gt;

&lt;p&gt;With the server running, an E2E test is simply a script that gives the browser instructions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_article_click_and_render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;live_server_process&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Tests clicking an article and verifying its content is rendered.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;sync_playwright&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new_context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ignore_https_errors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new_page&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BASE_URL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_by_role&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;link&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Docker Dev Environment&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="n"&gt;content_heading&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;#content h2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;has_text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;A Primer on Isolation&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content_heading&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_be_visible&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Deconstruction:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;with sync_playwright()...&lt;/code&gt;&lt;/strong&gt;: This block manages the browser lifecycle, starting it and ensuring it closes correctly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;page.goto(...)&lt;/code&gt;&lt;/strong&gt;: This command navigates the automated browser to our running server's URL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;page.get_by_role(...).click()&lt;/code&gt;&lt;/strong&gt;: This is the core of the user simulation. Playwright finds an element just like a user would (in this case, a link with the text "Docker Dev Environment") and simulates a click.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;expect(locator).to_be_visible()&lt;/code&gt;&lt;/strong&gt;: This is the assertion. Playwright's &lt;code&gt;expect&lt;/code&gt; function has a crucial feature: &lt;strong&gt;auto-waiting&lt;/strong&gt;. It will automatically wait for a few seconds for the content to appear on the screen before checking the assertion, which makes the test extremely reliable and free of the flaky timing issues that plague older testing tools.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;In this article, we've journeyed from a simple problem—the poor formatting of technical content on the web—to a complete, first-principles solution. We have successfully architected and built a production-grade, self-hosted article viewer, complete with a high-performance FastAPI backend, a dynamic vanilla JavaScript frontend, and a comprehensive, two-tiered automated testing suite to guarantee its reliability.&lt;/p&gt;

&lt;p&gt;More importantly than the final application, however, is the methodology we followed. By deliberately choosing a lean stack and deconstructing each component—from the server's &lt;code&gt;lifespan&lt;/code&gt; handler to the frontend's rendering pipeline and the isolated test fixtures—we have revealed the fundamental patterns that power modern web development. You now have not just a functional application, but a deep, practical understanding of the client-server model, API design, the SPA lifecycle, and professional verification workflows.&lt;/p&gt;

&lt;p&gt;This blueprint is now yours to build upon. With this foundation, you have the knowledge to extend this viewer with new features, adapt its architecture for your own projects, or confidently build entirely new applications from scratch. What will you build next?&lt;/p&gt;

&lt;h1&gt;
  
  
  Markdown and MathJax Feature Demo
&lt;/h1&gt;

&lt;p&gt;This section demonstrates the rendering capabilities of this article viewer. It includes various levels of headings, text formatting, lists, links, images, code blocks with syntax highlighting, and mathematical equations rendered with MathJax.&lt;/p&gt;

&lt;h2&gt;
  
  
  Text Formatting and Headings
&lt;/h2&gt;

&lt;p&gt;This is a level 2 heading. You can create headings by starting a line with one or more &lt;code&gt;#&lt;/code&gt; characters.&lt;/p&gt;

&lt;h3&gt;
  
  
  Level 3 Heading
&lt;/h3&gt;

&lt;p&gt;Here is some standard paragraph text. Text can be formatted in various ways. For example, you can make text &lt;strong&gt;bold&lt;/strong&gt;, &lt;em&gt;italic&lt;/em&gt;, or even &lt;strong&gt;&lt;em&gt;bold and italic&lt;/em&gt;&lt;/strong&gt;. You can also use &lt;code&gt;strikethrough&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Level 4 Heading
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;This is a blockquote. It's useful for quoting text from another source or for emphasizing a particular passage. Blockquotes can span multiple lines.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h5&gt;
  
  
  Level 5 Heading
&lt;/h5&gt;

&lt;p&gt;And a final, level 6 heading is below.&lt;/p&gt;

&lt;h6&gt;
  
  
  Level 6 Heading
&lt;/h6&gt;

&lt;p&gt;This demonstrates the full hierarchy of available headings.&lt;/p&gt;




&lt;h2&gt;
  
  
  Lists and Links
&lt;/h2&gt;

&lt;p&gt;You can create both unordered and ordered lists.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This is an item in an unordered list.

&lt;ul&gt;
&lt;li&gt;You can nest lists by indenting.&lt;/li&gt;
&lt;li&gt;This is another nested item.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;This is a second top-level item.&lt;/li&gt;

&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt; This is an item in an ordered list.&lt;/li&gt;
&lt;li&gt; The numbering is handled automatically.&lt;/li&gt;
&lt;li&gt; Here is a link to the &lt;a href="https://marked.js.org/" rel="noopener noreferrer"&gt;official Marked.js documentation&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Images and Code
&lt;/h2&gt;

&lt;p&gt;Images with relative paths are handled correctly by the server.&lt;/p&gt;

&lt;p&gt;Inline code, like &lt;code&gt;const app = FastAPI()&lt;/code&gt;, is rendered with a distinct background. For larger blocks of code, fenced code blocks with syntax highlighting are used.&lt;/p&gt;

&lt;h3&gt;
  
  
  Python Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;read_root&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;A simple root endpoint.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Server is running&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  JavaScript Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DOMContentLoaded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;The document is ready!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Shell/Bash Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Update all packages and clean up&lt;/span&gt;
apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    cowsay &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class="k"&gt;*&lt;/span&gt;

cowsay &lt;span class="s2"&gt;"Hello World"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  C++ Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;iostream&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;vector&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"Hello"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"C++"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"World"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"from"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"container!"&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;" "&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;endl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Tables
&lt;/h2&gt;

&lt;p&gt;Markdown tables are also supported.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;th&gt;Implemented By&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Markdown Rendering&lt;/td&gt;
&lt;td&gt;Complete&lt;/td&gt;
&lt;td&gt;&lt;code&gt;marked.js&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Syntax Highlighting&lt;/td&gt;
&lt;td&gt;Complete&lt;/td&gt;
&lt;td&gt;&lt;code&gt;highlight.js&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Math Typesetting&lt;/td&gt;
&lt;td&gt;Complete&lt;/td&gt;
&lt;td&gt;&lt;code&gt;MathJax&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Mathematical Equations with MathJax
&lt;/h2&gt;

&lt;p&gt;Thanks to MathJax, we can render complex mathematical formulas written in LaTeX. This can be done inline, such as Einstein's famous equation, $E = mc^2$.&lt;/p&gt;

&lt;p&gt;For larger, display-style equations, we can use block-level rendering. Here is the probability density function for the normal distribution:&lt;/p&gt;

&lt;p&gt;$$f(x) = \frac{1}{\sigma\sqrt{2\pi}} \exp\left( -\frac{(x - \mu)^2}{2\sigma^2} \right)$$&lt;/p&gt;

&lt;p&gt;And here is the integral form of the Fourier Transform:&lt;/p&gt;

&lt;p&gt;$$X(\omega) = \int_{-\infty}^{\infty} x(t) e^{-j\omega t} dt$$&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>html</category>
      <category>css</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Basic FastAPI</title>
      <dc:creator>Warren Jitsing</dc:creator>
      <pubDate>Wed, 31 Dec 2025 10:28:03 +0000</pubDate>
      <link>https://dev.to/warren_jitsing_dd1c1d6fc6/basic-fastapi-pko</link>
      <guid>https://dev.to/warren_jitsing_dd1c1d6fc6/basic-fastapi-pko</guid>
      <description>&lt;p&gt;GitHub: &lt;a href="https://github.com/InfiniteConsult/0001_basic_fastapi" rel="noopener noreferrer"&gt;https://github.com/InfiniteConsult/0001_basic_fastapi&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This was the first article I wrote and my most gentle post so far.&lt;/p&gt;




&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;In the landscape of Python web frameworks, FastAPI distinguishes itself through its high performance and modern, type-driven design. It is an indispensable tool not only for building production-grade APIs but also for the critical task of verifying the conformance of low-level, handwritten network protocols.&lt;/p&gt;

&lt;p&gt;This guide provides a first-principles walkthrough for setting up a combined HTTP and WebSocket server, covering the complete lifecycle from environment setup to automated testing.&lt;/p&gt;

&lt;h1&gt;
  
  
  Foundations
&lt;/h1&gt;

&lt;h2&gt;
  
  
  HTTP
&lt;/h2&gt;

&lt;p&gt;The Hypertext Transfer Protocol (HTTP) is the bedrock of data communication on the web, powering everything from browser requests to complex REST APIs. This guide will focus specifically on HTTP/1.1, as its human-readable, text-based format is ideal for understanding the protocol's fundamental mechanics.&lt;/p&gt;

&lt;p&gt;HTTP/1.1 operates on a strict request-response cycle. A client always initiates communication by sending a request to a server, and the server's sole job is to return a single response. The server cannot spontaneously send data; it must wait for a client's request. Our examples will concentrate on the two most common request types: GET to retrieve resources and POST to submit data.&lt;/p&gt;

&lt;p&gt;A critical feature of HTTP/1.1 is that it is a text-based protocol, unlike the complex binary framing used in HTTP/2. Every message is constructed as a simple string of characters, which is then encoded as a raw sequence of bytes for transmission. At a low level, you can think of this data as a &lt;code&gt;bytes&lt;/code&gt; object in Python, an &lt;code&gt;unsigned char*&lt;/code&gt; buffer in C, a &lt;code&gt;std::vector&amp;lt;std::byte&amp;gt;&lt;/code&gt; in C++, or a &lt;code&gt;Vec&amp;lt;u8&amp;gt;&lt;/code&gt; in Rust.&lt;/p&gt;

&lt;h3&gt;
  
  
  GET request
&lt;/h3&gt;

&lt;p&gt;The HTTP GET method is designed for a single purpose: to retrieve a representation of a specified resource. As a read-only operation, it is considered "safe," meaning it should not alter the state of the resource.&lt;/p&gt;

&lt;p&gt;A key characteristic of a GET request is that it contains no message body. While parameters can be sent to the server through the URL's query string (e.g., &lt;code&gt;GET /search?topic=api&amp;amp;page=2 HTTP/1.1&lt;/code&gt;), the request itself carries no payload.&lt;/p&gt;

&lt;p&gt;Like all HTTP/1.1 messages, each line is terminated by a carriage return and newline (&lt;code&gt;\r\n&lt;/code&gt;). A final blank line (&lt;code&gt;\r\n&lt;/code&gt; on its own) signals the end of the headers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET / HTTP/1.1\r\n
Host: www.example.com\r\n
Connection: close\r\n
User-Agent: SimpleClient/1.0\r\n
\r\n
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  POST request
&lt;/h3&gt;

&lt;p&gt;The HTTP POST method is used to submit an entity to the specified resource, often causing a change in state or the creation of a new resource on the server. It is the primary method for sending user-generated data to a web server.&lt;/p&gt;

&lt;p&gt;Unlike GET, a POST request is defined by its inclusion of a payload in the message body. To ensure the server can correctly process this payload, two headers are critical:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Content-Type: Specifies the media type of the body (e.g., application/json, multipart/form-data).&lt;/li&gt;
&lt;li&gt;Content-Length: Indicates the exact size of the body in bytes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because POST is designed to create or update resources, it is neither "safe" nor "idempotent"—meaning that sending the same request multiple times may have additional side effects, such as creating duplicate entries.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /api/users HTTP/1.1\r\n
Host: api.example.com\r\n
Content-Type: application/json\r\n
Content-Length: 25\r\n
Connection: close\r\n
\r\n
{"name": "monday","id": 7}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Headers
&lt;/h3&gt;

&lt;p&gt;HTTP headers are the key-value pairs that carry metadata and control information for both requests and responses. Think of them as the instructions on the outside of a package; they describe the contents, specify the destination, and provide handling directives without altering the package's contents (the message body).&lt;/p&gt;

&lt;p&gt;Each header consists of a case-insensitive name, a colon (:), and a value. While there are hundreds of possible headers, they generally fall into a few key roles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Describing the Request: Headers like Host specify the server a request is for, while User-Agent identifies the client making the request.&lt;/li&gt;
&lt;li&gt;Describing the Body: For messages with a payload (like POST), Content-Type defines the data format and Content-Length specifies its size.&lt;/li&gt;
&lt;li&gt;Controlling Behavior: Headers can act as directives. For example, Connection: close tells the server to close the socket after the response, and Cache-Control dictates how the response should be cached by browsers and proxies.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Together, this extensible system of headers governs everything from authentication and content negotiation to redirection and cookie management, forming the operational backbone of every HTTP message.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Note on HTTPS and Security
&lt;/h3&gt;

&lt;p&gt;HTTPS (Hypertext Transfer Protocol Secure) is not a separate protocol from HTTP. It is simply standard HTTP communication layered on top of a secure TLS socket. Think of it as sending the exact same plaintext HTTP messages you've already seen, but through a private, encrypted tunnel.&lt;/p&gt;

&lt;p&gt;A TLS (Transport Layer Security) socket is an enhanced network socket that uses a cryptographic protocol to secure the connection. Before any HTTP data is exchanged, the client and server perform a "TLS handshake." During this handshake, they:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Negotiate which encryption ciphers to use.&lt;/li&gt;
&lt;li&gt;Verify the server's identity using its TLS certificate.&lt;/li&gt;
&lt;li&gt;Securely generate and exchange session keys for encrypting the data.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The importance of using HTTPS is that it provides three critical security guarantees:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Confidentiality: All traffic is encrypted, preventing eavesdroppers from reading sensitive information like passwords or credit card numbers.&lt;/li&gt;
&lt;li&gt;Authentication: The server's certificate proves that you are connected to the legitimate website and not an imposter.&lt;/li&gt;
&lt;li&gt;Integrity: TLS ensures that the data sent between the client and server has not been tampered with or altered in transit.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  WebSockets
&lt;/h2&gt;

&lt;p&gt;Where HTTP's request-response model is inefficient for real-time applications, the &lt;strong&gt;WebSocket protocol&lt;/strong&gt; provides a persistent, &lt;strong&gt;full-duplex communication channel&lt;/strong&gt; over a single TCP connection. This allows both the client and server to send data to each other independently and at any time, making it the ideal standard for applications like live chats, financial data streams, and multiplayer online games.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Handshake: Upgrading the Connection
&lt;/h3&gt;

&lt;p&gt;A WebSocket connection does not start on its own. It begins life as a standard HTTP/1.1 GET request, which includes special headers asking the server to "upgrade" the connection from HTTP to the WebSocket protocol.&lt;/p&gt;

&lt;p&gt;The client sends an &lt;code&gt;Upgrade&lt;/code&gt; request with a unique key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the server supports WebSockets and agrees to the upgrade, it responds with a 101 Switching Protocols status. It also computes an acceptance key from the client's key to prove it understood the request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Data Framing Protocol
&lt;/h3&gt;

&lt;p&gt;Once the handshake is successful, the underlying TCP socket remains open, but the communication is no longer HTTP. Instead, data is exchanged as a sequence of &lt;strong&gt;frames&lt;/strong&gt;. Each frame is a small piece of data with a header that describes its content.&lt;/p&gt;

&lt;p&gt;The frame header specifies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Frame Type&lt;/strong&gt;: Whether the payload is text, binary data, a ping/pong control message, or a request to close the connection.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Payload Length&lt;/strong&gt;: The size of the data in the frame.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Masking&lt;/strong&gt;: Whether the data is masked (a security feature for client-to-server frames).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This framing mechanism is highly efficient because it removes the overhead of HTTP headers from every message. Instead of a verbose, text-based request-response cycle, you have a lean, continuous stream of data flowing in both directions.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;FIN (1 bit)&lt;/strong&gt;: The "final" bit. It's set to &lt;code&gt;1&lt;/code&gt; for the final frame of a message, or &lt;code&gt;0&lt;/code&gt; for intermediate frames of a fragmented message.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RSV1, RSV2, RSV3 (1 bit each)&lt;/strong&gt;: Three reserved bits. They must be &lt;code&gt;0&lt;/code&gt; unless an extension is negotiated to define a meaning for them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Opcode (4 bits)&lt;/strong&gt;: Defines the interpretation of the payload data. Common opcodes include:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;0x1&lt;/code&gt;: Text Frame (the payload is UTF-8 text)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;0x2&lt;/code&gt;: Binary Frame (the payload is raw binary data)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;0x8&lt;/code&gt;: Connection Close Frame&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;0x9&lt;/code&gt;: Ping Frame&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;0xA&lt;/code&gt;: Pong Frame&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;MASK (1 bit)&lt;/strong&gt;: Defines whether the payload is "masked" (XORed with a key). All frames sent from a client to a server &lt;strong&gt;must&lt;/strong&gt; be masked.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Payload Length (7, 7+16, or 7+64 bits)&lt;/strong&gt;: A variable-length field describing the payload's size in bytes:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;If the value is 0-125&lt;/strong&gt;: This is the literal length of the payload.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If the value is 126&lt;/strong&gt;: The next 2 bytes (16 bits) are read as an unsigned integer to get the actual payload length.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If the value is 127&lt;/strong&gt;: The next 8 bytes (64 bits) are read as an unsigned integer to get the actual payload length.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Masking Key (4 bytes / 32 bits)&lt;/strong&gt;: &lt;em&gt;(Optional)&lt;/em&gt; If the &lt;code&gt;MASK&lt;/code&gt; bit is set to &lt;code&gt;1&lt;/code&gt;, this field is present and contains the key used to unmask the payload.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Payload Data (N bytes)&lt;/strong&gt;: The actual application data. If the frame was masked, the recipient must XOR this data with the masking key to retrieve the original content.&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Securing WebSockets with WSS
&lt;/h3&gt;

&lt;p&gt;Much like HTTPS, WebSocket Secure (wss://) is not a fundamentally different protocol. It is simply the standard WebSocket protocol running over a secure TLS connection.&lt;/p&gt;

&lt;p&gt;The relationship between ws:// and wss:// is directly analogous to that of http:// and https://. The initial HTTP Upgrade handshake for a wss:// connection must be sent over an established HTTPS connection. Once the handshake is complete, all subsequent WebSocket frames are transmitted through that same secure TLS tunnel.&lt;/p&gt;

&lt;p&gt;This provides the same three critical security benefits for your real-time communication. Any production application that sends sensitive information over a WebSocket must use the wss:// scheme to ensure data security.&lt;/p&gt;

&lt;h1&gt;
  
  
  Environment Setup
&lt;/h1&gt;

&lt;p&gt;Before writing any code, we need to establish a clean, isolated development environment and install the necessary dependencies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dependencies
&lt;/h2&gt;

&lt;p&gt;First, create a &lt;code&gt;requirements.txt&lt;/code&gt; file in your project directory with the following contents. These packages provide the core framework, application server, and testing utilities.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fastapi
uvicorn
websockets
httpx
pytest
pytest-cov
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;fastapi&lt;/strong&gt;: The high-performance web framework we are using to build the API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;uvicorn&lt;/strong&gt;: A lightning-fast ASGI (Asynchronous Server Gateway Interface) server, required to run our FastAPI application.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;websockets&lt;/strong&gt;: A high-performance WebSocket library that Uvicorn can use for its WebSocket implementation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;httpx&lt;/strong&gt;: A modern, async-capable HTTP client library. It is a required dependency for FastAPI's integrated &lt;code&gt;TestClient&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;pytest&lt;/strong&gt;: The powerful and popular framework we will use to write our automated tests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;pytest-cov&lt;/strong&gt;: Allows us to calculate the coverage of our application code by our test suite. It is a valuable metric for identifying parts of your codebase that are not exercised by your tests.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;With the &lt;code&gt;requirements.txt&lt;/code&gt; file in place, create and activate a Python virtual environment, then install the packages using &lt;code&gt;pip&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create a virtual environment named .venv&lt;/span&gt;
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv .venv

&lt;span class="c"&gt;# Activate the virtual environment (syntax for Linux/macOS)&lt;/span&gt;
&lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate

&lt;span class="c"&gt;# Confirm the venv's python is now on your PATH&lt;/span&gt;
which python3
&lt;span class="c"&gt;# Expected output: /path/to/your/project/.venv/bin/python3&lt;/span&gt;

&lt;span class="c"&gt;# Install all packages from the requirements file&lt;/span&gt;
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why do I put &lt;code&gt;python3 -m&lt;/code&gt; in front of my &lt;code&gt;pip&lt;/code&gt; command? Because I like to be as &lt;code&gt;explicit&lt;/code&gt; as a single-argument C++ constructor should be.&lt;/p&gt;

&lt;h2&gt;
  
  
  Securing the Local Server with TLS
&lt;/h2&gt;

&lt;p&gt;To enable HTTPS and WSS, our server needs a TLS certificate to handle encrypted traffic. While production servers require certificates issued by a trusted Certificate Authority (CA), we can generate a &lt;strong&gt;self-signed certificate&lt;/strong&gt; for local development. This allows us to test our secure &lt;code&gt;https://&lt;/code&gt; and &lt;code&gt;wss://&lt;/code&gt; endpoints without purchasing a formal certificate, and crucially, without modern browser warnings.&lt;/p&gt;

&lt;p&gt;Run the following &lt;code&gt;openssl&lt;/code&gt; command in your terminal. This command generates a modern certificate using Elliptic Curve Cryptography and includes a Subject Alternative Name (SAN) for broad browser compatibility. It will create a private key (&lt;code&gt;key.pem&lt;/code&gt;) and a public certificate (&lt;code&gt;cert.pem&lt;/code&gt;), valid for one year.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openssl req &lt;span class="nt"&gt;-x509&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-nodes&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-newkey&lt;/span&gt; ed25519 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-keyout&lt;/span&gt; key.pem &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-out&lt;/span&gt; cert.pem &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-sha256&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-days&lt;/span&gt; 365 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-subj&lt;/span&gt; &lt;span class="s1"&gt;'/CN=localhost'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-addext&lt;/span&gt; &lt;span class="s2"&gt;"subjectAltName = DNS:localhost,IP:127.0.0.1"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is a brief breakdown of what these flags do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;req -x509&lt;/code&gt;&lt;/strong&gt;: Creates a self-signed certificate, suitable for development.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;-nodes&lt;/code&gt;&lt;/strong&gt;: "No DES," meaning it creates a private key that is not encrypted with a passphrase.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;-newkey ed25519&lt;/code&gt;&lt;/strong&gt;: Generates a new, highly efficient &lt;strong&gt;ED25519 Elliptic Curve private key&lt;/strong&gt;. This is a modern alternative to RSA with equivalent security at smaller key sizes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;-keyout key.pem&lt;/code&gt;&lt;/strong&gt;: Specifies the output file for the private key.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;-out cert.pem&lt;/code&gt;&lt;/strong&gt;: Specifies the output file for the certificate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;-subj '/CN=localhost'&lt;/code&gt;&lt;/strong&gt;: Sets the certificate's subject information non-interactively, with the "Common Name" set to &lt;code&gt;localhost&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;-addext "subjectAltName = DNS:localhost,IP:127.0.0.1"&lt;/code&gt;&lt;/strong&gt;: &lt;strong&gt;Crucially&lt;/strong&gt;, this adds a Subject Alternative Name (SAN) extension. Modern browsers rely on SANs rather than the Common Name for hostname validation, ensuring your certificate is trusted for &lt;code&gt;localhost&lt;/code&gt; and &lt;code&gt;127.0.0.1&lt;/code&gt; without warnings.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Implementation
&lt;/h1&gt;

&lt;h2&gt;
  
  
  The HTTP Layer
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Basic Application Structure
&lt;/h3&gt;

&lt;p&gt;We'll begin by creating the core of our application in a file named &lt;code&gt;main.py&lt;/code&gt;. This initial version will focus on two things: instantiating the FastAPI application and setting up a "lifespan" event handler to manage resources needed by our server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Managing Resources with &lt;code&gt;lifespan&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;lifespan handler&lt;/strong&gt; is a function that FastAPI runs on startup and shutdown. It's the ideal place to manage resources that the application needs to operate, such as database connections, machine learning models, or, in our case, temporary files for demonstration purposes.&lt;/p&gt;

&lt;p&gt;We'll use this handler to create a static file when the server starts and cleanly remove it when the server stops.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;contextlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asynccontextmanager&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;

&lt;span class="c1"&gt;# Define the path for our static content
&lt;/span&gt;&lt;span class="n"&gt;STATIC_DIR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;static&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;DATA_FILE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;STATIC_DIR&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;


&lt;span class="nd"&gt;@asynccontextmanager&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lifespan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    This function runs on application startup and shutdown.
    It creates a dummy file for our FileResponse example.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Server is starting up...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Create the static directory and a test file
&lt;/span&gt;    &lt;span class="n"&gt;STATIC_DIR&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mkdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exist_ok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;DATA_FILE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;This is a test file served by FastAPI.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;yield&lt;/span&gt;  &lt;span class="c1"&gt;# The application runs while the lifespan is in this state
&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Server is shutting down...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Clean up the file and directory
&lt;/span&gt;    &lt;span class="n"&gt;DATA_FILE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unlink&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;STATIC_DIR&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rmdir&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lifespan&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;lifespan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;read_root&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;A simple root endpoint to confirm the server is running.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Server is running&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code sets up an &lt;code&gt;asynccontextmanager&lt;/code&gt; named &lt;code&gt;lifespan&lt;/code&gt;. Everything &lt;strong&gt;before the &lt;code&gt;yield&lt;/code&gt; statement&lt;/strong&gt; is executed on startup. The server then runs and processes requests. Once the server is shut down (e.g., with &lt;code&gt;Ctrl+C&lt;/code&gt;), the code &lt;strong&gt;after the &lt;code&gt;yield&lt;/code&gt;&lt;/strong&gt; is executed, ensuring our temporary file and directory are cleanly removed. We connect this handler to our application by passing it to the &lt;code&gt;FastAPI&lt;/code&gt; constructor.&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Concept: HTTP Status Codes
&lt;/h3&gt;

&lt;p&gt;Before we implement our endpoints, it's crucial to understand &lt;strong&gt;HTTP status codes&lt;/strong&gt;. Every response a server sends includes a three-digit code that tells the client the outcome of its request. Using the correct code is a fundamental part of building a well-behaved and predictable API.&lt;/p&gt;

&lt;p&gt;These codes are grouped into five classes, which are easy to remember by their first digit.&lt;/p&gt;




&lt;h4&gt;
  
  
  &lt;strong&gt;&lt;code&gt;2xx&lt;/code&gt;: Success&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;A &lt;code&gt;2xx&lt;/code&gt; code means the request was successfully received, understood, and accepted.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;200 OK&lt;/code&gt;&lt;/strong&gt;: The standard response for a successful GET request.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;201 Created&lt;/code&gt;&lt;/strong&gt;: Indicates that a new resource was successfully created as a result of a POST request.&lt;/li&gt;
&lt;/ul&gt;




&lt;h4&gt;
  
  
  &lt;strong&gt;&lt;code&gt;3xx&lt;/code&gt;: Redirection&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;A &lt;code&gt;3xx&lt;/code&gt; code indicates that the client must take additional action to complete the request, usually by making a new request to a different URL.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;307 Temporary Redirect&lt;/code&gt;&lt;/strong&gt;: The requested resource has temporarily moved to a new URL.&lt;/li&gt;
&lt;/ul&gt;




&lt;h4&gt;
  
  
  &lt;strong&gt;&lt;code&gt;4xx&lt;/code&gt;: Client Error&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;A &lt;code&gt;4xx&lt;/code&gt; code means there was an error, and it appears to be the client's fault.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;400 Bad Request&lt;/code&gt;&lt;/strong&gt;: The server could not understand the request due to invalid syntax.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;404 Not Found&lt;/code&gt;&lt;/strong&gt;: The server could not find the requested resource.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;422 Unprocessable Entity&lt;/code&gt;&lt;/strong&gt;: The request was well-formed, but contained semantic errors (e.g., a required field was missing in a JSON payload). FastAPI uses this frequently for validation errors.&lt;/li&gt;
&lt;/ul&gt;




&lt;h4&gt;
  
  
  &lt;strong&gt;&lt;code&gt;5xx&lt;/code&gt;: Server Error&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;A &lt;code&gt;5xx&lt;/code&gt; code indicates that the server failed to fulfill a valid request due to an error on its end.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;500 Internal Server Error&lt;/code&gt;&lt;/strong&gt;: A generic error message given when an unexpected condition was encountered and no more specific message is suitable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Returning the correct status code is essential. It allows browsers, automated clients, and caching proxies to behave correctly. A common anti-pattern is to return a &lt;code&gt;200 OK&lt;/code&gt; status with an error message in the body; the correct approach is to use a &lt;code&gt;4xx&lt;/code&gt; or &lt;code&gt;5xx&lt;/code&gt; code to signal the error at the protocol level.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementing the HTTP Endpoints
&lt;/h3&gt;

&lt;p&gt;With our basic application structure and &lt;code&gt;lifespan&lt;/code&gt; handler in place, we can now add the core logic for our HTTP server. We will implement three different endpoints to demonstrate FastAPI's primary capabilities: returning JSON data, serving static files, and handling application errors.&lt;/p&gt;

&lt;p&gt;Add the following imports to the top of your &lt;code&gt;main.py&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi.responses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FileResponse&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HTTPException&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h4&gt;
  
  
  Endpoint 1: Returning a JSON Response
&lt;/h4&gt;

&lt;p&gt;The most common use case for a web API is returning structured data. FastAPI makes this incredibly simple. If you return a Python dictionary from an endpoint function, FastAPI will automatically serialize it into a JSON response and set the &lt;code&gt;Content-Type&lt;/code&gt; header to &lt;code&gt;application/json&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Add the following endpoint to &lt;code&gt;main.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_status&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Returns the current status of the server.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ok&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h4&gt;
  
  
  Endpoint 2: Serving a Static File
&lt;/h4&gt;

&lt;p&gt;To serve a static file, like the one we created in our &lt;code&gt;lifespan&lt;/code&gt; handler, we use FastAPI's &lt;code&gt;FileResponse&lt;/code&gt;. This response type is highly efficient as it &lt;strong&gt;streams the file from disk&lt;/strong&gt; rather than loading it all into memory first. It also automatically determines the file's &lt;code&gt;Content-Type&lt;/code&gt; from its extension and sets the &lt;code&gt;Content-Length&lt;/code&gt; header.&lt;/p&gt;

&lt;p&gt;Add this endpoint to &lt;code&gt;main.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/static/data.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_data_file&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Serves the static data.txt file.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;FileResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DATA_FILE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h4&gt;
  
  
  Endpoint 3: Handling Errors
&lt;/h4&gt;

&lt;p&gt;Proper error handling is critical for a robust API. When a client requests a resource that doesn't exist, the server should return a &lt;code&gt;404 Not Found&lt;/code&gt; status code. FastAPI manages this through the &lt;code&gt;HTTPException&lt;/code&gt; class.&lt;/p&gt;

&lt;p&gt;When you &lt;strong&gt;raise an &lt;code&gt;HTTPException&lt;/code&gt;&lt;/strong&gt;, FastAPI stops processing the request and immediately sends an HTTP response with the specified status code and a JSON error body.&lt;/p&gt;

&lt;p&gt;Add this endpoint to &lt;code&gt;main.py&lt;/code&gt; to simulate looking for an item that may not exist:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/items/{item_id}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Returns a dummy item if the ID is valid, otherwise raises a 404.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;item_id&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;HTTPException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;detail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Item with ID &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;item_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; not found.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;item_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;item_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The One Item&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Endpoint 4: Handling a POST Request
&lt;/h4&gt;

&lt;p&gt;To handle a &lt;code&gt;POST&lt;/code&gt; request and receive a JSON body, you define an endpoint using the &lt;code&gt;@app.post()&lt;/code&gt; decorator. By type-hinting the function argument with the Pydantic model we just created (&lt;code&gt;CreateItemRequest&lt;/code&gt;), you tell FastAPI to expect a body with that structure.&lt;/p&gt;

&lt;p&gt;FastAPI will then automatically:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Read the body of the request as JSON.&lt;/li&gt;
&lt;li&gt; Validate that it matches the &lt;code&gt;CreateItemRequest&lt;/code&gt; model.&lt;/li&gt;
&lt;li&gt; If validation fails, it returns a &lt;code&gt;422 Unprocessable Entity&lt;/code&gt; error.&lt;/li&gt;
&lt;li&gt; If it succeeds, it passes the parsed data as the &lt;code&gt;item&lt;/code&gt; argument.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We also specify &lt;code&gt;status_code=201&lt;/code&gt; in the decorator to return a &lt;code&gt;201 Created&lt;/code&gt; status, which is the correct semantic response for a successful resource creation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseModel&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CreateItemRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;is_offer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;


&lt;span class="nd"&gt;@app.post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/items&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;CreateItemRequest&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Creates a new item from a request body.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Item created successfully&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;item_data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model_dump&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Complete HTTP Server Code (Revised)
&lt;/h3&gt;

&lt;p&gt;This final version of our &lt;code&gt;main.py&lt;/code&gt; file includes an entry point to run the server directly using &lt;code&gt;uvicorn.run()&lt;/code&gt;. This makes the application self-contained.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;contextlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asynccontextmanager&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;uvicorn&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HTTPException&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi.responses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FileResponse&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseModel&lt;/span&gt;


&lt;span class="c1"&gt;# Define the path for our static content
&lt;/span&gt;&lt;span class="n"&gt;STATIC_DIR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;static&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;DATA_FILE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;STATIC_DIR&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CreateItemRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;is_offer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;


&lt;span class="nd"&gt;@asynccontextmanager&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lifespan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Manages the creation and cleanup of a static file for demonstration.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Server is starting up...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;STATIC_DIR&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mkdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exist_ok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;DATA_FILE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;This is a test file served by FastAPI.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Server is shutting down...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;DATA_FILE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unlink&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;STATIC_DIR&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rmdir&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lifespan&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;lifespan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;read_root&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;A simple root endpoint to confirm the server is running.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Server is running&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_status&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Returns the current status of the server.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ok&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/static/data.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_data_file&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Serves the static data.txt file.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;FileResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DATA_FILE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/items/{item_id}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Returns a dummy item if the ID is valid, otherwise raises a 404.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;item_id&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;HTTPException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;detail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Item with ID &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;item_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; not found.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;item_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;item_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The One Item&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="nd"&gt;@app.post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/items&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;CreateItemRequest&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Creates a new item from a request body.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Item created successfully&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;item_data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model_dump&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;uvicorn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;main:app&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.0.0.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nb"&gt;reload&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ssl_keyfile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;key.pem&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ssl_certfile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cert.pem&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Running the Server
&lt;/h3&gt;

&lt;p&gt;Because we have included the &lt;code&gt;if __name__ == "__main__"&lt;/code&gt; block, which calls &lt;code&gt;uvicorn.run()&lt;/code&gt; for us, we can now start the server directly using the Python interpreter.&lt;/p&gt;

&lt;p&gt;Make sure you are in the same directory as your &lt;code&gt;main.py&lt;/code&gt;, &lt;code&gt;key.pem&lt;/code&gt;, and &lt;code&gt;cert.pem&lt;/code&gt; files, and run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 main.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your secure HTTP server will start on port 8000 and is now ready to accept requests.&lt;/p&gt;

&lt;h3&gt;
  
  
  Client-Side Testing Strategy
&lt;/h3&gt;

&lt;p&gt;A critical part of developing a robust API is having an automated test suite. FastAPI provides a &lt;code&gt;TestClient&lt;/code&gt; that makes it easy to test your application using frameworks like &lt;code&gt;pytest&lt;/code&gt;. The &lt;code&gt;TestClient&lt;/code&gt; allows you to send requests to your application in memory without needing to run a live server, resulting in fast and reliable tests.&lt;/p&gt;

&lt;p&gt;Create a new file named &lt;code&gt;test_http.py&lt;/code&gt; in your project directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi.testclient&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TestClient&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DATA_FILE&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_read_root&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Tests the root endpoint.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;TestClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Server is running&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_read_status&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Tests the /status endpoint.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;TestClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ok&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_read_file&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Tests the static file serving endpoint.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;TestClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/static/data.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
        &lt;span class="c1"&gt;# The file content is read from the original source for the assertion
&lt;/span&gt;        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;DATA_FILE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_text&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_get_item_found&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Tests the successful case for finding an item.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;TestClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/items/1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;item_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The One Item&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_get_item_not_found&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Tests the error case for not finding an item.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;TestClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/items/999&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;detail&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Item with ID 999 not found.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_create_item_success&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Tests the successful creation of an item via POST.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;TestClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;item_payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Test Item&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;is_offer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/items&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;item_payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;201&lt;/span&gt;  &lt;span class="c1"&gt;# 201 Created
&lt;/span&gt;        &lt;span class="n"&gt;response_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;response_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Item created successfully&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;response_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;item_data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;item_payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;response_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;item_data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;is_offer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;item_payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;is_offer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_create_item_validation_error_missing_field&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Tests for a validation error when a required field is missing.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;TestClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# The 'name' field is required by the Pydantic model, so this is invalid.
&lt;/span&gt;        &lt;span class="n"&gt;item_payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;is_offer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/items&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;item_payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;422&lt;/span&gt;  &lt;span class="c1"&gt;# 422 Unprocessable Entity
&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_create_item_validation_error_wrong_type&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Tests for a validation error when a field has the wrong data type.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;TestClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# The 'name' field should be a string, not an integer.
&lt;/span&gt;        &lt;span class="n"&gt;item_payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;is_offer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/items&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;item_payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;422&lt;/span&gt;  &lt;span class="c1"&gt;# 422 Unprocessable Entity
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this script, we import our &lt;code&gt;app&lt;/code&gt; object from &lt;code&gt;main.py&lt;/code&gt; and pass it to the &lt;code&gt;TestClient&lt;/code&gt;. Each function follows the &lt;code&gt;pytest&lt;/code&gt; convention of being named &lt;code&gt;test_*&lt;/code&gt;. Inside each function, we use the &lt;code&gt;client&lt;/code&gt; to make requests to our endpoints and then use &lt;code&gt;assert&lt;/code&gt; statements to verify that the status code and response body are exactly what we expect.&lt;/p&gt;

&lt;p&gt;You will notice in our test suite that the &lt;code&gt;TestClient&lt;/code&gt; is instantiated using a &lt;code&gt;with&lt;/code&gt; statement inside each test function, rather than being created once at the top of the file. This is a deliberate and critical pattern.&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;with TestClient(app) as client:&lt;/code&gt; creates a context manager that properly simulates the application's full &lt;strong&gt;lifespan&lt;/strong&gt;. When the &lt;code&gt;with&lt;/code&gt; block is entered, FastAPI runs the startup portion of our &lt;code&gt;lifespan&lt;/code&gt; handler (creating the &lt;code&gt;data.txt&lt;/code&gt; file). When the block is exited, it runs the shutdown portion (cleaning up the file).&lt;/p&gt;

&lt;p&gt;This ensures that each test runs within a clean, predictable application state, which is essential for reliable and isolated testing, especially when dealing with resources like files or database connections.&lt;/p&gt;




&lt;h3&gt;
  
  
  Running the Tests
&lt;/h3&gt;

&lt;p&gt;To execute the test suite, run &lt;code&gt;pytest&lt;/code&gt; from your terminal. The &lt;code&gt;-v&lt;/code&gt; flag provides more verbose output.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; pytest &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pytest will automatically discover and run the functions in &lt;code&gt;test_http.py&lt;/code&gt;, reporting the success or failure of each assertion.&lt;/p&gt;

&lt;h3&gt;
  
  
  Manual and Ad-Hoc Testing
&lt;/h3&gt;

&lt;p&gt;While an automated test suite is essential for validation, it's also useful to interact with your server manually. This is a great way to perform quick, ad-hoc tests or inspect the raw responses from your endpoints while developing.&lt;/p&gt;




&lt;h4&gt;
  
  
  Testing with &lt;code&gt;curl&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;curl&lt;/code&gt; command is a powerful, ubiquitous tool for making HTTP requests from the command line. While your &lt;code&gt;uvicorn&lt;/code&gt; server is running, you can open a new terminal to send &lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;POST&lt;/code&gt;, or any other type of request to your endpoints.&lt;/p&gt;

&lt;p&gt;Because we are using a &lt;strong&gt;self-signed certificate&lt;/strong&gt;, you must include the &lt;code&gt;-k&lt;/code&gt; (or &lt;code&gt;--insecure&lt;/code&gt;) flag. This tells &lt;code&gt;curl&lt;/code&gt; to proceed with the request even though it cannot verify the certificate against a trusted Certificate Authority.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Test the status endpoint&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-k&lt;/span&gt; https://localhost:8000/status
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"status"&lt;/span&gt;:&lt;span class="s2"&gt;"ok"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Test the static file endpoint&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-k&lt;/span&gt; https://localhost:8000/static/data.txt
This is a &lt;span class="nb"&gt;test &lt;/span&gt;file served by FastAPI.

&lt;span class="c"&gt;# Test the item-not-found error case&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-k&lt;/span&gt; https://localhost:8000/items/999
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"detail"&lt;/span&gt;:&lt;span class="s2"&gt;"Item with ID 999 not found."&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Test creating a new item via POST&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-k&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://localhost:8000/items &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"name": "My New Item", "is_offer": false}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Testing with a Web Browser
&lt;/h4&gt;

&lt;p&gt;You can also test your &lt;code&gt;GET&lt;/code&gt; endpoints directly in a web browser. When you navigate to the URLs, your browser will display a security warning page because the certificate is self-signed and not trusted by default. You will need to click "Advanced" and then "Proceed to localhost (unsafe)" to view the page.&lt;/p&gt;

&lt;p&gt;You can test the following URLs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;https://localhost:8000/status&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;https://localhost:8000/static/data.txt&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;https://localhost:8000/items/1&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The primary script for generating certificates in the repository, &lt;code&gt;gen_certs.sh&lt;/code&gt;, uses a modern ED25519 (ECC) key for efficiency. However, some browser and operating system combinations can be overly strict when validating self-signed ECC certificates, which may lead to TLS handshake errors like &lt;code&gt;PR_END_OF_FILE_ERROR&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For maximum compatibility, the repository also includes &lt;code&gt;gen_certs_legacy.sh&lt;/code&gt;. This script generates a traditional RSA certificate, which is universally supported and provides a reliable fallback if you encounter browser connection issues with the primary ECC certificate.&lt;/p&gt;

&lt;h2&gt;
  
  
  The WebSocket Layer
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Basic WebSocket Usage
&lt;/h3&gt;

&lt;p&gt;While the HTTP layer is ideal for traditional request-response interactions, it is inefficient for real-time applications that require the server to push data to the client. For this, we turn to WebSockets, which provide a persistent, &lt;strong&gt;bidirectional communication channel&lt;/strong&gt; over a single TCP connection.&lt;/p&gt;

&lt;p&gt;FastAPI provides first-class support for WebSockets. Unlike an HTTP endpoint that processes a single request and returns a response, a WebSocket endpoint is a long-running asynchronous function that manages the entire lifecycle of a connection.&lt;/p&gt;

&lt;p&gt;You define a WebSocket endpoint using the &lt;code&gt;@app.websocket()&lt;/code&gt; decorator. Instead of returning a value, the endpoint function receives a &lt;code&gt;WebSocket&lt;/code&gt; object as an argument. This object is the primary interface for interacting with the client: accepting the connection, sending messages, and receiving messages until the connection is closed.&lt;/p&gt;




&lt;p&gt;First, add &lt;code&gt;WebSocket&lt;/code&gt; and &lt;code&gt;WebSocketDisconnect&lt;/code&gt; to your FastAPI imports at the top of &lt;code&gt;main.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HTTPException&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;WebSocket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;WebSocketDisconnect&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Endpoint 1: A Simple Echo Server
&lt;/h4&gt;

&lt;p&gt;The best way to understand the WebSocket connection lifecycle is to build a simple &lt;strong&gt;echo server&lt;/strong&gt;. This endpoint will accept a connection, wait for a message from the client, and immediately send that exact same message back.&lt;/p&gt;

&lt;p&gt;Add the following code to your &lt;code&gt;main.py&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@app.websocket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/ws/echo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;websocket_echo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;WebSocket&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    A simple WebSocket endpoint that echoes back any message it receives.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accept&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive_text&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Echo: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;WebSocketDisconnect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Client disconnected from echo endpoint.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  Deconstructing the Echo Server
&lt;/h5&gt;

&lt;p&gt;This function demonstrates the fundamental pattern for managing a WebSocket connection in FastAPI:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;await websocket.accept()&lt;/code&gt;&lt;/strong&gt;: This is the first and most crucial step. It performs the WebSocket handshake with the client. You must call &lt;code&gt;accept()&lt;/code&gt; before any &lt;code&gt;send&lt;/code&gt; or &lt;code&gt;receive&lt;/code&gt; operations can occur.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;try...except WebSocketDisconnect&lt;/code&gt;&lt;/strong&gt;: This block ensures we handle the client closing the connection gracefully. When the client disconnects, &lt;code&gt;receive_text()&lt;/code&gt; will raise a &lt;code&gt;WebSocketDisconnect&lt;/code&gt; exception, allowing us to catch it and cleanly exit the function.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;while True:&lt;/code&gt;&lt;/strong&gt;: This loop keeps the connection alive after the initial handshake, allowing the server to continuously listen for new messages from the same client.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;await websocket.receive_text()&lt;/code&gt;&lt;/strong&gt;: This line pauses execution and waits for a message to arrive from the client. Once a message is received, it's stored in the &lt;code&gt;message&lt;/code&gt; variable.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;await websocket.send_text(...)&lt;/code&gt;&lt;/strong&gt;: After receiving a message, this line sends a new message back to the client over the same persistent connection.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Endpoint 2: A Server-Side Data Streamer
&lt;/h4&gt;

&lt;p&gt;Add the following imports to the top of &lt;code&gt;main.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UTC&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The true power of WebSockets is the server's ability to &lt;strong&gt;proactively push data to the client&lt;/strong&gt; without waiting for a request. This next endpoint demonstrates this by streaming the server's timestamp to the client every second.&lt;/p&gt;

&lt;p&gt;Add the following code to your &lt;code&gt;main.py&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@app.websocket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/ws/stream&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;websocket_stream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;WebSocket&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Streams the current server time to the client every second.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accept&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# Generate the data to send
&lt;/span&gt;            &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;timestamp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UTC&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;isoformat&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;This is a periodic update from the server.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="c1"&gt;# Send the data as JSON
&lt;/span&gt;            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="c1"&gt;# Wait for one second
&lt;/span&gt;            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;WebSocketDisconnect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Client disconnected from stream endpoint.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  Deconstructing the Data Streamer
&lt;/h5&gt;

&lt;p&gt;This endpoint builds on the pattern of the echo server but with a key difference: the &lt;code&gt;while True&lt;/code&gt; loop is not blocked waiting for &lt;code&gt;receive_text()&lt;/code&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Server-Initiated Communication&lt;/strong&gt;: After accepting the connection, the server immediately enters a loop where it is the one initiating communication. It constructs a JSON payload and sends it to the client using &lt;code&gt;await websocket.send_json()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Controlled Pacing&lt;/strong&gt;: The &lt;code&gt;await asyncio.sleep(1)&lt;/code&gt; line is crucial. It pauses the loop for one second, creating a steady, paced stream of data. Without this, the server would flood the client with messages as fast as possible.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Graceful Disconnects&lt;/strong&gt;: The &lt;code&gt;try...except WebSocketDisconnect&lt;/code&gt; block is especially important here. If the client disconnects, the next call to &lt;code&gt;websocket.send_json()&lt;/code&gt; will raise this exception, allowing the server to gracefully stop the streaming task for that connection.&lt;/li&gt;
&lt;/ol&gt;




&lt;h4&gt;
  
  
  Endpoint 3: A Simulated Authentication Flow
&lt;/h4&gt;

&lt;p&gt;First, add the necessary imports for cryptographic operations to the top of &lt;code&gt;main.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;hmac&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This final WebSocket endpoint demonstrates a more realistic, stateful interaction: a private endpoint that requires clients to authenticate before proceeding. We will simulate the server-side validation of a signature-based login, a common pattern used by cryptocurrency exchanges like OKX.&lt;/p&gt;

&lt;p&gt;The server will expect a login message containing an API key, a timestamp, and a signature. It will then re-compute the signature on its end using a secret key and compare the two to verify the client's identity.&lt;/p&gt;

&lt;p&gt;Add the following code to your &lt;code&gt;main.py&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# --- DUMMY CREDENTIALS FOR DEMONSTRATION ---
# In a real application, these would be stored securely in a database.
&lt;/span&gt;&lt;span class="n"&gt;DUMMY_API_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;abc&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;DUMMY_API_SECRET&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;def&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_verify_signature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Verifies the signature from a client login message.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="c1"&gt;# Extract the arguments from the payload
&lt;/span&gt;    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;args&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;api_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;apiKey&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;timestamp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;client_sign&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sign&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nf"&gt;except &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;KeyError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;IndexError&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;

    &lt;span class="c1"&gt;# Check if the API key is valid
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;api_key&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;DUMMY_API_KEY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;

    &lt;span class="c1"&gt;# Re-create the signature on the server side
&lt;/span&gt;    &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/users/self/verify&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="n"&gt;mac&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hmac&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nf"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DUMMY_API_SECRET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nf"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;digestmod&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sha256&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;server_sign&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mac&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;# Securely compare the client's signature with the server's
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;hmac&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compare_digest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server_sign&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;client_sign&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nd"&gt;@app.websocket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/ws/v5/private&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;websocket_private&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;WebSocket&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    A simulated private endpoint requiring signature authentication.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accept&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Wait for the initial login message
&lt;/span&gt;        &lt;span class="n"&gt;login_payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive_json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;login_payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;op&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;login&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="nf"&gt;_verify_signature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;login_payload&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;event&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;login&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;success&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
            &lt;span class="c1"&gt;# --- AUTHENTICATED LOOP ---
&lt;/span&gt;            &lt;span class="c1"&gt;# The client is now authenticated.
&lt;/span&gt;            &lt;span class="c1"&gt;# You could enter another loop here to handle private data.
&lt;/span&gt;            &lt;span class="c1"&gt;# For this example, we will just close after login.
&lt;/span&gt;            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# If login fails, send an error and close the connection
&lt;/span&gt;            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;event&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;login&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;success&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authentication failed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;WebSocketDisconnect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Client disconnected from private endpoint.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Handle potential errors like malformed JSON
&lt;/span&gt;        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;An error occurred in the private endpoint: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  Deconstructing the Authentication Logic
&lt;/h5&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Dummy Credentials&lt;/strong&gt;: We define a &lt;code&gt;DUMMY_API_KEY&lt;/code&gt; and &lt;code&gt;DUMMY_API_SECRET&lt;/code&gt; at the top of the file. In a real system, the server would look up the secret key based on the API key provided by the client.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Signature Verification&lt;/strong&gt;: The &lt;code&gt;_verify_signature&lt;/code&gt; helper function encapsulates the core logic. It re-creates the exact same &lt;code&gt;message&lt;/code&gt; string the client used, generates the HMAC-SHA256 signature with the stored secret key, and base64-encodes it.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Secure Comparison&lt;/strong&gt;: It uses &lt;code&gt;hmac.compare_digest()&lt;/code&gt; to check if the client's signature matches the one generated by the server. This function is essential for preventing timing attacks.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Handshake Flow&lt;/strong&gt;: The main endpoint function waits for a single JSON message. It calls the verification function, sends back a success or failure response, and then closes the connection for this example. In a real application, a successful login would typically lead into another &lt;code&gt;while True&lt;/code&gt; loop to handle subsequent private messages.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The Complete WebSocket Code
&lt;/h3&gt;

&lt;p&gt;To finalize our server, we'll add the WebSocket logic to the existing &lt;code&gt;main.py&lt;/code&gt; file. This involves adding several new imports for WebSockets and cryptography, along with the code for our three distinct WebSocket endpoints.&lt;/p&gt;




&lt;h5&gt;
  
  
  New Imports
&lt;/h5&gt;

&lt;p&gt;Add the following imports to the top of your &lt;code&gt;main.py&lt;/code&gt; file alongside the existing ones.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;hmac&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;WebSocket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;WebSocketDisconnect&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h4&gt;
  
  
  WebSocket Endpoints and Logic
&lt;/h4&gt;

&lt;p&gt;Add this entire block of code to the end of your &lt;code&gt;main.py&lt;/code&gt; file, before the &lt;code&gt;if __name__ == "__main__"&lt;/code&gt; block.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# --- DUMMY CREDENTIALS FOR DEMONSTRATION ---
&lt;/span&gt;&lt;span class="n"&gt;DUMMY_API_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;abc&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;DUMMY_API_SECRET&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;def&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_verify_signature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Verifies the signature from a client login message.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;args&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;api_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;apiKey&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;timestamp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;client_sign&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sign&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nf"&gt;except &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;KeyError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;IndexError&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;api_key&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;DUMMY_API_KEY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;

    &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/users/self/verify&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="n"&gt;mac&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hmac&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nf"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DUMMY_API_SECRET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nf"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;digestmod&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sha256&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;server_sign&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mac&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;hmac&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compare_digest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server_sign&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;client_sign&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nd"&gt;@app.websocket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/ws/echo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;websocket_echo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;WebSocket&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;A simple WebSocket endpoint that echoes back any message it receives.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accept&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive_text&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Echo: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;WebSocketDisconnect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Client disconnected from echo endpoint.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nd"&gt;@app.websocket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/ws/stream&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;websocket_stream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;WebSocket&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Streams the current server time to the client every second.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accept&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;timestamp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UTC&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;isoformat&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;This is a periodic update from the server.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;WebSocketDisconnect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Client disconnected from stream endpoint.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nd"&gt;@app.websocket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/ws/v5/private&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;websocket_private&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;WebSocket&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;A simulated private endpoint requiring signature authentication.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accept&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;login_payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive_json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;login_payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;op&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;login&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="nf"&gt;_verify_signature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;login_payload&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;event&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;login&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;success&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
            &lt;span class="c1"&gt;# In a real app, a loop would follow for private data exchange.
&lt;/span&gt;            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;event&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;login&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;success&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authentication failed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;WebSocketDisconnect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Client disconnected from private endpoint.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;An error occurred in the private endpoint: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Testing the WebSocket Endpoints
&lt;/h3&gt;

&lt;p&gt;Testing WebSockets requires a different approach than testing HTTP endpoints. We need a client that can establish a persistent connection and exchange messages. We will cover two methods: an automated suite using &lt;code&gt;pytest&lt;/code&gt; and FastAPI's &lt;code&gt;TestClient&lt;/code&gt;, and a manual script for ad-hoc interactive testing.&lt;/p&gt;




&lt;h4&gt;
  
  
  Automated Testing with Pytest
&lt;/h4&gt;

&lt;p&gt;FastAPI's &lt;code&gt;TestClient&lt;/code&gt; provides a &lt;code&gt;websocket_connect()&lt;/code&gt; context manager that allows us to test our WebSocket endpoints just as easily as our HTTP endpoints.&lt;/p&gt;

&lt;p&gt;Create a new file named &lt;code&gt;test_websockets.py&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;hmac&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi.testclient&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TestClient&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;


&lt;span class="c1"&gt;# --- Test Data for Private Endpoint ---
&lt;/span&gt;&lt;span class="n"&gt;DUMMY_API_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;abc&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;DUMMY_API_SECRET&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;def&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_auth_payload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;api_secret&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Generates a valid login payload for the private endpoint.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/users/self/verify&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="n"&gt;mac&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hmac&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nf"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_secret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nf"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;digestmod&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sha256&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sign&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mac&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;op&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;login&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;args&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;apiKey&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;passphrase&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dummy_passphrase&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# Not used in our server logic
&lt;/span&gt;            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;timestamp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sign&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;sign&lt;/span&gt;
        &lt;span class="p"&gt;}]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_websocket_echo&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Tests the /ws/echo endpoint.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;TestClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;websocket_connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/ws/echo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;test_message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello, WebSocket!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;test_message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive_text&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Echo: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;test_message&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_websocket_stream&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Tests the /ws/stream endpoint for a few messages.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;TestClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;websocket_connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/ws/stream&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# Receive the first 3 messages from the stream
&lt;/span&gt;            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive_json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;timestamp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;
                &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;
            &lt;span class="c1"&gt;# The test client automatically closes the connection here.
&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_websocket_private_auth_success&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Tests a successful authentication on the private endpoint.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;TestClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;websocket_connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/ws/v5/private&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_auth_payload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DUMMY_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DUMMY_API_SECRET&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive_json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;event&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;login&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;success&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_websocket_private_auth_failure&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Tests a failed authentication with a bad secret.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;TestClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;websocket_connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/ws/v5/private&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_auth_payload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DUMMY_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BAD_SECRET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive_json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;event&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;login&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;success&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To run these tests, use the same &lt;code&gt;pytest&lt;/code&gt; command as before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pytest &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h4&gt;
  
  
  Manual Testing with a Client Script
&lt;/h4&gt;

&lt;p&gt;For interactive testing, we can create a small client using the &lt;code&gt;websockets&lt;/code&gt; library. This allows us to connect to our running server and see the message exchange in real-time.&lt;/p&gt;

&lt;p&gt;Create a new file named &lt;code&gt;manual_ws_client.py&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ssl&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;argparse&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;websockets&lt;/span&gt;

&lt;span class="c1"&gt;# Include the same auth payload generator from our tests
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;hmac&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;


&lt;span class="n"&gt;DUMMY_API_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;abc&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;DUMMY_API_SECRET&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;def&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_auth_payload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;api_secret&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/users/self/verify&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="n"&gt;mac&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hmac&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_secret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;digestmod&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sha256&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sign&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mac&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;op&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;login&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;args&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;apiKey&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;passphrase&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pw&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;timestamp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sign&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;}]}&lt;/span&gt;


&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;argparse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ArgumentParser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Manual WebSocket client.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;endpoint&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;echo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stream&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;private&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The endpoint to connect to.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;# Create an SSL context that trusts our self-signed certificate
&lt;/span&gt;    &lt;span class="n"&gt;ssl_context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SSLContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PROTOCOL_TLS_CLIENT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ssl_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;check_hostname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
    &lt;span class="n"&gt;ssl_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;verify_mode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CERT_NONE&lt;/span&gt;

    &lt;span class="n"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wss://localhost:8000/ws/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;endpoint&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;private&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wss://localhost:8000/ws/v5/private&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;websockets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ssl_context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Connected to &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;endpoint&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;echo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Message to send (or &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;exit&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;): &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;exit&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="k"&gt;break&lt;/span&gt;
                &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;recv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt; Received: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;endpoint&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stream&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;recv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt; Received: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;websockets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ConnectionClosed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Stream connection closed by server.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;endpoint&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;private&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_auth_payload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DUMMY_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DUMMY_API_SECRET&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;gt; Sending login request: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;recv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt; Received: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To use this script, make sure your FastAPI server is running. Then, from a new terminal, run one of the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Test the echo server interactively&lt;/span&gt;
python3 manual_ws_client.py &lt;span class="nb"&gt;echo&lt;/span&gt;

&lt;span class="c"&gt;# Connect to the data stream&lt;/span&gt;
python3 manual_ws_client.py stream

&lt;span class="c"&gt;# Test the private endpoint authentication&lt;/span&gt;
python3 manual_ws_client.py private
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Measuring Test Coverage
&lt;/h3&gt;

&lt;p&gt;While our test suite validates our endpoints, &lt;strong&gt;test coverage&lt;/strong&gt; is a metric that tells us which lines of our application code were actually executed during the test run. It's a valuable tool for identifying parts of your codebase that are not tested at all.&lt;/p&gt;

&lt;p&gt;First, you'll need to install the &lt;code&gt;pytest-cov&lt;/code&gt; plugin (included in requirements.txt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install &lt;/span&gt;pytest-cov
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, you can run &lt;code&gt;pytest&lt;/code&gt; with the &lt;code&gt;--cov&lt;/code&gt; flag to generate a coverage report. We'll target our &lt;code&gt;main&lt;/code&gt; module.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; pytest &lt;span class="nt"&gt;--cov&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;main &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the tests complete, you will see a report similar to this at the bottom of the output, showing that our tests exercised 100% of our application code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;================================================== test session starts ==================================================
platform linux -- Python 3.11.2, pytest-8.4.1, pluggy-1.6.0 -- xxx
cachedir: .pytest_cache
rootdir: xxx
plugins: cov-6.2.1, anyio-4.10.0
collected 12 items                                                                                                      

test_http.py::test_read_root PASSED                                                                               [  8%]
test_http.py::test_read_status PASSED                                                                             [ 16%]
test_http.py::test_read_file PASSED                                                                               [ 25%]
test_http.py::test_get_item_found PASSED                                                                          [ 33%]
test_http.py::test_get_item_not_found PASSED                                                                      [ 41%]
test_http.py::test_create_item_success PASSED                                                                     [ 50%]
test_http.py::test_create_item_validation_error_missing_field PASSED                                              [ 58%]
test_http.py::test_create_item_validation_error_wrong_type PASSED                                                 [ 66%]
test_websockets.py::test_websocket_echo PASSED                                                                    [ 75%]
test_websockets.py::test_websocket_stream PASSED                                                                  [ 83%]
test_websockets.py::test_websocket_private_auth_success PASSED                                                    [ 91%]
test_websockets.py::test_websocket_private_auth_failure PASSED                                                    [100%]

==================================================== tests coverage =====================================================
____________________________________ coverage: platform linux, python 3.11.2-final-0 ____________________________________

Name      Stmts   Miss  Cover
-----------------------------
main.py      93      9    90%
-----------------------------
TOTAL        93      9    90%
================================================== 12 passed in 2.30s ===================================================
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;In this guide, we have built a complete, dual-protocol server from the ground up. You have moved from the foundational theory of HTTP and WebSockets to implementing a secure, tested, and robust application using FastAPI. This server is not just a simple example; it is a solid foundation that correctly handles application state, serves different of content, manages errors gracefully, and provides real-time communication capabilities.&lt;/p&gt;

&lt;p&gt;This guide presents one approach to building and testing a dual-protocol server, but there are many valid techniques and architectures. I'm keen to hear about your own experiences and preferred stacks. What challenges have you encountered when working with WebSockets in a production environment, and what tools have you found indispensable for testing and validation? Share your thoughts and questions in the comments below. &lt;/p&gt;

</description>
      <category>fastapi</category>
      <category>webdev</category>
      <category>testing</category>
      <category>programming</category>
    </item>
    <item>
      <title>Part 09: Building a Sovereign Software Factory: Monitoring with Prometheus &amp; Grafana</title>
      <dc:creator>Warren Jitsing</dc:creator>
      <pubDate>Tue, 30 Dec 2025 09:04:58 +0000</pubDate>
      <link>https://dev.to/warren_jitsing_dd1c1d6fc6/part-09-building-a-sovereign-software-factory-monitoring-with-prometheus-grafana-227k</link>
      <guid>https://dev.to/warren_jitsing_dd1c1d6fc6/part-09-building-a-sovereign-software-factory-monitoring-with-prometheus-grafana-227k</guid>
      <description>&lt;p&gt;GitHub: &lt;a href="https://github.com/InfiniteConsult/0013_cicd_part09_prometheus_grafana" rel="noopener noreferrer"&gt;https://github.com/InfiniteConsult/0013_cicd_part09_prometheus_grafana&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; In this installment, we solve the "Black Box" problem where we cannot see the health of our infrastructure. We construct the &lt;strong&gt;"Observatory"&lt;/strong&gt; by deploying &lt;strong&gt;Prometheus&lt;/strong&gt; (The Brain) and &lt;strong&gt;Grafana&lt;/strong&gt; (The Face). We implement a "Pull" monitoring architecture, deploying &lt;strong&gt;Exporters&lt;/strong&gt; to translate the opaque internal states of our Host, Docker Engine, and legacy applications into standardized metrics. We navigate complex &lt;strong&gt;Integration Hurdles&lt;/strong&gt;, using "Architect Scripts" to inject secrets and pre-configure dashboards, and we solve the "Trust Gap" by embedding our Root CA directly into Grafana's provisioning logic, achieving full-stack visibility over our encrypted &lt;code&gt;cicd-net&lt;/code&gt; city.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Sovereign Software Factory Series:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Part 01:&lt;/strong&gt; Building a Sovereign Software Factory: Docker Networking &amp;amp; Persistence&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 02:&lt;/strong&gt; Building a Sovereign Software Factory: The Local Root CA &amp;amp; Trust Chains&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 03:&lt;/strong&gt; Building a Sovereign Software Factory: Self-Hosted GitLab &amp;amp; Secrets Management&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 04:&lt;/strong&gt; Building a Sovereign Software Factory: Jenkins Configuration as Code (JCasC)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 05:&lt;/strong&gt; Building a Sovereign Software Factory: Artifactory &amp;amp; The "Strict TLS" Trap&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 06:&lt;/strong&gt; Building a Sovereign Software Factory: SonarQube Quality Gates&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 07:&lt;/strong&gt; Building a Sovereign Software Factory: ChatOps with Mattermost&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 08:&lt;/strong&gt; Building a Sovereign Software Factory: Observability with the ELK Stack&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 09: Building a Sovereign Software Factory: Monitoring with Prometheus &amp;amp; Grafana&lt;/strong&gt; (You are here)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 10:&lt;/strong&gt; Building a Sovereign Software Factory: The Python API Package (Capstone)&lt;/li&gt;
&lt;/ol&gt;




&lt;h1&gt;
  
  
  Chapter 1: The Challenge - The Opaque Infrastructure
&lt;/h1&gt;

&lt;h2&gt;
  
  
  1.1 The "Black Box" City
&lt;/h2&gt;

&lt;p&gt;In the previous eight articles, we have meticulously constructed a sovereign, end-to-end Software Supply Chain. We started with the bedrock of &lt;strong&gt;Docker&lt;/strong&gt; and a custom &lt;strong&gt;Certificate Authority&lt;/strong&gt;, then built a &lt;strong&gt;Library&lt;/strong&gt; (GitLab) to store our blueprints, a &lt;strong&gt;Factory&lt;/strong&gt; (Jenkins) to manufacture our products, an &lt;strong&gt;Inspector&lt;/strong&gt; (SonarQube) to certify their quality, a &lt;strong&gt;Warehouse&lt;/strong&gt; (Artifactory) to store them securely, a &lt;strong&gt;Command Center&lt;/strong&gt; (Mattermost) to coordinate our teams, and an &lt;strong&gt;Investigation Office&lt;/strong&gt; (ELK Stack) to analyze our logs.&lt;/p&gt;

&lt;p&gt;Technically, our city is operational. The pipelines run, the code is analyzed, the artifacts are shipped, and the logs are indexed.&lt;/p&gt;

&lt;p&gt;Functionally, however, our city is opaque. It is a "Black Box."&lt;/p&gt;

&lt;p&gt;We know &lt;em&gt;that&lt;/em&gt; the factory is running, but we do not know if the engines are overheating. We know the warehouse is accepting packages, but we do not know if the shelves are 99% full. When a build suddenly takes 15 minutes instead of 5, we are forced to guess the cause. Is the Jenkins container CPU-starved? Is the GitLab database locking up? Is the host machine running out of memory?&lt;/p&gt;

&lt;p&gt;Currently, the only way to answer these questions is manual intervention. We have to SSH into the host, run &lt;code&gt;top&lt;/code&gt; to check load averages, install &lt;code&gt;iotop&lt;/code&gt; to check disk usage, and grepping through application-specific status pages. We are flying a complex spaceship with no instrument panel, relying on the sound of the engine to detect trouble.&lt;/p&gt;

&lt;p&gt;In a professional environment, this lack of visibility is a critical risk. We cannot wait for a crash to know we are in danger. We need a centralized &lt;strong&gt;Observatory&lt;/strong&gt;—a single pane of glass that constantly measures the vital signs of every component in our stack, alerting us to degradation &lt;em&gt;before&lt;/em&gt; it becomes a disaster.&lt;/p&gt;

&lt;p&gt;To achieve this, we will deploy the industry-standard cloud-native monitoring stack: &lt;strong&gt;Prometheus&lt;/strong&gt; (The Brain) and &lt;strong&gt;Grafana&lt;/strong&gt; (The Face).&lt;/p&gt;

&lt;h2&gt;
  
  
  1.2 The Technical Barrier: Troubleshooting by Flashlight
&lt;/h2&gt;

&lt;p&gt;The fundamental flaw in our current architecture is not a lack of data; it is a lack of &lt;strong&gt;aggregation&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Currently, when a developer reports that "the pipeline is stuck," diagnosing the root cause requires a manual, multi-step investigation that resembles troubleshooting by flashlight in a dark room.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Is it the Host?&lt;/strong&gt; We must SSH into the server and run &lt;code&gt;htop&lt;/code&gt;. We might see high load averages, but we cannot tell &lt;em&gt;history&lt;/em&gt;. Was the load high 10 minutes ago when the build failed, or is it high now because we are running diagnostics? We lack temporal context.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Is it the Factory?&lt;/strong&gt; We log into the Jenkins UI to check the build queue. We might see executors are busy, but we cannot see if the JVM heap is exhausted or if Garbage Collection is pausing the system.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Is it the Library?&lt;/strong&gt; We check GitLab logs. We might see slow SQL queries, but we cannot easily correlate them with the exact timestamp of the Jenkins build spike.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This disconnected workflow forces us to hold the entire state of the distributed system in our heads. We are trying to manually correlate a CPU spike on the host (Infrastructure Layer) with a slow database query in GitLab (Application Layer) and a timeout in Jenkins (Service Layer). This cognitive load is unsustainable. As our city grows, the complexity of these interactions increases exponentially, turning every minor incident into a major forensic investigation.&lt;/p&gt;

&lt;h2&gt;
  
  
  1.3 The Solution: The Observatory
&lt;/h2&gt;

&lt;p&gt;To solve this, we must decouple &lt;strong&gt;Metric Generation&lt;/strong&gt; from &lt;strong&gt;Metric Analysis&lt;/strong&gt;. We need a system that passively collects the vital signs of our city and aggregates them into a single, unified view. We need to move from reactive troubleshooting to proactive observability.&lt;/p&gt;

&lt;p&gt;We will achieve this by deploying the industry-standard "Cloud Native" monitoring stack:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Sensors (Exporters):&lt;/strong&gt; We will deploy small, lightweight agents alongside our services. These agents act as translators, converting the opaque internal state of a service (Linux Kernel stats, Docker cgroups, JVM memory pools) into a standardized, machine-readable format.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Brain (Prometheus):&lt;/strong&gt; This is our Time-Series Database (TSDB). Unlike traditional monitoring tools that wait for agents to "push" data to them, Prometheus actively "pulls" (scrapes) data from our sensors on a strict schedule. This architectural inversion makes the brain incredibly resilient; if a sensor dies, the brain knows immediately because the scrape fails.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Face (Grafana):&lt;/strong&gt; This is our visualization engine. It connects to the Brain, queries the historical data, and renders it into intuitive, real-time dashboards.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;However, deploying this stack in our environment introduces a specific &lt;strong&gt;Integration Hurdle&lt;/strong&gt;. We have built a "Zero Trust" city. Our services communicate over strict HTTPS channels using a private Certificate Authority. We cannot simply drop a default Prometheus container into the network and expect it to work. It will be blocked by our security layers. We must engineer our Observatory to possess the same cryptographic identity as the rest of our city, enabling it to peer inside our secure HTTPS enclaves without breaking the chain of trust.&lt;/p&gt;

&lt;h1&gt;
  
  
  Chapter 2: Architecture - The Pull Model &amp;amp; The Chain of Trust
&lt;/h1&gt;

&lt;h2&gt;
  
  
  2.1 The Theory: "Push" vs. "Pull" Monitoring
&lt;/h2&gt;

&lt;p&gt;Before we write code, we must understand the fundamental architectural shift that Prometheus represents. Traditional monitoring systems (like Nagios or older ELK setups) rely on a &lt;strong&gt;"Push"&lt;/strong&gt; model. In this model, you install an intelligent agent on every server. This agent collects data and actively transmits it to a central collector.&lt;/p&gt;

&lt;p&gt;This model has significant fragility. If the central server goes down, every agent in your fleet panics. They must buffer data locally, consuming RAM on your production servers, or drop data, creating gaps in your history. Furthermore, configuration is decentralized; if you want to change the reporting interval, you often have to update the config on hundreds of agents.&lt;/p&gt;

&lt;p&gt;Prometheus inverts this architecture. It uses a &lt;strong&gt;"Pull"&lt;/strong&gt; (Scrape) model.&lt;/p&gt;

&lt;p&gt;In our stack, the agents (which we call Exporters) are dumb. They do not know Prometheus exists. They simply expose a lightweight HTTP endpoint (&lt;code&gt;/metrics&lt;/code&gt;) that displays their current state. Prometheus is the active initiator. It wakes up on a defined schedule (e.g., every 15 seconds), reaches out to every target in its configuration, and "scrapes" the current data.&lt;/p&gt;

&lt;p&gt;This inversion creates a highly resilient system. If Prometheus goes down for maintenance, the agents don't care; they just keep serving their endpoint. There is no buffering pressure on our production services. If a target dies, Prometheus knows instantly because the network connection fails—there is no waiting for a "heartbeat" to timeout. We control the entire monitoring cadence from one central configuration file (&lt;code&gt;prometheus.yml&lt;/code&gt;), rather than managing config files scattered across the city.&lt;/p&gt;

&lt;h2&gt;
  
  
  2.2 The Components: Sensors, Brain, and Face
&lt;/h2&gt;

&lt;p&gt;Our Observatory is built on three specialized pillars, each performing a single function with high efficiency.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Sensors (Exporters &amp;amp; Native Endpoints):&lt;/strong&gt;
These are the translators and signals of our city. In a perfect world, every piece of software would speak Prometheus natively. In reality, we deal with a mix of modern applications and legacy systems. To handle this, we employ two distinct strategies for data collection:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Native Instrumentation:&lt;/strong&gt; Many of our "Cloud Native" tools—&lt;strong&gt;GitLab&lt;/strong&gt;, &lt;strong&gt;Artifactory&lt;/strong&gt;, &lt;strong&gt;SonarQube&lt;/strong&gt;, and &lt;strong&gt;Mattermost&lt;/strong&gt;—have internalized the need for observability. They expose their own &lt;code&gt;/metrics&lt;/code&gt; endpoints directly. We do not need to install extra software to monitor them; we simply need to configure Prometheus to scrape their built-in API. Even &lt;strong&gt;Jenkins&lt;/strong&gt;, essentially a legacy Java application, joins this category thanks to its Prometheus plugin.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Exporter Pattern (Translators):&lt;/strong&gt; Other components—specifically the &lt;strong&gt;Linux Kernel&lt;/strong&gt;, the &lt;strong&gt;Docker Daemon&lt;/strong&gt;, and &lt;strong&gt;Elasticsearch&lt;/strong&gt;—do not natively speak Prometheus. For these, we deploy "Exporters." These are lightweight sidecar processes that query the target (e.g., reading &lt;code&gt;/proc&lt;/code&gt; files or hitting the Elasticsearch &lt;code&gt;_cluster/health&lt;/code&gt; API) and translate that raw data into the standardized Prometheus format on the fly. We use &lt;strong&gt;Node Exporter&lt;/strong&gt; for host hardware, &lt;strong&gt;cAdvisor&lt;/strong&gt; for container stats, and the &lt;strong&gt;Elasticsearch Exporter&lt;/strong&gt; for log cluster health.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Architectural Note:&lt;/em&gt; You will notice we are &lt;em&gt;not&lt;/em&gt; deploying a PostgreSQL exporter. This is a deliberate scope decision. While a dedicated DB exporter offers deep insight into lock contention and buffer pools, our primary focus is "Service Health" as seen by the application. If GitLab is slow, its native metrics will reveal slow database transaction times, often giving us enough context without the added complexity of managing database credentials for a dedicated exporter.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Brain (Prometheus):&lt;/strong&gt;
This is the Time-Series Database (TSDB). It is the central authority. It holds the map of the city (&lt;code&gt;prometheus.yml&lt;/code&gt;) and is responsible for reaching out to every sensor—whether Native or Exporter—to collect data. It stores this data in a highly optimized format on disk and evaluates alerting rules. It is optimized for write throughput and reliability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Face (Grafana):&lt;/strong&gt;
This is the visualization engine. While Prometheus is excellent at storing data, its native UI is rudimentary. Grafana connects to Prometheus as a datasource. It executes queries (using PromQL) against the Brain and renders the results into rich, interactive dashboards. It is the single pane of glass where we will observe our city.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  2.3 The Security Architecture: The Chain of Trust
&lt;/h2&gt;

&lt;p&gt;Security in a distributed system is usually a trade-off between purity and pragmatism. In our "City," our primary directive is &lt;strong&gt;"HTTPS Everywhere."&lt;/strong&gt; We have established a private Certificate Authority, and for the vast majority of our citizens—&lt;strong&gt;GitLab, Jenkins, Artifactory, Mattermost, Prometheus, and Grafana&lt;/strong&gt;—we strictly enforce encrypted communication. When Prometheus scrapes Jenkins, it validates the server's identity using our Root CA, ensuring that no intruder can spoof the factory's vital signs.&lt;/p&gt;

&lt;p&gt;However, we must address two specific architectural exceptions: &lt;strong&gt;SonarQube&lt;/strong&gt; and &lt;strong&gt;cAdvisor&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Unlike our modern "Cloud Native" tools, neither of these applications supports native TLS termination. They expect to sit behind a reverse proxy (like Nginx) that handles the encryption for them. In a high-compliance production environment, we would build custom Docker images that bundle an Nginx sidecar into the container to wrap these services in SSL. However, to keep our architecture lean and focused on the &lt;em&gt;principles&lt;/em&gt; of observability rather than the nuances of Nginx configuration, we have elected to run these two specific endpoints over plain HTTP.&lt;/p&gt;

&lt;p&gt;We mitigate this risk through &lt;strong&gt;Isolation&lt;/strong&gt; and &lt;strong&gt;Authentication&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Network Isolation (cAdvisor):&lt;/strong&gt; The cAdvisor container is a "Dark Service." It exposes &lt;em&gt;no ports&lt;/em&gt; to the host machine. It lives entirely within the &lt;code&gt;cicd-net&lt;/code&gt; Docker network. The only entity that can reach it is Prometheus, which resides on the same private virtual network. It is effectively air-gapped from the rest of the world.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strict Authentication (SonarQube):&lt;/strong&gt; While SonarQube's metrics travel over HTTP, they are not open to the public. If a rogue process attempts to query the endpoint &lt;code&gt;http://sonarqube:9000/api/monitoring/metrics&lt;/code&gt;, it will be rejected with an HTTP 403 error: &lt;code&gt;{"errors":[{"msg":"Insufficient privileges"}]}&lt;/code&gt;. Access requires a high-entropy &lt;strong&gt;System Passcode&lt;/strong&gt;, which we generate and inject strictly into the Prometheus configuration. We rely on strong identity (Who are you?) to compensate for the lack of transport encryption (Can anyone read this?) within our private network perimeter.&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Chapter 3: The Architect - Preparing the Environment
&lt;/h1&gt;

&lt;h2&gt;
  
  
  3.1 The Pre-Computation Strategy
&lt;/h2&gt;

&lt;p&gt;In many tutorials, you are asked to manually create a &lt;code&gt;prometheus.yml&lt;/code&gt; file, copy-paste some YAML, and hope for the best. In our City, we reject this manual "Click-Ops" approach. It is error-prone, insecure, and hard to replicate.&lt;/p&gt;

&lt;p&gt;Instead, we employ a &lt;strong&gt;Pre-Computation Strategy&lt;/strong&gt;. We use a shell script—The Architect (&lt;code&gt;01-setup-monitoring.sh&lt;/code&gt;)—to dynamically generate our configuration files &lt;em&gt;before&lt;/em&gt; the containers ever launch.&lt;/p&gt;

&lt;p&gt;This approach offers critical advantages for our complex environment:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Secret Injection:&lt;/strong&gt; We can securely inject high-entropy secrets (like the &lt;code&gt;SONAR_WEB_SYSTEMPASSCODE&lt;/code&gt; or the &lt;code&gt;ARTIFACTORY_ADMIN_TOKEN&lt;/code&gt;) directly into the configuration files without ever hardcoding them in our source repository.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Path Consistency:&lt;/strong&gt; We ensure that certificate paths match exactly between the host (where we manage files) and the container (where the app reads them).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Permission Management:&lt;/strong&gt; We can automate the complex permission hand-offs required to satisfy the strict UID requirements of Prometheus and Grafana.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This script acts as the construction crew that lays the foundation, pours the concrete, and wires the electricity before the residents move in.&lt;/p&gt;

&lt;h2&gt;
  
  
  3.2 The Map of the City (&lt;code&gt;prometheus.yml&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;The heart of our monitoring system is the &lt;code&gt;prometheus.yml&lt;/code&gt; file. This file acts as the "Map of the City," telling the Brain exactly where every Sensor is located and how to talk to it.&lt;/p&gt;

&lt;p&gt;Our Architect script generates a configuration with &lt;strong&gt;9 distinct scrape jobs&lt;/strong&gt;, covering every layer of our stack:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Infrastructure:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;node-exporter&lt;/code&gt;: Monitors the host hardware (CPU, RAM, Disk).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cadvisor&lt;/code&gt;: Monitors Docker containers.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;elasticsearch-exporter&lt;/code&gt;: Monitors the log storage cluster.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Applications:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;jenkins&lt;/code&gt;, &lt;code&gt;gitlab&lt;/code&gt;, &lt;code&gt;artifactory&lt;/code&gt;, &lt;code&gt;mattermost&lt;/code&gt;: Monitor the service-level performance (HTTP request rates, build queues, transaction times).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Special Cases:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;sonarqube&lt;/code&gt;: Uses a custom header (&lt;code&gt;X-Sonar-Passcode&lt;/code&gt;) for authentication instead of a standard token.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;prometheus&lt;/code&gt;: The Brain monitors itself to ensure &lt;em&gt;it&lt;/em&gt; isn't running out of memory.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Crucially, the script enforces our &lt;strong&gt;Zero Trust&lt;/strong&gt; model. For every service capable of it (Jenkins, GitLab, Artifactory), we set &lt;code&gt;scheme: https&lt;/code&gt; and point to the &lt;code&gt;ca_file&lt;/code&gt;. This ensures that when Prometheus reaches out to scrape metrics, it is validating the cryptographic identity of the target. We are not just blindly trusting that the IP &lt;code&gt;172.30.0.x&lt;/code&gt; belongs to Jenkins; we are verifying it against our Root CA.&lt;/p&gt;

&lt;h2&gt;
  
  
  3.3 The Integration Hurdle: Embedded Trust (&lt;code&gt;datasources.yaml&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;While &lt;code&gt;prometheus.yml&lt;/code&gt; defines how the Brain talks to the Sensors, &lt;code&gt;datasources.yaml&lt;/code&gt; defines how the Face (Grafana) talks to the Brain (Prometheus).&lt;/p&gt;

&lt;p&gt;Since our Prometheus instance is secured with HTTPS, Grafana cannot simply connect to &lt;code&gt;http://prometheus:9090&lt;/code&gt;. It must connect to &lt;code&gt;https://prometheus.cicd.local:9090&lt;/code&gt;. This introduces a classic "Chicken and Egg" problem regarding trust. Grafana needs to trust the Certificate Authority (CA) that signed Prometheus's certificate.&lt;/p&gt;

&lt;p&gt;In a standard deployment, you might mount the &lt;code&gt;ca.pem&lt;/code&gt; file into the Grafana container and point to it with a file path. However, this creates a hard dependency on the container's filesystem structure. If the mount fails or the path changes, Grafana breaks.&lt;/p&gt;

&lt;p&gt;Our Architect script takes a more robust approach: &lt;strong&gt;Certificate Embedding&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;sed&lt;/code&gt;, the script reads the content of our &lt;code&gt;ca.pem&lt;/code&gt; on the host, indents it correctly for YAML, and injects the actual certificate data directly into the &lt;code&gt;datasources.yaml&lt;/code&gt; file under the &lt;code&gt;secureJsonData.tlsCACert&lt;/code&gt; field.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;    &lt;span class="na"&gt;secureJsonData&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;tlsCACert&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;-----BEGIN CERTIFICATE-----&lt;/span&gt;
          &lt;span class="s"&gt;MIIFVTCCAz2gAwIBAgIU...&lt;/span&gt;
          &lt;span class="s"&gt;...&lt;/span&gt;
          &lt;span class="s"&gt;-----END CERTIFICATE-----&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes the configuration &lt;strong&gt;portable&lt;/strong&gt; and &lt;strong&gt;self-contained&lt;/strong&gt;. Grafana does not need to look for a file on disk; the trust store is baked directly into its provisioning logic. This ensures that the connection between the Face and the Brain is encrypted and verified from the very first millisecond of boot time.&lt;/p&gt;

&lt;h2&gt;
  
  
  3.4 The Critical Barrier: File Permissions (UID 65534 &amp;amp; 472)
&lt;/h2&gt;

&lt;p&gt;The most common reason for a Prometheus or Grafana deployment to fail in a "hardened" environment is a permissions mismatch.&lt;/p&gt;

&lt;p&gt;By default, when you mount a host directory into a Docker container, the permissions are passed through verbatim. If your config files on the host are owned by &lt;code&gt;root&lt;/code&gt; (because you used &lt;code&gt;sudo&lt;/code&gt;) or your personal user, the process inside the container must have read access to those users' files.&lt;/p&gt;

&lt;p&gt;However, for security reasons, neither Prometheus nor Grafana runs as &lt;code&gt;root&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prometheus&lt;/strong&gt; runs as the &lt;code&gt;nobody&lt;/code&gt; user (UID &lt;strong&gt;65534&lt;/strong&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Grafana&lt;/strong&gt; runs as a specific &lt;code&gt;grafana&lt;/code&gt; user (UID &lt;strong&gt;472&lt;/strong&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If we simply mount our generated configuration files into the containers, they will crash immediately with &lt;code&gt;permission denied&lt;/code&gt; errors because UID 65534 cannot read a file owned by UID 1000 (your user) or UID 0 (root) with strict permissions.&lt;/p&gt;

&lt;p&gt;Our Architect script solves this via &lt;strong&gt;Surgical Ownership&lt;/strong&gt;. In Phase 6, it performs a precise &lt;code&gt;chown&lt;/code&gt; operation:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It changes the ownership of the Prometheus configuration and certificate directories to &lt;code&gt;65534:65534&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;It changes the ownership of the Grafana directories to &lt;code&gt;472:472&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This ensures that when the containers wake up, the files they need to breathe (configs) and the ground they need to walk on (data volumes) belong to them. This preemptive alignment prevents the "CrashLoopBackOff" nightmare that plagues so many manual deployments.&lt;/p&gt;

&lt;h2&gt;
  
  
  3.5 The Source Code: 01-setup-monitoring.sh
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;#           01-setup-monitoring.sh&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  The "Architect" script for Prometheus &amp;amp; Grafana.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  1. Secrets: Generates GRAFANA_ADMIN_PASSWORD &amp;amp; SONAR_WEB_SYSTEMPASSCODE.&lt;/span&gt;
&lt;span class="c"&gt;#  2. Certs: Stages existing certs into config dirs.&lt;/span&gt;
&lt;span class="c"&gt;#  3. Configs: Generates prometheus.yml, grafana.ini, datasources.yaml.&lt;/span&gt;
&lt;span class="c"&gt;#  4. Permissions: Sets surgical ownership for UID 65534 &amp;amp; 472.&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="c"&gt;# --- 1. Define Paths ---&lt;/span&gt;
&lt;span class="nv"&gt;HOST_CICD_ROOT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/cicd_stack"&lt;/span&gt;
&lt;span class="nv"&gt;PROMETHEUS_BASE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOST_CICD_ROOT&lt;/span&gt;&lt;span class="s2"&gt;/prometheus"&lt;/span&gt;
&lt;span class="nv"&gt;GRAFANA_BASE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOST_CICD_ROOT&lt;/span&gt;&lt;span class="s2"&gt;/grafana"&lt;/span&gt;
&lt;span class="nv"&gt;MASTER_ENV_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOST_CICD_ROOT&lt;/span&gt;&lt;span class="s2"&gt;/cicd.env"&lt;/span&gt;

&lt;span class="c"&gt;# Source CA Paths&lt;/span&gt;
&lt;span class="nv"&gt;CA_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOST_CICD_ROOT&lt;/span&gt;&lt;span class="s2"&gt;/ca"&lt;/span&gt;
&lt;span class="nv"&gt;SRC_CA_CRT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CA_DIR&lt;/span&gt;&lt;span class="s2"&gt;/pki/certs/ca.pem"&lt;/span&gt;
&lt;span class="nv"&gt;SRC_PROM_CRT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CA_DIR&lt;/span&gt;&lt;span class="s2"&gt;/pki/services/prometheus.cicd.local/prometheus.cicd.local.crt.pem"&lt;/span&gt;
&lt;span class="nv"&gt;SRC_PROM_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CA_DIR&lt;/span&gt;&lt;span class="s2"&gt;/pki/services/prometheus.cicd.local/prometheus.cicd.local.key.pem"&lt;/span&gt;
&lt;span class="nv"&gt;SRC_GRAF_CRT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CA_DIR&lt;/span&gt;&lt;span class="s2"&gt;/pki/services/grafana.cicd.local/grafana.cicd.local.crt.pem"&lt;/span&gt;
&lt;span class="nv"&gt;SRC_GRAF_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CA_DIR&lt;/span&gt;&lt;span class="s2"&gt;/pki/services/grafana.cicd.local/grafana.cicd.local.key.pem"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🚀 Starting Monitoring 'Architect' Setup..."&lt;/span&gt;

&lt;span class="c"&gt;# --- 2. Secrets Management ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Phase 1: Secrets Management ---"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: Master env file not found at &lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Load existing secrets&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt;
&lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; +a

&lt;span class="c"&gt;# Helper to append new secrets&lt;/span&gt;
append_secret&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;val&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s2"&gt;"^&lt;/span&gt;&lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="s2"&gt;="&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="s2"&gt;=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$val&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Generated &lt;/span&gt;&lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$val&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;else
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Found existing &lt;/span&gt;&lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

generate_password&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; openssl rand &lt;span class="nt"&gt;-hex&lt;/span&gt; 16&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
generate_passcode&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; openssl rand &lt;span class="nt"&gt;-hex&lt;/span&gt; 32&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Generate missing monitoring secrets&lt;/span&gt;
append_secret &lt;span class="s2"&gt;"GRAFANA_ADMIN_PASSWORD"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;generate_password&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
append_secret &lt;span class="s2"&gt;"SONAR_WEB_SYSTEMPASSCODE"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;generate_passcode&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Validate dependencies&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ARTIFACTORY_ADMIN_TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ELASTIC_PASSWORD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"❌ ERROR: Missing dependency secrets (ARTIFACTORY_ADMIN_TOKEN or ELASTIC_PASSWORD)."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# --- 3. Directory &amp;amp; Permission Prep ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Phase 2: Directory Preparation ---"&lt;/span&gt;

&lt;span class="c"&gt;# Take ownership to current user to allow writing configs without sudo&lt;/span&gt;
&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$USER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;:&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$USER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROMETHEUS_BASE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$USER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;:&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$USER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GRAFANA_BASE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Create config structures&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROMETHEUS_BASE&lt;/span&gt;&lt;span class="s2"&gt;/config/certs"&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GRAFANA_BASE&lt;/span&gt;&lt;span class="s2"&gt;/config/certs"&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GRAFANA_BASE&lt;/span&gt;&lt;span class="s2"&gt;/provisioning/datasources"&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GRAFANA_BASE&lt;/span&gt;&lt;span class="s2"&gt;/provisioning/dashboards"&lt;/span&gt;

&lt;span class="c"&gt;# --- 4. Certificate Staging ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Phase 3: Staging Certificates ---"&lt;/span&gt;

&lt;span class="c"&gt;# Prometheus&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SRC_CA_CRT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROMETHEUS_BASE&lt;/span&gt;&lt;span class="s2"&gt;/config/certs/ca.pem"&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SRC_PROM_CRT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROMETHEUS_BASE&lt;/span&gt;&lt;span class="s2"&gt;/config/certs/prometheus.crt"&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SRC_PROM_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROMETHEUS_BASE&lt;/span&gt;&lt;span class="s2"&gt;/config/certs/prometheus.key"&lt;/span&gt;

&lt;span class="c"&gt;# Grafana&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SRC_CA_CRT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GRAFANA_BASE&lt;/span&gt;&lt;span class="s2"&gt;/config/certs/ca.pem"&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SRC_GRAF_CRT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GRAFANA_BASE&lt;/span&gt;&lt;span class="s2"&gt;/config/certs/grafana.crt"&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SRC_GRAF_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GRAFANA_BASE&lt;/span&gt;&lt;span class="s2"&gt;/config/certs/grafana.key"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Certificates staged."&lt;/span&gt;

&lt;span class="c"&gt;# --- 5. Configuration Generation ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Phase 4: Generating Configurations ---"&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt; "&lt;/span&gt;&lt;span class="nv"&gt;$PROMETHEUS_BASE&lt;/span&gt;&lt;span class="sh"&gt;/config/prometheus.yml"
global:
  scrape_interval: 15s
  evaluation_interval: 15s

scrape_configs:
  # 1. Prometheus (Self)
  - job_name: 'prometheus'
    scheme: https
    tls_config:
      ca_file: /etc/prometheus/certs/ca.pem
    static_configs:
      - targets: ['prometheus.cicd.local:9090']

  # 2. Node Exporter (Host Hardware)
  - job_name: 'node-exporter'
    scheme: https
    tls_config:
      ca_file: /etc/prometheus/certs/ca.pem
    static_configs:
      - targets: ['172.30.0.1:9100']

  # 3. cAdvisor (Container Stats)
  - job_name: 'cadvisor'
    static_configs:
      - targets: ['cadvisor.cicd.local:8080']

  # 4. Elasticsearch Exporter
  - job_name: 'elasticsearch-exporter'
    scheme: https
    tls_config:
      ca_file: /etc/prometheus/certs/ca.pem
    static_configs:
      - targets: ['elasticsearch-exporter.cicd.local:9114']

  # 5. GitLab
  - job_name: 'gitlab'
    metrics_path: /-/metrics
    scheme: https
    tls_config:
      ca_file: /etc/prometheus/certs/ca.pem
    static_configs:
      - targets: ['gitlab.cicd.local:10300']

  # 6. Jenkins
  - job_name: 'jenkins'
    metrics_path: /prometheus/
    scheme: https
    tls_config:
      ca_file: /etc/prometheus/certs/ca.pem
    static_configs:
      - targets: ['jenkins.cicd.local:10400']

  # 7. SonarQube (Prometheus v3.x Syntax)
  - job_name: 'sonarqube'
    metrics_path: /api/monitoring/metrics
    static_configs:
      - targets: ['sonarqube.cicd.local:9000']
    http_headers:
      X-Sonar-Passcode:
        values: ["&lt;/span&gt;&lt;span class="nv"&gt;$SONAR_WEB_SYSTEMPASSCODE&lt;/span&gt;&lt;span class="sh"&gt;"]

  # 8. Artifactory
  - job_name: 'artifactory'
    metrics_path: /artifactory/api/v1/metrics
    scheme: https
    tls_config:
      ca_file: /etc/prometheus/certs/ca.pem
    static_configs:
      - targets: ['artifactory.cicd.local:8443']
    bearer_token: "&lt;/span&gt;&lt;span class="nv"&gt;$ARTIFACTORY_ADMIN_TOKEN&lt;/span&gt;&lt;span class="sh"&gt;"

  # 9. Mattermost
  - job_name: 'mattermost'
    static_configs:
      - targets: ['mattermost.cicd.local:8067']
&lt;/span&gt;&lt;span class="no"&gt;EOF


&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt; "&lt;/span&gt;&lt;span class="nv"&gt;$PROMETHEUS_BASE&lt;/span&gt;&lt;span class="sh"&gt;/config/web-config.yml"
tls_server_config:
  cert_file: /etc/prometheus/certs/prometheus.crt
  key_file: /etc/prometheus/certs/prometheus.key
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# B. Grafana Config (grafana.ini)&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt; "&lt;/span&gt;&lt;span class="nv"&gt;$GRAFANA_BASE&lt;/span&gt;&lt;span class="sh"&gt;/config/grafana.ini"
[server]
protocol = https
domain = grafana.cicd.local
cert_file = /etc/grafana/certs/grafana.crt
cert_key = /etc/grafana/certs/grafana.key
http_port = 3000

[database]
type = postgres
host = postgres.cicd.local:5432
name = grafana
user = grafana
ssl_mode = require

[security]
admin_user = admin
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# C. Grafana Provisioning (datasources.yaml)&lt;/span&gt;
&lt;span class="c"&gt;# We embed the CA content directly into the YAML for the TLS connection to Prometheus&lt;/span&gt;
&lt;span class="nv"&gt;CA_CONTENT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SRC_CA_CRT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'s/^/          /'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt; "&lt;/span&gt;&lt;span class="nv"&gt;$GRAFANA_BASE&lt;/span&gt;&lt;span class="sh"&gt;/provisioning/datasources/datasources.yaml"
apiVersion: 1

datasources:
  - name: Prometheus
    type: prometheus
    access: proxy
    url: https://prometheus.cicd.local:9090
    isDefault: true
    jsonData:
      tlsAuth: false
      tlsAuthWithCACert: true
    secureJsonData:
      tlsCACert: |
&lt;/span&gt;&lt;span class="nv"&gt;$CA_CONTENT&lt;/span&gt;&lt;span class="sh"&gt;
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# --- 6. Scoped Environment Files ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Phase 5: Generating Env Files ---"&lt;/span&gt;

&lt;span class="c"&gt;# Grafana Env&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt; "&lt;/span&gt;&lt;span class="nv"&gt;$GRAFANA_BASE&lt;/span&gt;&lt;span class="sh"&gt;/grafana.env"
GF_DATABASE_PASSWORD=&lt;/span&gt;&lt;span class="nv"&gt;$GRAFANA_DB_PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;
GF_SECURITY_ADMIN_PASSWORD=&lt;/span&gt;&lt;span class="nv"&gt;$GRAFANA_ADMIN_PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# Elasticsearch Exporter Env&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt; "&lt;/span&gt;&lt;span class="nv"&gt;$HOST_CICD_ROOT&lt;/span&gt;&lt;span class="sh"&gt;/elk/elasticsearch-exporter.env"
ES_URI=https://elastic:&lt;/span&gt;&lt;span class="nv"&gt;$ELASTIC_PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;@elasticsearch.cicd.local:9200
ES_ALL=true
ES_INDICES=true
ES_CA=/certs/ca.pem
ES_SSL_SKIP_VERIFY=false
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# --- 7. Permissions Lockdown ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Phase 6: Locking Down Permissions ---"&lt;/span&gt;

&lt;span class="c"&gt;# Prometheus (UID 65534 - nobody)&lt;/span&gt;
&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; 65534:65534 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROMETHEUS_BASE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="c"&gt;# Grafana (UID 472 - grafana)&lt;/span&gt;
&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; 472:472 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GRAFANA_BASE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Lock keys&lt;/span&gt;
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;600 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROMETHEUS_BASE&lt;/span&gt;&lt;span class="s2"&gt;/config/certs/prometheus.key"&lt;/span&gt;
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;600 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GRAFANA_BASE&lt;/span&gt;&lt;span class="s2"&gt;/config/certs/grafana.key"&lt;/span&gt;
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;600 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GRAFANA_BASE&lt;/span&gt;&lt;span class="s2"&gt;/grafana.env"&lt;/span&gt;
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;600 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOST_CICD_ROOT&lt;/span&gt;&lt;span class="s2"&gt;/elk/elasticsearch-exporter.env"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✅ Architect Setup Complete."&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3.6 Deconstructing the Architect
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;01-setup-monitoring.sh&lt;/code&gt; script is the physical implementation of the theory we just discussed. Let’s break down exactly how it enforces our architecture.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Secrets &amp;amp; Dependencies (Phase 1)
&lt;/h3&gt;

&lt;p&gt;The script begins by sourcing the master &lt;code&gt;cicd.env&lt;/code&gt; file. It validates that critical dependencies—specifically the &lt;code&gt;ARTIFACTORY_ADMIN_TOKEN&lt;/code&gt; and &lt;code&gt;ELASTIC_PASSWORD&lt;/code&gt;—already exist. This enforces the dependency chain: we cannot monitor the Warehouse or the Logs if those services haven't been built yet. It then programmatically generates new high-entropy secrets for Grafana and SonarQube, ensuring no default passwords ever exist in our system. This fulfills the &lt;strong&gt;Pre-Computation Strategy&lt;/strong&gt; outlined in &lt;strong&gt;Section 3.1&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The Map Generation (Phase 4 - Prometheus)
&lt;/h3&gt;

&lt;p&gt;The script uses a heredoc (&lt;code&gt;cat &amp;lt;&amp;lt; EOF&lt;/code&gt;) to write the &lt;code&gt;prometheus.yml&lt;/code&gt; file. This isn't a static copy; it is dynamic. Notice how it injects the &lt;code&gt;$SONAR_WEB_SYSTEMPASSCODE&lt;/code&gt; and &lt;code&gt;$ARTIFACTORY_ADMIN_TOKEN&lt;/code&gt; variables directly into the scrape configuration. This creates the "Map of the City" discussed in &lt;strong&gt;Section 3.2&lt;/strong&gt;, enabling Prometheus to authenticate with our secured endpoints immediately upon startup.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. The Embedded Trust (Phase 4 - Grafana)
&lt;/h3&gt;

&lt;p&gt;In the Grafana provisioning section, the script performs a critical text manipulation operation:&lt;br&gt;
&lt;code&gt;CA_CONTENT=$(cat "$SRC_CA_CRT" | sed 's/^/          /')&lt;/code&gt;.&lt;br&gt;
It reads the Root CA certificate from the host, indents it to match YAML syntax, and embeds it directly into &lt;code&gt;datasources.yaml&lt;/code&gt;. This implements the &lt;strong&gt;Certificate Embedding&lt;/strong&gt; strategy from &lt;strong&gt;Section 3.3&lt;/strong&gt;, allowing Grafana to verify Prometheus's HTTPS identity without needing an external volume mount for trust stores.&lt;/p&gt;
&lt;h3&gt;
  
  
  4. The Permission Fix (Phases 2 &amp;amp; 6)
&lt;/h3&gt;

&lt;p&gt;Finally, the script wraps the configuration generation with explicit permission handling. In Phase 2, it changes directory ownership to the current user to allow writing configs without &lt;code&gt;sudo&lt;/code&gt;. In Phase 6, it performs the "Surgical Strike" discussed in &lt;strong&gt;Section 3.4&lt;/strong&gt;, flipping ownership of the &lt;code&gt;prometheus&lt;/code&gt; directory to UID &lt;code&gt;65534&lt;/code&gt; and the &lt;code&gt;grafana&lt;/code&gt; directory to UID &lt;code&gt;472&lt;/code&gt;. This guarantees that when the containers launch in later chapters, they encounter a filesystem they can actually read.&lt;/p&gt;
&lt;h2&gt;
  
  
  3.6 Deconstructing the Architect
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;01-setup-monitoring.sh&lt;/code&gt; script is the physical implementation of the theory we just discussed. Let’s break down exactly how it enforces our architecture.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Secrets &amp;amp; Dependencies (Phase 1)
&lt;/h3&gt;

&lt;p&gt;The script begins by sourcing the master &lt;code&gt;cicd.env&lt;/code&gt; file. It validates that critical dependencies—specifically the &lt;code&gt;ARTIFACTORY_ADMIN_TOKEN&lt;/code&gt; and &lt;code&gt;ELASTIC_PASSWORD&lt;/code&gt;—already exist. This enforces the dependency chain: we cannot monitor the Warehouse or the Logs if those services haven't been built yet. It then programmatically generates new high-entropy secrets for Grafana and SonarQube, ensuring no default passwords ever exist in our system. This fulfills the &lt;strong&gt;Pre-Computation Strategy&lt;/strong&gt; outlined in &lt;strong&gt;Section 3.1&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. The Map Generation (Phase 4 - Prometheus)
&lt;/h3&gt;

&lt;p&gt;The script uses a heredoc (&lt;code&gt;cat &amp;lt;&amp;lt; EOF&lt;/code&gt;) to write the &lt;code&gt;prometheus.yml&lt;/code&gt; file. This isn't a static copy; it is dynamic. Notice how it injects the &lt;code&gt;$SONAR_WEB_SYSTEMPASSCODE&lt;/code&gt; and &lt;code&gt;$ARTIFACTORY_ADMIN_TOKEN&lt;/code&gt; variables directly into the scrape configuration. This creates the "Map of the City" discussed in &lt;strong&gt;Section 3.2&lt;/strong&gt;, enabling Prometheus to authenticate with our secured endpoints immediately upon startup.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. The Embedded Trust (Phase 4 - Grafana)
&lt;/h3&gt;

&lt;p&gt;In the Grafana provisioning section, the script performs a critical text manipulation operation:&lt;br&gt;
&lt;code&gt;CA_CONTENT=$(cat "$SRC_CA_CRT" | sed 's/^/          /')&lt;/code&gt;.&lt;br&gt;
It reads the Root CA certificate from the host, indents it to match YAML syntax, and embeds it directly into &lt;code&gt;datasources.yaml&lt;/code&gt;. This implements the &lt;strong&gt;Certificate Embedding&lt;/strong&gt; strategy from &lt;strong&gt;Section 3.3&lt;/strong&gt;, allowing Grafana to verify Prometheus's HTTPS identity without needing an external volume mount for trust stores.&lt;/p&gt;
&lt;h3&gt;
  
  
  4. The Permission Fix (Phases 2 &amp;amp; 6)
&lt;/h3&gt;

&lt;p&gt;Finally, the script wraps the configuration generation with explicit permission handling. In Phase 2, it changes directory ownership to the current user to allow writing configs without &lt;code&gt;sudo&lt;/code&gt;. In Phase 6, it performs the "Surgical Strike" discussed in &lt;strong&gt;Section 3.4&lt;/strong&gt;, flipping ownership of the &lt;code&gt;prometheus&lt;/code&gt; directory to UID &lt;code&gt;65534&lt;/code&gt; and the &lt;code&gt;grafana&lt;/code&gt; directory to UID &lt;code&gt;472&lt;/code&gt;. This guarantees that when the containers launch in later chapters, they encounter a filesystem they can actually read.&lt;/p&gt;
&lt;h1&gt;
  
  
  Chapter 4: The Retrofit – Exposing the Metrics
&lt;/h1&gt;
&lt;h2&gt;
  
  
  4.1 The Hidden Pulse
&lt;/h2&gt;

&lt;p&gt;We have successfully generated our "Map of the City" (&lt;code&gt;prometheus.yml&lt;/code&gt;). The Brain knows where to look. However, if we were to launch Prometheus right now, it would be staring at a series of closed doors.&lt;/p&gt;

&lt;p&gt;Most enterprise software ships with observability &lt;strong&gt;disabled&lt;/strong&gt; or strictly limited by default. This is a sound security practice known as "Information Hiding." A metrics endpoint is essentially a high-resolution blueprint of your internal operations. It reveals your query volume, your memory usage, your error rates, and even the topology of your internal network. Exposing this to the public internet—or even to the wrong subnet—is a vulnerability.&lt;/p&gt;

&lt;p&gt;Therefore, our services are currently holding their breath. GitLab's Nginx proxy blocks external access to &lt;code&gt;/-/metrics&lt;/code&gt;. SonarQube demands a specific authentication header. Artifactory and Mattermost have the feature turned off entirely in their configuration files.&lt;/p&gt;

&lt;p&gt;To build our Observatory, we must perform a &lt;strong&gt;"Retrofit."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We are moving into the realm of &lt;strong&gt;Day 2 Operations&lt;/strong&gt;. We are not deploying fresh containers; we are surgically modifying the state of &lt;em&gt;running&lt;/em&gt; infrastructure. We need to open these specific ports and endpoints to our private &lt;code&gt;cicd-net&lt;/code&gt; network without exposing them to the host or the outside world.&lt;/p&gt;

&lt;p&gt;This presents an engineering challenge: consistency. While our goal is the same for every service ("Open the &lt;code&gt;/metrics&lt;/code&gt; endpoint"), the implementation varies wildly because each tool speaks a different configuration language:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Jenkins:&lt;/strong&gt; This is the exception that proves the rule. Because we installed the &lt;code&gt;prometheus&lt;/code&gt; plugin in &lt;strong&gt;Article 8&lt;/strong&gt;, Jenkins is already broadcasting. It exposes &lt;code&gt;/prometheus/&lt;/code&gt; by default, requiring no further action from us today.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Monoliths (GitLab &amp;amp; SonarQube):&lt;/strong&gt; These legacy-style applications are configured via flat text files (&lt;code&gt;gitlab.rb&lt;/code&gt;) or environment variables (&lt;code&gt;sonarqube.env&lt;/code&gt;). We will manipulate them using &lt;strong&gt;Bash&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Modern Stack (Artifactory &amp;amp; Mattermost):&lt;/strong&gt; These newer applications store configuration in structured data formats (YAML) or internal databases accessible only via CLI APIs. We cannot safely edit these with &lt;code&gt;sed&lt;/code&gt; or &lt;code&gt;echo&lt;/code&gt;; we need the precision of &lt;strong&gt;Python&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We will tackle these in two waves, starting with the text-based Monoliths.&lt;/p&gt;
&lt;h2&gt;
  
  
  4.2 Patching the Monoliths (GitLab &amp;amp; SonarQube)
&lt;/h2&gt;

&lt;p&gt;Our first target is the legacy stack. These applications rely on traditional configuration files and environment variables. We will automate their reconfiguration using the &lt;strong&gt;Bash&lt;/strong&gt; script &lt;code&gt;02-patch-services.sh&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This script performs two distinct operations: modifying a host-side text file for GitLab and injecting a secret into a container environment for SonarQube.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Source Code: &lt;code&gt;02-patch-services.sh&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Create this file at &lt;code&gt;~/Documents/FromFirstPrinciples/articles/0013_cicd_part09_prometheus_grafana/02-patch-services.sh&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;#           02-patch-services.sh&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  The "Retrofit" Script.&lt;/span&gt;
&lt;span class="c"&gt;#  Modifies running services to expose metrics endpoints.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  1. GitLab: Updates host-side gitlab.rb &amp;amp; triggers reconfigure.&lt;/span&gt;
&lt;span class="c"&gt;#  2. SonarQube: Injects System Passcode into env &amp;amp; redeploys.&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🔧 Starting Service Retrofit..."&lt;/span&gt;

&lt;span class="c"&gt;# --- 1. Load Secrets &amp;amp; Paths ---&lt;/span&gt;
&lt;span class="nv"&gt;HOST_CICD_ROOT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/cicd_stack"&lt;/span&gt;
&lt;span class="nv"&gt;MASTER_ENV_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOST_CICD_ROOT&lt;/span&gt;&lt;span class="s2"&gt;/cicd.env"&lt;/span&gt;

&lt;span class="c"&gt;# GitLab Paths&lt;/span&gt;
&lt;span class="nv"&gt;GITLAB_CONFIG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOST_CICD_ROOT&lt;/span&gt;&lt;span class="s2"&gt;/gitlab/config/gitlab.rb"&lt;/span&gt;

&lt;span class="c"&gt;# SonarQube Paths&lt;/span&gt;
&lt;span class="nv"&gt;SONAR_BASE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOST_CICD_ROOT&lt;/span&gt;&lt;span class="s2"&gt;/sonarqube"&lt;/span&gt;
&lt;span class="nv"&gt;SONAR_ENV_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SONAR_BASE&lt;/span&gt;&lt;span class="s2"&gt;/sonarqube.env"&lt;/span&gt;
&lt;span class="c"&gt;# Determine deploy script location relative to this script or hardcoded&lt;/span&gt;
&lt;span class="nv"&gt;SONAR_DEPLOY_SCRIPT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/Documents/FromFirstPrinciples/articles/0010_cicd_part06_sonarqube/03-deploy-sonarqube.sh"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: Master env file not found."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Load SONAR_WEB_SYSTEMPASSCODE&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt; +a

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SONAR_WEB_SYSTEMPASSCODE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: SONAR_WEB_SYSTEMPASSCODE not found in cicd.env"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Did you run 01-setup-monitoring.sh?"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# --- 2. Patch GitLab (Host File Update) ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Patching GitLab ---"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GITLAB_CONFIG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="c"&gt;# Idempotency check: Don't append if it exists&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s2"&gt;"monitoring_whitelist"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GITLAB_CONFIG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Appending monitoring whitelist to host config..."&lt;/span&gt;
        &lt;span class="c"&gt;# We need sudo because gitlab.rb is owned by root&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"gitlab_rails['monitoring_whitelist'] = ['172.30.0.0/24', '127.0.0.1']"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GITLAB_CONFIG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Config updated."&lt;/span&gt;
    &lt;span class="k"&gt;else
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Whitelist already present in gitlab.rb."&lt;/span&gt;
    &lt;span class="k"&gt;fi&lt;/span&gt;

    &lt;span class="c"&gt;# Trigger Reconfigure if container is running&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;gitlab&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Triggering GitLab Reconfigure (this will take a minute)..."&lt;/span&gt;
        docker &lt;span class="nb"&gt;exec &lt;/span&gt;gitlab gitlab-ctl reconfigure &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✅ GitLab Reconfigured."&lt;/span&gt;
    &lt;span class="k"&gt;else
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"⚠️  GitLab container is not running. Changes will apply on next start."&lt;/span&gt;
    &lt;span class="k"&gt;fi
else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"❌ ERROR: &lt;/span&gt;&lt;span class="nv"&gt;$GITLAB_CONFIG&lt;/span&gt;&lt;span class="s2"&gt; not found on host."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# --- 3. Patch SonarQube (Env Injection &amp;amp; Redeploy) ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Patching SonarQube ---"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SONAR_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Injecting System Passcode into sonarqube.env..."&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s2"&gt;"SONAR_WEB_SYSTEMPASSCODE"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SONAR_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
        &lt;span class="c"&gt;# Ensure we can write (we likely own the dir, but file might be 600)&lt;/span&gt;
        &lt;span class="nb"&gt;chmod &lt;/span&gt;600 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SONAR_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SONAR_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"# Metrics Access"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SONAR_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"SONAR_WEB_SYSTEMPASSCODE=&lt;/span&gt;&lt;span class="nv"&gt;$SONAR_WEB_SYSTEMPASSCODE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SONAR_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;else
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Passcode already present."&lt;/span&gt;
    &lt;span class="k"&gt;fi

    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Redeploying SonarQube to apply changes..."&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-x&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SONAR_DEPLOY_SCRIPT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
        &lt;span class="c"&gt;# Run the original deploy script from its directory context&lt;/span&gt;
        &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SONAR_DEPLOY_SCRIPT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; ./03-deploy-sonarqube.sh&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✅ SonarQube Patched and Redeploying."&lt;/span&gt;
    &lt;span class="k"&gt;else
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"❌ ERROR: Sonar deploy script not found at &lt;/span&gt;&lt;span class="nv"&gt;$SONAR_DEPLOY_SCRIPT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Please check the path or ensure Article 10 files exist."&lt;/span&gt;
        &lt;span class="nb"&gt;exit &lt;/span&gt;1
    &lt;span class="k"&gt;fi
else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"❌ ERROR: sonarqube.env not found at &lt;/span&gt;&lt;span class="nv"&gt;$SONAR_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✨ Retrofit Complete."&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deconstructing the Retrofit
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. GitLab: The Whitelist (&lt;code&gt;monitoring_whitelist&lt;/code&gt;)&lt;/strong&gt;&lt;br&gt;
GitLab's integrated Prometheus exporter is protected by a strict IP whitelist. By default, it allows only &lt;code&gt;localhost&lt;/code&gt;. Prometheus, however, lives at &lt;code&gt;172.30.0.x&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Config:&lt;/strong&gt; The script appends &lt;code&gt;gitlab_rails['monitoring_whitelist'] = ['172.30.0.0/24', '127.0.0.1']&lt;/code&gt; to the host-side &lt;code&gt;gitlab.rb&lt;/code&gt; file. This explicitly trusts our entire Docker subnet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Execution:&lt;/strong&gt; Instead of restarting the heavy GitLab container (which takes 5 minutes), we trigger &lt;code&gt;gitlab-ctl reconfigure&lt;/code&gt; via &lt;code&gt;docker exec&lt;/code&gt;. This recompiles the internal Nginx configuration on the fly, applying the whitelist in about 60 seconds.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. SonarQube: The Secret Handshake (&lt;code&gt;SONAR_WEB_SYSTEMPASSCODE&lt;/code&gt;)&lt;/strong&gt;&lt;br&gt;
SonarQube handles metrics differently. It does not use IP whitelisting; it uses a shared secret.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Injection:&lt;/strong&gt; The script reads the &lt;code&gt;SONAR_WEB_SYSTEMPASSCODE&lt;/code&gt; (which we generated in &lt;code&gt;01-setup-monitoring.sh&lt;/code&gt;) from the master &lt;code&gt;cicd.env&lt;/code&gt; and injects it into the scoped &lt;code&gt;sonarqube.env&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Redeployment:&lt;/strong&gt; Because Docker environment variables are immutable once a container is created, we cannot just "reload" SonarQube. We must destroy and recreate the container. The script automates this by calling the original &lt;code&gt;03-deploy-sonarqube.sh&lt;/code&gt; script from 0010_cicd_part06_sonarqube, ensuring a clean state transition.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  4.2 Patching the Monoliths (GitLab &amp;amp; SonarQube)
&lt;/h2&gt;

&lt;p&gt;Our first target is the legacy stack. These applications rely on traditional configuration files and environment variables. We will automate their reconfiguration using the &lt;strong&gt;Bash&lt;/strong&gt; script &lt;code&gt;02-patch-services.sh&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This script performs two distinct operations: modifying a host-side text file for GitLab and injecting a secret into a container environment for SonarQube.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Source Code: &lt;code&gt;02-patch-services.sh&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Create this file at &lt;code&gt;~/Documents/FromFirstPrinciples/articles/0013_cicd_part09_prometheus_grafana/02-patch-services.sh&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;#           02-patch-services.sh&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  The "Retrofit" Script.&lt;/span&gt;
&lt;span class="c"&gt;#  Modifies running services to expose metrics endpoints.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  1. GitLab: Updates host-side gitlab.rb &amp;amp; triggers reconfigure.&lt;/span&gt;
&lt;span class="c"&gt;#  2. SonarQube: Injects System Passcode into env &amp;amp; redeploys.&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🔧 Starting Service Retrofit..."&lt;/span&gt;

&lt;span class="c"&gt;# --- 1. Load Secrets &amp;amp; Paths ---&lt;/span&gt;
&lt;span class="nv"&gt;HOST_CICD_ROOT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/cicd_stack"&lt;/span&gt;
&lt;span class="nv"&gt;MASTER_ENV_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOST_CICD_ROOT&lt;/span&gt;&lt;span class="s2"&gt;/cicd.env"&lt;/span&gt;

&lt;span class="c"&gt;# GitLab Paths&lt;/span&gt;
&lt;span class="nv"&gt;GITLAB_CONFIG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOST_CICD_ROOT&lt;/span&gt;&lt;span class="s2"&gt;/gitlab/config/gitlab.rb"&lt;/span&gt;

&lt;span class="c"&gt;# SonarQube Paths&lt;/span&gt;
&lt;span class="nv"&gt;SONAR_BASE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOST_CICD_ROOT&lt;/span&gt;&lt;span class="s2"&gt;/sonarqube"&lt;/span&gt;
&lt;span class="nv"&gt;SONAR_ENV_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SONAR_BASE&lt;/span&gt;&lt;span class="s2"&gt;/sonarqube.env"&lt;/span&gt;
&lt;span class="c"&gt;# Determine deploy script location relative to this script or hardcoded&lt;/span&gt;
&lt;span class="nv"&gt;SONAR_DEPLOY_SCRIPT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/Documents/FromFirstPrinciples/articles/0010_cicd_part06_sonarqube/03-deploy-sonarqube.sh"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: Master env file not found."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Load SONAR_WEB_SYSTEMPASSCODE&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt; +a

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SONAR_WEB_SYSTEMPASSCODE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: SONAR_WEB_SYSTEMPASSCODE not found in cicd.env"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Did you run 01-setup-monitoring.sh?"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# --- 2. Patch GitLab (Host File Update) ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Patching GitLab ---"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GITLAB_CONFIG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="c"&gt;# Idempotency check: Don't append if it exists&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s2"&gt;"monitoring_whitelist"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GITLAB_CONFIG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Appending monitoring whitelist to host config..."&lt;/span&gt;
        &lt;span class="c"&gt;# We need sudo because gitlab.rb is owned by root&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"gitlab_rails['monitoring_whitelist'] = ['172.30.0.0/24', '127.0.0.1']"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GITLAB_CONFIG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Config updated."&lt;/span&gt;
    &lt;span class="k"&gt;else
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Whitelist already present in gitlab.rb."&lt;/span&gt;
    &lt;span class="k"&gt;fi&lt;/span&gt;

    &lt;span class="c"&gt;# Trigger Reconfigure if container is running&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;gitlab&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Triggering GitLab Reconfigure (this will take a minute)..."&lt;/span&gt;
        docker &lt;span class="nb"&gt;exec &lt;/span&gt;gitlab gitlab-ctl reconfigure &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✅ GitLab Reconfigured."&lt;/span&gt;
    &lt;span class="k"&gt;else
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"⚠️  GitLab container is not running. Changes will apply on next start."&lt;/span&gt;
    &lt;span class="k"&gt;fi
else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"❌ ERROR: &lt;/span&gt;&lt;span class="nv"&gt;$GITLAB_CONFIG&lt;/span&gt;&lt;span class="s2"&gt; not found on host."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# --- 3. Patch SonarQube (Env Injection &amp;amp; Redeploy) ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Patching SonarQube ---"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SONAR_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Injecting System Passcode into sonarqube.env..."&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s2"&gt;"SONAR_WEB_SYSTEMPASSCODE"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SONAR_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
        &lt;span class="c"&gt;# Ensure we can write (we likely own the dir, but file might be 600)&lt;/span&gt;
        &lt;span class="nb"&gt;chmod &lt;/span&gt;600 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SONAR_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SONAR_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"# Metrics Access"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SONAR_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"SONAR_WEB_SYSTEMPASSCODE=&lt;/span&gt;&lt;span class="nv"&gt;$SONAR_WEB_SYSTEMPASSCODE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SONAR_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;else
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Passcode already present."&lt;/span&gt;
    &lt;span class="k"&gt;fi

    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Redeploying SonarQube to apply changes..."&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-x&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SONAR_DEPLOY_SCRIPT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
        &lt;span class="c"&gt;# Run the original deploy script from its directory context&lt;/span&gt;
        &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SONAR_DEPLOY_SCRIPT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; ./03-deploy-sonarqube.sh&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✅ SonarQube Patched and Redeploying."&lt;/span&gt;
    &lt;span class="k"&gt;else
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"❌ ERROR: Sonar deploy script not found at &lt;/span&gt;&lt;span class="nv"&gt;$SONAR_DEPLOY_SCRIPT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Please check the path or ensure Article 10 files exist."&lt;/span&gt;
        &lt;span class="nb"&gt;exit &lt;/span&gt;1
    &lt;span class="k"&gt;fi
else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"❌ ERROR: sonarqube.env not found at &lt;/span&gt;&lt;span class="nv"&gt;$SONAR_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✨ Retrofit Complete."&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deconstructing the Retrofit
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. GitLab: The Whitelist (&lt;code&gt;monitoring_whitelist&lt;/code&gt;)&lt;/strong&gt;&lt;br&gt;
GitLab's integrated Prometheus exporter is protected by a strict IP whitelist. By default, it allows only &lt;code&gt;localhost&lt;/code&gt;. Prometheus, however, lives at &lt;code&gt;172.30.0.x&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Config:&lt;/strong&gt; The script appends &lt;code&gt;gitlab_rails['monitoring_whitelist'] = ['172.30.0.0/24', '127.0.0.1']&lt;/code&gt; to the host-side &lt;code&gt;gitlab.rb&lt;/code&gt; file. This explicitly trusts our entire Docker subnet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Execution:&lt;/strong&gt; Instead of restarting the heavy GitLab container (which takes 5 minutes), we trigger &lt;code&gt;gitlab-ctl reconfigure&lt;/code&gt; via &lt;code&gt;docker exec&lt;/code&gt;. This recompiles the internal Nginx configuration on the fly, applying the whitelist in about 60 seconds.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. SonarQube: The Secret Handshake (&lt;code&gt;SONAR_WEB_SYSTEMPASSCODE&lt;/code&gt;)&lt;/strong&gt;&lt;br&gt;
SonarQube handles metrics differently. It does not use IP whitelisting; it uses a shared secret.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Injection:&lt;/strong&gt; The script reads the &lt;code&gt;SONAR_WEB_SYSTEMPASSCODE&lt;/code&gt; (which we generated in &lt;code&gt;01-setup-monitoring.sh&lt;/code&gt;) from the master &lt;code&gt;cicd.env&lt;/code&gt; and injects it into the scoped &lt;code&gt;sonarqube.env&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Redeployment:&lt;/strong&gt; Because Docker environment variables are immutable once a container is created, we cannot just "reload" SonarQube. We must destroy and recreate the container. The script automates this by calling the original &lt;code&gt;03-deploy-sonarqube.sh&lt;/code&gt; script from Article 10, ensuring a clean state transition.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  4.3 Patching the Modern Stack (Artifactory &amp;amp; Mattermost)
&lt;/h2&gt;

&lt;p&gt;For our modern, cloud-native applications, we cannot rely on simple text manipulation. Their configurations are stored in structured formats (YAML) or internal databases. We handle this complexity with the Python script &lt;code&gt;03-patch-additional-services.py&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Source Code: &lt;code&gt;03-patch-additional-services.py&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Create this file at &lt;code&gt;~/Documents/FromFirstPrinciples/articles/0013_cicd_part09_prometheus_grafana/03-patch-additional-services.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env python3
&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;yaml&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;

&lt;span class="c1"&gt;# --- Configuration ---
&lt;/span&gt;&lt;span class="n"&gt;HOME_DIR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;HOME&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;CICD_STACK_DIR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HOME_DIR&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cicd_stack&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# Artifactory Paths
&lt;/span&gt;&lt;span class="n"&gt;ART_VAR_DIR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CICD_STACK_DIR&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;artifactory&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;var&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;ART_CONFIG_FILE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ART_VAR_DIR&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;etc&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;system.yaml&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="c1"&gt;# PATH TO ORIGINAL DEPLOY SCRIPT
&lt;/span&gt;&lt;span class="n"&gt;ART_DEPLOY_SCRIPT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HOME_DIR&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Documents/FromFirstPrinciples/articles/0009_cicd_part05_artifactory/05-deploy-artifactory.sh&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# Mattermost Configuration
&lt;/span&gt;&lt;span class="n"&gt;MM_CONTAINER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mattermost&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmd_list&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Runs a shell command and prints status.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   EXEC: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;cmd_list&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;capture_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   ✅ Success.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CalledProcessError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   ❌ Failed: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;patch_artifactory&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--- Patching Artifactory Configuration (&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ART_CONFIG_FILE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;) ---&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;ART_CONFIG_FILE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;❌ Error: Config file not found at &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ART_CONFIG_FILE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 1. Read existing YAML
&lt;/span&gt;    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ART_CONFIG_FILE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;yaml&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;safe_load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;PermissionError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;❌ Error: Cannot read config file. Try running: sudo chmod o+r &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ART_CONFIG_FILE&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;yaml&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;YAMLError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;❌ Error parsing YAML: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 2. Modify Structure (Idempotent)
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;shared&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;shared&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;metrics&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;shared&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;shared&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;metrics&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;shared&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;metrics&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;enabled&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   &amp;gt; Set shared.metrics.enabled = true&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;artifactory&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;artifactory&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;metrics&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;artifactory&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;artifactory&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;metrics&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;artifactory&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;metrics&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;enabled&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   &amp;gt; Set artifactory.metrics.enabled = true&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 3. Write to Temp File
&lt;/span&gt;    &lt;span class="n"&gt;tmp_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/tmp/artifactory_system.yaml.tmp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tmp_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;w&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;yaml&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;safe_dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default_flow_style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sort_keys&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 4. Overwrite Protected File
&lt;/span&gt;    &lt;span class="nf"&gt;run_command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sudo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tmp_path&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ART_CONFIG_FILE&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Overwriting system.yaml (sudo)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tmp_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   ✅ Patched system.yaml&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 5. Redeploy using Original Script (Instead of Restart)
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--- Redeploying Artifactory via Script ---&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ART_DEPLOY_SCRIPT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;access&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ART_DEPLOY_SCRIPT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;X_OK&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   EXEC: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ART_DEPLOY_SCRIPT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; ...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# Run relative to script directory so it finds its local env files
&lt;/span&gt;            &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ART_DEPLOY_SCRIPT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;cwd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ART_DEPLOY_SCRIPT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;shell&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   ✅ Artifactory Redeployed.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CalledProcessError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   ❌ Failed to redeploy Artifactory.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   ⚠️  Deploy script not found or not executable at: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ART_DEPLOY_SCRIPT&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;      Falling back to docker restart...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;run_command&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;docker&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;restart&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;artifactory&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Restarting container&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;patch_mattermost&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--- Patching Mattermost Configuration (via mmctl) ---&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;docker&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;inspect&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MM_CONTAINER&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DEVNULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CalledProcessError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;⚠️  Mattermost container &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;MM_CONTAINER&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; not running. Skipping.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;mmctl_set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;docker&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;exec&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-i&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MM_CONTAINER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mmctl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--local&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;config&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;set&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="nf"&gt;run_command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Setting &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; = &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Enable Metrics &amp;amp; Set Port
&lt;/span&gt;    &lt;span class="nf"&gt;mmctl_set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MetricsSettings.Enable&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;mmctl_set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MetricsSettings.ListenAddress&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;:8067&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Reload
&lt;/span&gt;    &lt;span class="n"&gt;reload_cmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;docker&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;exec&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-i&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MM_CONTAINER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mmctl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--local&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;config&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reload&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nf"&gt;run_command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reload_cmd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Reloading Mattermost Config&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🔧 Starting Additional Services Patcher...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;patch_artifactory&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;patch_mattermost&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;✨ All patches applied successfully.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deconstructing the Retrofit
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Artifactory: YAML Surgery&lt;/strong&gt;&lt;br&gt;
Artifactory 7 stores its configuration in &lt;code&gt;system.yaml&lt;/code&gt;. This file is owned by UID 1030 and is often read-only to regular users.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Logic:&lt;/strong&gt; We use Python's &lt;code&gt;PyYAML&lt;/code&gt; library to load the file structure into memory. We navigate the nested dictionary to &lt;code&gt;shared.metrics.enabled&lt;/code&gt; and &lt;code&gt;artifactory.metrics.enabled&lt;/code&gt;, setting both to &lt;code&gt;True&lt;/code&gt;. This ensures metrics are collected for both the shared microservices (Router/Access) and the core Artifactory service.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Execution:&lt;/strong&gt; We write the modified config to a temporary file and use &lt;code&gt;sudo cp&lt;/code&gt; to overwrite the protected original. We then trigger the original &lt;code&gt;05-deploy-artifactory.sh&lt;/code&gt; script to force a clean restart of the Java process.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Mattermost: The CLI Override&lt;/strong&gt;&lt;br&gt;
Mattermost offers a powerful command-line interface (&lt;code&gt;mmctl&lt;/code&gt;) that can modify the server's configuration database in real-time.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Logic:&lt;/strong&gt; We execute &lt;code&gt;mmctl config set MetricsSettings.Enable true&lt;/code&gt; directly inside the container via &lt;code&gt;docker exec&lt;/code&gt;. We also bind the listener to &lt;code&gt;:8067&lt;/code&gt; to avoid port conflicts with the main web application.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Execution:&lt;/strong&gt; We finalize the change with &lt;code&gt;mmctl config reload&lt;/code&gt;. Mattermost is unique in our stack; it applies these changes &lt;em&gt;instantly&lt;/em&gt; without killing the process, demonstrating true cloud-native behavior.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  4.4 Execution &amp;amp; Verification
&lt;/h2&gt;

&lt;p&gt;Now that we have built our tools, we run them in sequence.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Run the Bash Patcher:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nb"&gt;chmod&lt;/span&gt; +x 02-patch-services.sh
   ./02-patch-services.sh

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;em&gt;Result:&lt;/em&gt; GitLab reconfigures (approx. 60s) and SonarQube restarts (approx. 2 mins).&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Run the Python Patcher:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nb"&gt;chmod&lt;/span&gt; +x 03-patch-additional-services.py
   ./03-patch-additional-services.py

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;em&gt;Result:&lt;/em&gt; Artifactory restarts (approx. 2 mins) and Mattermost updates immediately.&lt;/p&gt;

&lt;p&gt;Our applications are now broadcasting. The doors are open. In the next chapter, we will deploy the translators to handle the infrastructure layer.&lt;/p&gt;
&lt;h1&gt;
  
  
  Chapter 5: The Translators – Infrastructure Sensors
&lt;/h1&gt;
&lt;h2&gt;
  
  
  5.1 The "Translation" Problem
&lt;/h2&gt;

&lt;p&gt;We have successfully retrofitted our applications. GitLab, Jenkins, Artifactory, and Mattermost are now broadcasting their internal metrics in the standardized Prometheus format. We know &lt;em&gt;what&lt;/em&gt; the software is doing.&lt;/p&gt;

&lt;p&gt;But software does not run in a vacuum; it runs on infrastructure.&lt;/p&gt;

&lt;p&gt;If the Host Machine runs out of RAM, every container crashes. If the Docker Daemon hangs due to disk I/O throttling, the pipeline stops. If the Elasticsearch cluster splits its brain, the logs vanish.&lt;/p&gt;

&lt;p&gt;Crucially, &lt;strong&gt;Prometheus cannot speak to these components directly.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;Linux Kernel&lt;/strong&gt; speaks in system calls and &lt;code&gt;/proc&lt;/code&gt; files.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Docker Daemon&lt;/strong&gt; speaks via a Unix Socket and Control Groups (cgroups).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Elasticsearch&lt;/strong&gt; speaks via a proprietary JSON REST API.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Prometheus only speaks HTTP. It expects to hit a &lt;code&gt;/metrics&lt;/code&gt; endpoint and receive a plain-text list of key-value pairs. It does not know how to read a &lt;code&gt;cgroup&lt;/code&gt; or query a Unix socket.&lt;/p&gt;

&lt;p&gt;To bridge this gap, we must deploy &lt;strong&gt;Exporters&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;An Exporter is a lightweight "Translation Agent." It sits right next to the infrastructure component it is monitoring (often as a sidecar). Its job is simple:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Query:&lt;/strong&gt; It polls the opaque internal state of the system (e.g., reading &lt;code&gt;/proc/meminfo&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Translate:&lt;/strong&gt; It converts that raw data into the standardized Prometheus exposition format (e.g., &lt;code&gt;node_memory_MemTotal_bytes&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Serve:&lt;/strong&gt; It exposes a lightweight HTTP server so Prometheus can scrape the translated data.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In this chapter, we will deploy three distinct translators to cover the blind spots in our city:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Node Exporter:&lt;/strong&gt; For the Host Hardware (CPU, RAM, Disk, Network).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;cAdvisor:&lt;/strong&gt; For the Docker Engine (Container resource usage).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Elasticsearch Exporter:&lt;/strong&gt; For the Log Storage Cluster (Health and Indices).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We will automate this deployment with &lt;code&gt;04-deploy-exporters.sh&lt;/code&gt;. However, accessing the &lt;em&gt;Host&lt;/em&gt; hardware from inside a &lt;em&gt;Container&lt;/em&gt; requires us to break the very isolation rules we have spent eight articles enforcing.&lt;/p&gt;
&lt;h2&gt;
  
  
  5.2 The Host Sensor: Node Exporter (The "Boundary Breaker")
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Node Exporter&lt;/strong&gt; is the industry standard for hardware monitoring. It reads from the Linux &lt;code&gt;/proc&lt;/code&gt; and &lt;code&gt;/sys&lt;/code&gt; filesystems to report on CPU usage, memory distribution, disk I/O latency, and network traffic.&lt;/p&gt;

&lt;p&gt;However, deploying Node Exporter in a containerized environment introduces a fundamental paradox: we want to monitor the &lt;strong&gt;Host&lt;/strong&gt;, but the container is explicitly designed to be isolated &lt;em&gt;from&lt;/em&gt; the Host. A standard container sees only its own virtual CPU slices and its own virtual network interface. It has no visibility into the physical hardware.&lt;/p&gt;

&lt;p&gt;To bridge this gap, we must deliberately break the isolation model we have spent eight articles enforcing. In our deployment script, we apply two powerful flags that "tear down the walls" of the container:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;--network host&lt;/code&gt;:&lt;/strong&gt; We remove the network namespace isolation. This allows the exporter to see the host's actual physical network interfaces (eth0, wlan0), not just the virtual interface of the container.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;--pid host&lt;/code&gt;:&lt;/strong&gt; We share the Process ID namespace. This allows the exporter to see the host's process table, preventing the "blindness" typical of containerized monitoring tools where they can only see their own children.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  The Security "Dragon": The LAN Exposure
&lt;/h3&gt;

&lt;p&gt;Running a container in Host Mode (&lt;code&gt;--network host&lt;/code&gt;) comes with a dangerous side effect: &lt;strong&gt;Port Exposure.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When a normal container listens on port 9100, that port is reachable only inside the Docker network unless we explicitly map it (&lt;code&gt;-p 9100:9100&lt;/code&gt;). But when a Host Mode container listens on port 9100, it opens that port on &lt;strong&gt;every network interface of the physical machine&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This means your detailed hardware metrics—potentially revealing kernel versions, running processes, and exact resource usage—would be instantly accessible to anyone on your office WiFi or corporate LAN. In a "Zero Trust" architecture, this information leakage is unacceptable.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Solution: The Gateway Bind
&lt;/h3&gt;

&lt;p&gt;We solve this by binding the exporter strictly to the &lt;strong&gt;Docker Gateway IP&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In our network design (0005_cicd_part01_docker), we established &lt;code&gt;cicd-net&lt;/code&gt; with a gateway of &lt;strong&gt;&lt;code&gt;172.30.0.1&lt;/code&gt;&lt;/strong&gt;. This IP address represents the "Host Machine" from the perspective of the containers. By configuring Node Exporter to listen &lt;em&gt;only&lt;/em&gt; on this specific IP (&lt;code&gt;--web.listen-address=172.30.0.1:9100&lt;/code&gt;), we create a "private door".&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;From the LAN:&lt;/strong&gt; The port 9100 is closed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;From the Container Network:&lt;/strong&gt; Prometheus can reach &lt;code&gt;172.30.0.1:9100&lt;/code&gt; and scrape the metrics.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  The Trust Gap: Certificates vs. IPs
&lt;/h3&gt;

&lt;p&gt;This binding solution creates a new problem: &lt;strong&gt;TLS Identity&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Standard SSL certificates are issued to &lt;strong&gt;Domain Names&lt;/strong&gt; (e.g., &lt;code&gt;node-exporter.cicd.local&lt;/code&gt;). However, because of our specific network binding, Prometheus must connect to this target via its &lt;strong&gt;IP Address&lt;/strong&gt; (&lt;code&gt;172.30.0.1&lt;/code&gt;), not a DNS name.&lt;/p&gt;

&lt;p&gt;If we used a standard certificate, the TLS handshake would fail because the address in the URL (&lt;code&gt;172.30.0.1&lt;/code&gt;) does not match the Common Name in the certificate (&lt;code&gt;node-exporter...&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;To fix this, our &lt;code&gt;04-deploy-exporters.sh&lt;/code&gt; script includes a specialized function called &lt;code&gt;generate_node_exporter_cert&lt;/code&gt;. This function dynamically generates a custom OpenSSL configuration that adds a specific &lt;strong&gt;Subject Alternative Name (SAN)&lt;/strong&gt; entry:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[alt_names]&lt;/span&gt;
&lt;span class="py"&gt;IP.2&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;172.30.0.1&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By baking the Gateway IP directly into the cryptographic identity of the certificate, we ensure that Prometheus can connect securely to the raw IP address without breaking the chain of trust.&lt;/p&gt;

&lt;h2&gt;
  
  
  5.3 The Container Sensor: cAdvisor (The "Introspector")
&lt;/h2&gt;

&lt;p&gt;While Node Exporter gives us the "Landlord's View" of the building (total electricity used, total water consumed), it tells us nothing about the individual tenants. If the server is slow, Node Exporter can tell us &lt;em&gt;that&lt;/em&gt; CPU usage is high, but it cannot tell us &lt;em&gt;who&lt;/em&gt; is responsible. Is Jenkins compiling a massive C++ project? Is GitLab performing a database migration? Or has the Artifactory Java process gone rogue?&lt;/p&gt;

&lt;p&gt;To answer these questions, we need an &lt;strong&gt;Introspector&lt;/strong&gt;. We need a tool that can look inside the Docker engine, identify every running container, and measure its specific resource consumption against the kernel's accounting ledgers.&lt;/p&gt;

&lt;p&gt;The industry standard for this is &lt;strong&gt;cAdvisor&lt;/strong&gt; (Container Advisor) by Google.&lt;/p&gt;

&lt;p&gt;Deploying cAdvisor involves navigating the complex boundary between the Docker Daemon and the Linux Kernel. Unlike a standard web app that stays in its lane, cAdvisor is designed to be invasive. It essentially "spies" on its neighbors. To allow this, our deployment script grants it significant privileges.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Privilege Hurdle
&lt;/h3&gt;

&lt;p&gt;In &lt;code&gt;04-deploy-exporters.sh&lt;/code&gt;, we launch cAdvisor with a specific set of flags that might look alarming to a security-conscious engineer:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;--privileged&lt;/code&gt;:&lt;/strong&gt; We grant the container extended privileges.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;--volume /:/rootfs:ro&lt;/code&gt;:&lt;/strong&gt; We mount the entire host filesystem as read-only.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;--volume /var/run:/var/run:ro&lt;/code&gt;:&lt;/strong&gt; We mount the Docker socket.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;--volume /sys:/sys:ro&lt;/code&gt;:&lt;/strong&gt; We mount the kernel's system directory.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Why is this "God Mode" access necessary?&lt;/p&gt;

&lt;p&gt;It comes down to &lt;strong&gt;Control Groups (cgroups)&lt;/strong&gt;. This is the Linux kernel feature that Docker uses to limit how much CPU and RAM a container can use. Every time a container writes to RAM or uses a CPU cycle, the kernel updates a counter in a virtual file located deep inside &lt;code&gt;/sys/fs/cgroup&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For cAdvisor to report that "Jenkins is using 2GB of RAM," it must be able to read these raw kernel counters directly from the host's &lt;code&gt;/sys&lt;/code&gt; directory. Furthermore, to know that "cgroup ID 4f3a..." actually corresponds to the name "jenkins," it must talk to the Docker Socket to retrieve the container metadata.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Security Mitigation
&lt;/h3&gt;

&lt;p&gt;Granting a container access to the Docker socket is equivalent to giving it root access to the host. If cAdvisor were compromised, an attacker could use that socket to spawn new privileged containers and take over the machine.&lt;/p&gt;

&lt;p&gt;We mitigate this risk through &lt;strong&gt;Network Isolation&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Unlike Node Exporter, which we deliberately exposed to the Host Network, cAdvisor is a "Dark Container."&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We do &lt;strong&gt;not&lt;/strong&gt; use &lt;code&gt;--network host&lt;/code&gt;. It lives inside &lt;code&gt;cicd-net&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;We do &lt;strong&gt;not&lt;/strong&gt; publish any ports (&lt;code&gt;-p 8080:8080&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means cAdvisor is unreachable from the host machine, the LAN, or the internet. It listens on port 8080 &lt;em&gt;only&lt;/em&gt; inside the private Docker network. The only entity that can talk to it is Prometheus (which is also on &lt;code&gt;cicd-net&lt;/code&gt;). By combining high privilege (local access) with zero visibility (network access), we adhere to the Principle of Least Privilege in a containerized context.&lt;/p&gt;

&lt;h2&gt;
  
  
  5.4 The Middleware Sensor: Elasticsearch Exporter (The "Bridge")
&lt;/h2&gt;

&lt;p&gt;Our final infrastructure target is the &lt;strong&gt;Elasticsearch&lt;/strong&gt; cluster we built in 0012_cicd_part08_elk. This is the "Memory" of our city, storing gigabytes of build logs and audit trails. If it fills up or slows down, our "Investigation Office" goes dark.&lt;/p&gt;

&lt;p&gt;Elasticsearch exposes a wealth of data via its API (&lt;code&gt;_cluster/health&lt;/code&gt;, &lt;code&gt;_nodes/stats&lt;/code&gt;), but it does so in hierarchical &lt;strong&gt;JSON&lt;/strong&gt;. Prometheus cannot ingest JSON; it requires a flat, line-delimited format.&lt;/p&gt;

&lt;p&gt;To solve this, we deploy the &lt;strong&gt;Elasticsearch Exporter&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This component functions as a &lt;strong&gt;Protocol Bridge&lt;/strong&gt;. It sits between the Brain and the Memory.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Incoming:&lt;/strong&gt; It accepts a scrape request from Prometheus.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Translation:&lt;/strong&gt; It makes authenticated, encrypted API calls to the Elasticsearch cluster.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Outgoing:&lt;/strong&gt; It flattens the JSON response into metrics like &lt;code&gt;elasticsearch_cluster_health_status{color="green"}&lt;/code&gt; and serves them to Prometheus.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The Authentication Chain
&lt;/h3&gt;

&lt;p&gt;Deploying this bridge in a secured environment requires navigating a complex authentication chain. We effectively have two secured conversations happening simultaneously:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Prometheus  Exporter:&lt;/strong&gt; Prometheus must trust the Exporter. We handle this by mounting our standard &lt;code&gt;web-config.yml&lt;/code&gt; and the &lt;code&gt;elasticsearch-exporter&lt;/code&gt; certificates we staged in Chapter 3.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exporter  Elasticsearch:&lt;/strong&gt; The Exporter must authenticate with the Database. It needs the &lt;code&gt;elastic&lt;/code&gt; superuser credentials to query deep statistics.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We solve the second link using the &lt;strong&gt;URI Injection&lt;/strong&gt; pattern. In Chapter 3 (&lt;code&gt;01-setup-monitoring.sh&lt;/code&gt;), we pre-computed a file named &lt;code&gt;elasticsearch-exporter.env&lt;/code&gt; containing the full connection string:&lt;br&gt;
&lt;code&gt;ES_URI=https://elastic:PASSWORD@elasticsearch.cicd.local:9200&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In our deployment script, we extract this URI and pass it to the container:&lt;br&gt;
&lt;code&gt;--es.uri="$ES_URI"&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Trust Triangle
&lt;/h3&gt;

&lt;p&gt;Finally, we must ensure the Exporter trusts the Database. Since Elasticsearch is serving a self-signed certificate (from our CA), the Exporter (written in Go) will reject the connection by default.&lt;/p&gt;

&lt;p&gt;We map our Root CA into the container at &lt;code&gt;/certs/ca.pem&lt;/code&gt; and explicitly flag the application to use it:&lt;br&gt;
&lt;code&gt;--es.ca=/certs/ca.pem&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This closes the loop. Prometheus verifies the Exporter; the Exporter verifies the Database. The Chain of Trust is unbroken.&lt;/p&gt;
&lt;h2&gt;
  
  
  5.5 Execution &amp;amp; Verification
&lt;/h2&gt;

&lt;p&gt;With our three translators defined—Node Exporter for the Host, cAdvisor for the Containers, and ES Exporter for the Middleware—we are ready to deploy.&lt;/p&gt;

&lt;p&gt;We automate this using the &lt;code&gt;04-deploy-exporters.sh&lt;/code&gt; script. This script orchestrates the entire process: generating the custom Gateway certificate, fixing permissions, and launching the containers with the necessary privilege flags.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Source Code: &lt;code&gt;04-deploy-exporters.sh&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Create this file at &lt;code&gt;~/Documents/FromFirstPrinciples/articles/0013_cicd_part09_prometheus_grafana/04-deploy-exporters.sh&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;#           03-deploy-exporters.sh&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  The "Translators" Script.&lt;/span&gt;
&lt;span class="c"&gt;#  Deploys metrics exporters for Host, Docker, and ES.&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🚀 Deploying Exporters (The Translators)..."&lt;/span&gt;

&lt;span class="c"&gt;# --- 1. Load Paths &amp;amp; Secrets ---&lt;/span&gt;
&lt;span class="nv"&gt;HOST_CICD_ROOT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/cicd_stack"&lt;/span&gt;
&lt;span class="nv"&gt;ELK_BASE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOST_CICD_ROOT&lt;/span&gt;&lt;span class="s2"&gt;/elk"&lt;/span&gt;
&lt;span class="nv"&gt;PROMETHEUS_BASE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOST_CICD_ROOT&lt;/span&gt;&lt;span class="s2"&gt;/prometheus"&lt;/span&gt;
&lt;span class="nv"&gt;CA_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOST_CICD_ROOT&lt;/span&gt;&lt;span class="s2"&gt;/ca"&lt;/span&gt;
&lt;span class="nv"&gt;CA_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"password"&lt;/span&gt;  &lt;span class="c"&gt;# Password for the Root CA Key&lt;/span&gt;

&lt;span class="c"&gt;# Exporter Config Dirs&lt;/span&gt;
&lt;span class="nv"&gt;NODE_CONF_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROMETHEUS_BASE&lt;/span&gt;&lt;span class="s2"&gt;/node_exporter"&lt;/span&gt;
&lt;span class="nv"&gt;ES_EXP_CONF_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ELK_BASE&lt;/span&gt;&lt;span class="s2"&gt;/elasticsearch-exporter"&lt;/span&gt;

&lt;span class="c"&gt;# --- 2. Permission Fix (The Host Takeover) ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Preparing Directories ---"&lt;/span&gt;
&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NODE_CONF_DIR&lt;/span&gt;&lt;span class="s2"&gt;/certs"&lt;/span&gt;
&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ES_EXP_CONF_DIR&lt;/span&gt;&lt;span class="s2"&gt;/certs"&lt;/span&gt;

&lt;span class="c"&gt;# Take ownership to current user for writing configs&lt;/span&gt;
&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$USER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;:&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$USER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NODE_CONF_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$USER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;:&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$USER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ES_EXP_CONF_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# --- 3. Custom Certificate Generation ---&lt;/span&gt;

ensure_generic_cert&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;service_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;cert_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CA_DIR&lt;/span&gt;&lt;span class="s2"&gt;/pki/services/&lt;/span&gt;&lt;span class="nv"&gt;$service_name&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$service_name&lt;/span&gt;&lt;span class="s2"&gt;.crt.pem"&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$cert_path&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   ⚠️  Certificate for &lt;/span&gt;&lt;span class="nv"&gt;$service_name&lt;/span&gt;&lt;span class="s2"&gt; not found. Generating..."&lt;/span&gt;
        &lt;span class="o"&gt;(&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; ../0006_cicd_part02_certificate_authority &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; ./02-issue-service-cert.sh &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$service_name&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   ✅ Generated &lt;/span&gt;&lt;span class="nv"&gt;$service_name&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;else
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   ℹ️  Certificate for &lt;/span&gt;&lt;span class="nv"&gt;$service_name&lt;/span&gt;&lt;span class="s2"&gt; already exists."&lt;/span&gt;
    &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

generate_node_exporter_cert&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Generating Custom Certificate for Node Exporter ---"&lt;/span&gt;

    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;SERVICE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"node-exporter"&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;DOMAIN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"node-exporter.cicd.local"&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;GATEWAY_IP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"172.30.0.1"&lt;/span&gt;

    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;KEY_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NODE_CONF_DIR&lt;/span&gt;&lt;span class="s2"&gt;/certs/node-exporter.key"&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;CRT_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NODE_CONF_DIR&lt;/span&gt;&lt;span class="s2"&gt;/certs/node-exporter.crt"&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;CSR_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NODE_CONF_DIR&lt;/span&gt;&lt;span class="s2"&gt;/certs/node-exporter.csr"&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;EXT_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NODE_CONF_DIR&lt;/span&gt;&lt;span class="s2"&gt;/certs/v3.ext"&lt;/span&gt;

    &lt;span class="c"&gt;# Check if we need to generate (check if Gateway IP is in existing cert)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CRT_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        if &lt;/span&gt;openssl x509 &lt;span class="nt"&gt;-text&lt;/span&gt; &lt;span class="nt"&gt;-noout&lt;/span&gt; &lt;span class="nt"&gt;-in&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CRT_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;/dev/null | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GATEWAY_IP&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
            &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   ℹ️  Valid Node Exporter cert exists (IP &lt;/span&gt;&lt;span class="nv"&gt;$GATEWAY_IP&lt;/span&gt;&lt;span class="s2"&gt; present)."&lt;/span&gt;
            &lt;span class="k"&gt;return
        else
            &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   ⚠️  Old cert found without Gateway IP. Regenerating..."&lt;/span&gt;
        &lt;span class="k"&gt;fi
    fi

    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   1. Generating Private Key..."&lt;/span&gt;
    openssl genrsa &lt;span class="nt"&gt;-out&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$KEY_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2048

    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   2. Generating CSR..."&lt;/span&gt;
    openssl req &lt;span class="nt"&gt;-new&lt;/span&gt; &lt;span class="nt"&gt;-key&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$KEY_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-out&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CSR_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-subj&lt;/span&gt; &lt;span class="s2"&gt;"/C=ZA/ST=Gauteng/L=Johannesburg/O=Local CICD Stack/CN=&lt;/span&gt;&lt;span class="nv"&gt;$DOMAIN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   3. Creating SAN Extension..."&lt;/span&gt;
    &lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt; "&lt;/span&gt;&lt;span class="nv"&gt;$EXT_FILE&lt;/span&gt;&lt;span class="sh"&gt;"
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names

[alt_names]
DNS.1 = &lt;/span&gt;&lt;span class="nv"&gt;$DOMAIN&lt;/span&gt;&lt;span class="sh"&gt;
DNS.2 = localhost
IP.1 = 127.0.0.1
IP.2 = &lt;/span&gt;&lt;span class="nv"&gt;$GATEWAY_IP&lt;/span&gt;&lt;span class="sh"&gt;
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   4. Signing with Root CA..."&lt;/span&gt;
    &lt;span class="nb"&gt;sudo &lt;/span&gt;openssl x509 &lt;span class="nt"&gt;-req&lt;/span&gt; &lt;span class="nt"&gt;-in&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CSR_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-CA&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CA_DIR&lt;/span&gt;&lt;span class="s2"&gt;/pki/certs/ca.pem"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-CAkey&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CA_DIR&lt;/span&gt;&lt;span class="s2"&gt;/pki/private/ca.key"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-CAcreateserial&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-out&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CRT_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-days&lt;/span&gt; 825 &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-sha256&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-extfile&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$EXT_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-passin&lt;/span&gt; pass:&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CA_PASSWORD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    &lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$USER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;:&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$USER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CRT_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CSR_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$EXT_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   ✅ Generated Custom Node Exporter Cert."&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Verifying Certificates ---"&lt;/span&gt;
generate_node_exporter_cert
ensure_generic_cert &lt;span class="s2"&gt;"elasticsearch-exporter.cicd.local"&lt;/span&gt;

&lt;span class="c"&gt;# --- 4. Stage Certificates ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Staging Certificates ---"&lt;/span&gt;

&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CA_DIR&lt;/span&gt;&lt;span class="s2"&gt;/pki/services/elasticsearch-exporter.cicd.local/elasticsearch-exporter.cicd.local.crt.pem"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ES_EXP_CONF_DIR&lt;/span&gt;&lt;span class="s2"&gt;/certs/elasticsearch-exporter.crt"&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CA_DIR&lt;/span&gt;&lt;span class="s2"&gt;/pki/services/elasticsearch-exporter.cicd.local/elasticsearch-exporter.cicd.local.key.pem"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ES_EXP_CONF_DIR&lt;/span&gt;&lt;span class="s2"&gt;/certs/elasticsearch-exporter.key"&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CA_DIR&lt;/span&gt;&lt;span class="s2"&gt;/pki/certs/ca.pem"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ES_EXP_CONF_DIR&lt;/span&gt;&lt;span class="s2"&gt;/certs/ca.pem"&lt;/span&gt;

&lt;span class="c"&gt;# --- 5. Generate TLS Web Configs ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Generating TLS Web Configs ---"&lt;/span&gt;

&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt; "&lt;/span&gt;&lt;span class="nv"&gt;$NODE_CONF_DIR&lt;/span&gt;&lt;span class="sh"&gt;/web-config.yml"
tls_server_config:
  cert_file: /etc/node_exporter/certs/node-exporter.crt
  key_file: /etc/node_exporter/certs/node-exporter.key
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt; "&lt;/span&gt;&lt;span class="nv"&gt;$ES_EXP_CONF_DIR&lt;/span&gt;&lt;span class="sh"&gt;/web-config.yml"
tls_server_config:
  cert_file: /etc/elasticsearch_exporter/certs/elasticsearch-exporter.crt
  key_file: /etc/elasticsearch_exporter/certs/elasticsearch-exporter.key
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# --- 6. Permission Lockdown (UID 65534) ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Locking Permissions (UID 65534) ---"&lt;/span&gt;
&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; 65534:65534 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NODE_CONF_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; 65534:65534 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ES_EXP_CONF_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;600 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NODE_CONF_DIR&lt;/span&gt;&lt;span class="s2"&gt;/certs/node-exporter.key"&lt;/span&gt;
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;600 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ES_EXP_CONF_DIR&lt;/span&gt;&lt;span class="s2"&gt;/certs/elasticsearch-exporter.key"&lt;/span&gt;

&lt;span class="c"&gt;# --- 7. Deploy Node Exporter ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Deploying Node Exporter (Host Hardware) ---"&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;node-exporter&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then &lt;/span&gt;docker &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; node-exporter&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;fi

&lt;/span&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; node-exporter &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--restart&lt;/span&gt; always &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network&lt;/span&gt; host &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--pid&lt;/span&gt; host &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; &lt;span class="s2"&gt;"/:/host:ro,rslave"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NODE_CONF_DIR&lt;/span&gt;&lt;span class="s2"&gt;/web-config.yml"&lt;/span&gt;:/etc/node_exporter/web-config.yml:ro &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NODE_CONF_DIR&lt;/span&gt;&lt;span class="s2"&gt;/certs"&lt;/span&gt;:/etc/node_exporter/certs:ro &lt;span class="se"&gt;\&lt;/span&gt;
  quay.io/prometheus/node-exporter:latest &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--path&lt;/span&gt;.rootfs&lt;span class="o"&gt;=&lt;/span&gt;/host &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--web&lt;/span&gt;.listen-address&lt;span class="o"&gt;=&lt;/span&gt;172.30.0.1:9100 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--web&lt;/span&gt;.config.file&lt;span class="o"&gt;=&lt;/span&gt;/etc/node_exporter/web-config.yml

&lt;span class="c"&gt;# --- 8. Deploy Elasticsearch Exporter ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Deploying ES Exporter ---"&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;elasticsearch-exporter&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then &lt;/span&gt;docker &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; elasticsearch-exporter&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nv"&gt;ES_ENV_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOST_CICD_ROOT&lt;/span&gt;&lt;span class="s2"&gt;/elk/elasticsearch-exporter.env"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ES_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nv"&gt;ES_URI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;sudo grep &lt;/span&gt;ES_URI &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ES_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nt"&gt;-f2-&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"❌ ERROR: ES env file not found at &lt;/span&gt;&lt;span class="nv"&gt;$ES_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

&lt;/span&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; elasticsearch-exporter &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--restart&lt;/span&gt; always &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network&lt;/span&gt; cicd-net &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--hostname&lt;/span&gt; elasticsearch-exporter.cicd.local &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ES_EXP_CONF_DIR&lt;/span&gt;&lt;span class="s2"&gt;/web-config.yml"&lt;/span&gt;:/web-config.yml:ro &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ES_EXP_CONF_DIR&lt;/span&gt;&lt;span class="s2"&gt;/certs"&lt;/span&gt;:/etc/elasticsearch_exporter/certs:ro &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ES_EXP_CONF_DIR&lt;/span&gt;&lt;span class="s2"&gt;/certs/ca.pem"&lt;/span&gt;:/certs/ca.pem:ro &lt;span class="se"&gt;\&lt;/span&gt;
  quay.io/prometheuscommunity/elasticsearch-exporter:latest &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--web&lt;/span&gt;.config.file&lt;span class="o"&gt;=&lt;/span&gt;/web-config.yml &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--es&lt;/span&gt;.uri&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ES_URI&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--es&lt;/span&gt;.ca&lt;span class="o"&gt;=&lt;/span&gt;/certs/ca.pem &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--es&lt;/span&gt;.all &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--es&lt;/span&gt;.indices

&lt;span class="c"&gt;# --- 9. Deploy cAdvisor ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Deploying cAdvisor (Container Stats) ---"&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;cadvisor&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then &lt;/span&gt;docker &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; cadvisor&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;fi

&lt;/span&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; cadvisor &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--restart&lt;/span&gt; always &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network&lt;/span&gt; cicd-net &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--hostname&lt;/span&gt; cadvisor.cicd.local &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--privileged&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--device&lt;/span&gt; /dev/kmsg &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; /:/rootfs:ro &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; /var/run:/var/run:ro &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; /sys:/sys:ro &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; /var/lib/docker/:/var/lib/docker:ro &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; /dev/disk/:/dev/disk:ro &lt;span class="se"&gt;\&lt;/span&gt;
  ghcr.io/google/cadvisor:latest

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✅ Exporters Deployed."&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   - Node Exporter: https://172.30.0.1:9100/metrics (Gateway Bind)"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   - ES Exporter:   https://elasticsearch-exporter.cicd.local:9114/metrics"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   - cAdvisor:      http://cadvisor:8080/metrics"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the script from your host machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x 04-deploy-exporters.sh
./04-deploy-exporters.sh

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Output Analysis:&lt;/strong&gt;&lt;br&gt;
Watch the logs closely. You will see:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Certificate Generation:&lt;/strong&gt; The script detects the missing &lt;code&gt;node-exporter&lt;/code&gt; cert and generates a new one with the Gateway IP SAN.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Container Launch:&lt;/strong&gt; Three containers spin up (&lt;code&gt;node-exporter&lt;/code&gt;, &lt;code&gt;cadvisor&lt;/code&gt;, &lt;code&gt;elasticsearch-exporter&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Access Points:&lt;/strong&gt; The script concludes by printing the targets:

&lt;ul&gt;
&lt;li&gt;Node Exporter: &lt;code&gt;https://172.30.0.1:9100/metrics&lt;/code&gt; (Gateway Access).&lt;/li&gt;
&lt;li&gt;ES Exporter: &lt;code&gt;https://elasticsearch-exporter.cicd.local:9114/metrics&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;cAdvisor: &lt;code&gt;http://cadvisor:8080/metrics&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Our sensors are deployed. The city is now broadcasting on all frequencies. Before we turn on the "Brain" to record this data, we must perform a connectivity audit to ensure the signals are reaching the control center.&lt;/p&gt;
&lt;h1&gt;
  
  
  Chapter 6: Quality Assurance – The Connectivity Verification
&lt;/h1&gt;
&lt;h2&gt;
  
  
  6.1 The "Blind Spot" Risk
&lt;/h2&gt;

&lt;p&gt;We have generated our configurations, patched our services, and deployed our translators. On paper, our monitoring infrastructure is complete.&lt;/p&gt;

&lt;p&gt;However, if we were to launch Prometheus immediately, we would be taking a significant operational risk. In a complex distributed system like our "City," configuration does not guarantee connectivity.&lt;/p&gt;

&lt;p&gt;The moment Prometheus starts, it will attempt to scrape all nine targets simultaneously. If there is a single misconfiguration—a firewall rule blocking port 9100, a typo in a certificate Common Name, or a missing authentication token—Prometheus will flood its own logs with generic, unhelpful errors like &lt;code&gt;context deadline exceeded&lt;/code&gt; or &lt;code&gt;connection refused&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Diagnosing these errors from &lt;em&gt;within&lt;/em&gt; the Prometheus logs is difficult because Prometheus is a high-level aggregator. It tells you &lt;em&gt;that&lt;/em&gt; it failed, but rarely &lt;em&gt;why&lt;/em&gt; it failed in sufficient detail to debug a TLS handshake or a DNS resolution issue.&lt;/p&gt;

&lt;p&gt;This creates a &lt;strong&gt;Blind Spot&lt;/strong&gt;. We have built the pipes, but we haven't checked if they flow.&lt;/p&gt;

&lt;p&gt;To mitigate this, we adopt the philosophy of &lt;strong&gt;"Trust but Verify."&lt;/strong&gt; Before we deploy the "Brain" (Prometheus), we must audit the nervous system. We need to perform an integration test that simulates a Prometheus scrape from the exact network vantage point that Prometheus will occupy.&lt;/p&gt;

&lt;p&gt;Our verification scope covers three distinct layers of failure:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Network Reachability:&lt;/strong&gt; Can we route packets to the target IP/Port? (Is the door open?)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Identity Verification:&lt;/strong&gt; Does the internal DNS (&lt;code&gt;*.cicd.local&lt;/code&gt;) resolve correctly, and does the SSL Certificate match that name? (Is this the right house?)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security Authorization:&lt;/strong&gt; Does the target accept our credentials (&lt;code&gt;Bearer Token&lt;/code&gt; or &lt;code&gt;Passcode&lt;/code&gt;) and return a valid HTTP 200 payload? (Do we have the key?)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Only when all three layers pass for every single service will we clear the "Brain" for deployment.&lt;/p&gt;
&lt;h2&gt;
  
  
  6.2 The Vantage Point (Host vs. Container)
&lt;/h2&gt;

&lt;p&gt;A common mistake in validating distributed systems is testing from the wrong vantage point. It is tempting to simply run &lt;code&gt;curl https://gitlab.cicd.local:10300/&lt;/code&gt; from your host terminal. If it returns a 200 OK, you might assume the system is healthy.&lt;/p&gt;

&lt;p&gt;This assumption is dangerous because your &lt;strong&gt;Host Machine&lt;/strong&gt; has superpowers that your containers do not.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The &lt;code&gt;/etc/hosts&lt;/code&gt; Cheat:&lt;/strong&gt; Your host machine resolves &lt;code&gt;gitlab.cicd.local&lt;/code&gt; to &lt;code&gt;127.0.0.1&lt;/code&gt; because we manually edited the hosts file in Article 7. Containers, however, rely on the internal Docker DNS server (&lt;code&gt;127.0.0.11&lt;/code&gt;). If Docker DNS fails, your host will still work, but your containers will be blind.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Network Bypass:&lt;/strong&gt; Your host machine accesses the containers via &lt;strong&gt;Port Mapping&lt;/strong&gt; (e.g., &lt;code&gt;127.0.0.1:10300&lt;/code&gt;). Containers access each other via the &lt;strong&gt;Bridge Network&lt;/strong&gt; (&lt;code&gt;172.30.0.x:10300&lt;/code&gt;). Testing via &lt;code&gt;localhost&lt;/code&gt; bypasses the entire software-defined network (SDN) layer, failing to catch firewall rules or routing errors inside the bridge.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Trust Store Divergence:&lt;/strong&gt; Your host machine trusts the CA because we ran &lt;code&gt;sudo update-ca-certificates&lt;/code&gt;. The containers trust the CA because we baked it into their images (or mounted it). These are two completely separate filesystems. A pass on the host does not guarantee a pass in the container.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To perform a valid audit, we must simulate the perspective of Prometheus itself. We need to stand inside the &lt;code&gt;cicd-net&lt;/code&gt; network, use the Docker DNS resolver, and rely on the container's internal CA bundle.&lt;/p&gt;

&lt;p&gt;We achieve this by executing our verification logic &lt;strong&gt;inside&lt;/strong&gt; the &lt;code&gt;dev-container&lt;/code&gt;. This container acts as our "Test Probe." It sits on the same network as Prometheus, uses the same DNS, and has the same CA certificate mounted. If the &lt;code&gt;dev-container&lt;/code&gt; can see the targets, we can be confident that Prometheus will see them too.&lt;/p&gt;
&lt;h2&gt;
  
  
  6.3 The Verifier Script (&lt;code&gt;05-verify-services.sh&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;To automate this audit, we utilize the &lt;code&gt;05-verify-services.sh&lt;/code&gt; script. This tool acts as the "Integration Test" for the entire network mesh we have built over the last few articles. It bridges the gap between the host (where we hold the keys) and the container network (where the locks are).&lt;/p&gt;
&lt;h3&gt;
  
  
  Architectural Highlights
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. The Secret Bridge (Memory Injection)&lt;/strong&gt;&lt;br&gt;
We face a security dilemma: the authentication tokens (&lt;code&gt;SONAR_WEB_SYSTEMPASSCODE&lt;/code&gt;, &lt;code&gt;ARTIFACTORY_ADMIN_TOKEN&lt;/code&gt;) live in &lt;code&gt;cicd.env&lt;/code&gt; on the Host. We need to use them inside the &lt;code&gt;dev-container&lt;/code&gt; to run &lt;code&gt;curl&lt;/code&gt;. We cannot simply &lt;code&gt;cp&lt;/code&gt; the env file into the container, as that would risk leaving sensitive artifacts on the container's filesystem.&lt;/p&gt;

&lt;p&gt;The script solves this by reading the secrets into memory on the host and then injecting them directly into the environment of the &lt;code&gt;docker exec&lt;/code&gt; process:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;SONAR_PASS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SONAR_PASS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;ART_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ART_TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  dev-container &lt;span class="se"&gt;\&lt;/span&gt;
  bash &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'...'&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures the secrets exist only in the RAM of the running test process and vanish immediately after the test completes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The Reusable Probe (&lt;code&gt;check_metric&lt;/code&gt;)&lt;/strong&gt;&lt;br&gt;
To avoid writing repetitive &lt;code&gt;curl&lt;/code&gt; commands, the script defines a Bash function &lt;code&gt;check_metric&lt;/code&gt; inside the container context. This function abstracts away the complexity of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Handling both HTTP and HTTPS.&lt;/li&gt;
&lt;li&gt;injecting arbitrary headers (Bearer Tokens vs. Sonar Passcodes).&lt;/li&gt;
&lt;li&gt;Parsing the output to verify that actual content was returned (preventing false positives from empty 200 OK responses).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  The Source Code: &lt;code&gt;05-verify-services.sh&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Create this file at &lt;code&gt;~/Documents/FromFirstPrinciples/articles/0013_cicd_part09_prometheus_grafana/05-verify-services.sh&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;#           05-verify-services.sh&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  The "Verifier" Script.&lt;/span&gt;
&lt;span class="c"&gt;#  Runs a test loop inside the dev-container to validate&lt;/span&gt;
&lt;span class="c"&gt;#  that all 8 endpoints are reachable and returning metrics.&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="c"&gt;# --- 1. Load Secrets from Host ---&lt;/span&gt;
&lt;span class="nv"&gt;ENV_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/cicd_stack/cicd.env"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="c"&gt;# We source the file so Bash handles quote removal automatically.&lt;/span&gt;
    &lt;span class="c"&gt;# We run this in a subshell so we don't pollute the main script environment excessively.&lt;/span&gt;
    &lt;span class="nb"&gt;eval&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;
        &lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt;
        &lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="c"&gt;# Print the variables we need so the parent script can capture them&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"export SONAR_PASS='&lt;/span&gt;&lt;span class="nv"&gt;$SONAR_WEB_SYSTEMPASSCODE&lt;/span&gt;&lt;span class="s2"&gt;'"&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"export ART_TOKEN='&lt;/span&gt;&lt;span class="nv"&gt;$ARTIFACTORY_ADMIN_TOKEN&lt;/span&gt;&lt;span class="s2"&gt;'"&lt;/span&gt;
        &lt;span class="nb"&gt;set&lt;/span&gt; +a
    &lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"❌ ERROR: cicd.env not found."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🚀 Starting Metrics Verification (Targeting: dev-container)..."&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"---------------------------------------------------"&lt;/span&gt;

&lt;span class="c"&gt;# --- 2. Execute Loop Inside Container ---&lt;/span&gt;
&lt;span class="c"&gt;# We pass the secrets as environment variables to the container command&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;SONAR_PASS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SONAR_PASS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;ART_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ART_TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  dev-container &lt;span class="se"&gt;\&lt;/span&gt;
  bash &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'
    # Function to check a metric endpoint
    check_metric() {
        NAME=$1
        URL=$2
        HEADER_FLAG=${3:-}
        HEADER_VAL=${4:-}

        echo "🔍 $NAME"
        echo "   URL: $URL"

        # We capture the output. If curl fails, it usually prints to stderr.
        # We use tail -2 to show the last few lines of the metric payload.
        if [ -n "$HEADER_FLAG" ]; then
            CONTENT=$(curl -s "$HEADER_FLAG" "$HEADER_VAL" "$URL" | tail -n 2)
        else
            CONTENT=$(curl -s "$URL" | tail -n 2)
        fi

        # Check if content is empty (curl failed silently or empty response)
        if [ -z "$CONTENT" ]; then
            echo "   ❌ FAILED (No Content)"
        else
            echo "$CONTENT"
            echo "   ✅ OK"
        fi
        echo "---------------------------------------------------"
    }

    # --- Infrastructure (The Translators) ---
    check_metric "Node Exporter" "https://172.30.0.1:9100/metrics"
    check_metric "ES Exporter"   "https://elasticsearch-exporter.cicd.local:9114/metrics"
    check_metric "cAdvisor"      "http://cadvisor.cicd.local:8080/metrics"

    # --- Applications (The Retrofit) ---
    check_metric "GitLab"        "https://gitlab.cicd.local:10300/-/metrics"
    check_metric "Jenkins"       "https://jenkins.cicd.local:10400/prometheus/"
    check_metric "Mattermost"    "http://mattermost.cicd.local:8067/metrics"

    # Authenticated Checks
    check_metric "SonarQube"     "http://sonarqube.cicd.local:9000/api/monitoring/metrics" \
        "-H" "X-Sonar-Passcode: $SONAR_PASS"

    check_metric "Artifactory"   "https://artifactory.cicd.local:8443/artifactory/api/v1/metrics" \
        "-H" "Authorization: Bearer $ART_TOKEN"
'&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  6.4 Execution &amp;amp; Forensics
&lt;/h2&gt;

&lt;p&gt;We are now ready to audit the city. Run the verification script from your host machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x 05-verify-services.sh
./05-verify-services.sh

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pro Tip:&lt;/strong&gt; The script defaults to showing only the last 2 lines of output (using &lt;code&gt;tail -n 2&lt;/code&gt;) to keep the console clean. If you wish to inspect the full flood of telemetry—which spans thousands of lines—you can edit the script to remove the &lt;code&gt;| tail -n 2&lt;/code&gt; pipe and then redirect the output to a file for manual review:&lt;br&gt;
&lt;code&gt;./05-verify-services.sh &amp;gt; full_metrics_dump.txt&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  The Evidence
&lt;/h3&gt;

&lt;p&gt;When you run the standard script, you should see a cascade of green checks. This is your "Connectivity Certificate."&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🚀 Starting Metrics Verification (Targeting: dev-container)...
---------------------------------------------------
🔍 Node Exporter
   URL: https://172.30.0.1:9100/metrics
promhttp_metric_handler_requests_total{code="503"} 0
   ✅ OK
---------------------------------------------------
🔍 cAdvisor
   URL: http://cadvisor.cicd.local:8080/metrics
process_virtual_memory_max_bytes 1.8446744073709552e+19
   ✅ OK
---------------------------------------------------
🔍 GitLab
   URL: https://gitlab.cicd.local:10300/-/metrics
ruby_sampler_duration_seconds_total 499.7820231985699
   ✅ OK
---------------------------------------------------
🔍 SonarQube
   URL: http://sonarqube.cicd.local:9000/api/monitoring/metrics
sonarqube_health_web_status 1.0
   ✅ OK
...

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you see &lt;code&gt;✅ OK&lt;/code&gt; for all 8 targets, you have proven three critical facts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Routing:&lt;/strong&gt; The &lt;code&gt;cicd-net&lt;/code&gt; bridge is correctly routing traffic between containers and the gateway.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DNS:&lt;/strong&gt; The internal names &lt;code&gt;gitlab.cicd.local&lt;/code&gt; etc. are resolving to the correct internal IPs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trust:&lt;/strong&gt; The &lt;code&gt;dev-container&lt;/code&gt; (and therefore Prometheus) successfully negotiated TLS handshakes with our internal Certificate Authority.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;But looking at these logs, you might wonder: &lt;strong&gt;What exactly are we looking at?&lt;/strong&gt;&lt;br&gt;
What does &lt;code&gt;ruby_sampler_duration_seconds_total&lt;/code&gt; actually mean? Why is &lt;code&gt;sonarqube_health_web_status&lt;/code&gt; exactly 1.0?&lt;/p&gt;

&lt;p&gt;To move from "Verification" to "Understanding," we need to learn the language of Prometheus.&lt;/p&gt;
&lt;h2&gt;
  
  
  6.5 Metric Anatomy: Understanding the Signal
&lt;/h2&gt;

&lt;p&gt;The output we just witnessed is not random noise. It is a highly structured language known as the &lt;strong&gt;Prometheus Exposition Format&lt;/strong&gt;. Every line follows a strict syntax that tells the database exactly how to store and index the data.&lt;/p&gt;

&lt;p&gt;To an uninitiated eye, it looks like a wall of text. But once you understand the four fundamental "Data Types," you can read the health of your server like a dashboard.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. The Counter (The "Odometer")
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Identifier:&lt;/strong&gt; Usually ends in &lt;code&gt;_total&lt;/code&gt; (e.g., &lt;code&gt;node_cpu_seconds_total&lt;/code&gt;, &lt;code&gt;http_requests_total&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Behavior:&lt;/strong&gt; It starts at zero when the process launches and &lt;strong&gt;only goes up&lt;/strong&gt;. It never decreases (unless the process crashes and restarts).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Analogy:&lt;/strong&gt; Think of the odometer in a car. It tells you the car has driven 100,000 kilometers in its lifetime.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Usage:&lt;/strong&gt; The raw number is mostly useless ("This server has handled 1 million requests since 2021"). The value lies in the &lt;strong&gt;Rate of Change&lt;/strong&gt;. By comparing the counter now vs. 1 minute ago, Prometheus can calculate "Requests Per Second."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Example from our Log:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node_network_transmit_drop_total 4671

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This doesn't mean the network is failing &lt;em&gt;now&lt;/em&gt;. It means 4,671 packets have been dropped since the machine turned on. If this number jumps to 5,000 in the next second, &lt;em&gt;then&lt;/em&gt; we have a crisis.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. The Gauge (The "Speedometer")
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Identifier:&lt;/strong&gt; Standard nouns (e.g., &lt;code&gt;node_memory_MemFree_bytes&lt;/code&gt;, &lt;code&gt;sonarqube_health_web_status&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Behavior:&lt;/strong&gt; It can go up and down arbitrarily.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Analogy:&lt;/strong&gt; Think of a speedometer or a fuel gauge. It tells you the state of the system &lt;strong&gt;right now&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Usage:&lt;/strong&gt; You don't calculate the rate of a gauge; you look at its absolute value. Is the tank empty? Is the temperature too high?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Example from our Log:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;process_virtual_memory_max_bytes 1.84e+19

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This is a snapshot. At this exact millisecond, the process has access to this much virtual memory.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. The Histogram (The "Heatmap")
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Identifier:&lt;/strong&gt; Appears as a triplet: &lt;code&gt;_bucket&lt;/code&gt;, &lt;code&gt;_sum&lt;/code&gt;, and &lt;code&gt;_count&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Behavior:&lt;/strong&gt; It groups observations into "buckets" to track distribution, usually for latency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Analogy:&lt;/strong&gt; Imagine sorting mail into slots based on weight: "Under 10g", "Under 50g", "Under 100g".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Usage:&lt;/strong&gt; This allows us to calculate &lt;strong&gt;Percentiles&lt;/strong&gt; (e.g., the P99). It answers questions like "Do 99% of my users see a response time under 0.5 seconds?" even if the average is much lower.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Example from our Log (Mattermost):&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mattermost_api_time_bucket{le="0.1"} 15
mattermost_api_time_bucket{le="+Inf"} 20

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This tells us that out of 20 total requests (&lt;code&gt;+Inf&lt;/code&gt;), 15 of them completed in &lt;strong&gt;L&lt;/strong&gt;ess than or &lt;strong&gt;E&lt;/strong&gt;qual to (&lt;code&gt;le&lt;/code&gt;) 0.1 seconds.&lt;/p&gt;
&lt;h3&gt;
  
  
  4. The Summary (The "Pre-Calculated")
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Identifier:&lt;/strong&gt; Contains a &lt;code&gt;quantile&lt;/code&gt; label (e.g., &lt;code&gt;quantile="0.9"&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Behavior:&lt;/strong&gt; Similar to a Histogram, but the heavy math (calculating the P99) is done by the &lt;strong&gt;Client&lt;/strong&gt; (the app), not the Server (Prometheus).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Usage:&lt;/strong&gt; Useful for accurate snapshots of complex runtimes (like Go or Java garbage collection) without burdening the monitoring server with expensive calculations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Example from our Log (Jenkins/Java):&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;jvm_gc_collection_seconds{quantile="0.5"} 0.003

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This tells us the median (0.5) garbage collection pause was 3 milliseconds. We cannot re-aggregate this (you cannot average an average), but it gives us an instant health check.&lt;/p&gt;
&lt;h2&gt;
  
  
  6.6 Decoding the City's Voice (Practical Examples)
&lt;/h2&gt;

&lt;p&gt;Now that we understand the grammar, we can translate the specific messages our city is sending. The &lt;code&gt;verification&lt;/code&gt; script output provides a glimpse into the operational reality of our stack.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. The Infrastructure Voice (Node Exporter &amp;amp; cAdvisor)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Resource Tank:&lt;/strong&gt; &lt;code&gt;node_memory_MemFree_bytes&lt;/code&gt; (Gauge).
If this gauge drops near zero, the host is starving. In a Linux environment, "Free" memory is often low because the kernel caches files, so we usually combine this with &lt;code&gt;node_memory_Cached_bytes&lt;/code&gt; to get the true "Available" memory.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The OOM Killer:&lt;/strong&gt; &lt;code&gt;container_memory_failures_total&lt;/code&gt; (Counter).
This is one of the most critical signals in the Docker ecosystem. If this counter increases, it means a container tried to grab more RAM than its limit allowed, and the kernel likely killed it (OOM Killed). If you see this incrementing for SonarQube, your Java Heap is too big for the container.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. The Application Voice (GitLab, Jenkins, SonarQube)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitLab's Heartbeat:&lt;/strong&gt; &lt;code&gt;ruby_sampler_duration_seconds_total&lt;/code&gt; (Counter).
GitLab is a massive Ruby on Rails monolith. This metric tracks the time the Ruby runtime spends just managing itself (sampling stack traces for performance profiling). A spike here indicates the application is struggling under its own weight, independent of user traffic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SonarQube's Pulse:&lt;/strong&gt; &lt;code&gt;sonarqube_health_web_status&lt;/code&gt; (Gauge).
This is a binary signal: &lt;code&gt;1.0&lt;/code&gt; means healthy, &lt;code&gt;0.0&lt;/code&gt; means dead. It’s simple, but vital. It tells us that the web server is not just running, but successfully connected to its Database and Elasticsearch backend.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Jenkins' Throughput:&lt;/strong&gt; &lt;code&gt;jvm_memory_pool_allocated_bytes_created&lt;/code&gt; (Counter).
Jenkins is memory-hungry. This counter shows the churn—how many bytes of temporary objects are being created. In a heavy build environment, this number skyrockets as pipelines instantiate thousands of short-lived variables.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. The Storage Voice (Artifactory)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Warehouse Capacity:&lt;/strong&gt; &lt;code&gt;jfrt_stg_summary_repo_size_bytes&lt;/code&gt; (Gauge).
This tells us the physical disk consumption of a specific repository (e.g., &lt;code&gt;libs-release-local&lt;/code&gt;). It allows us to set alerts: "Warn me when the Maven repository exceeds 50GB."&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  6.7 Conclusion
&lt;/h2&gt;

&lt;p&gt;We have done the hard work. We have retrofitted the endpoints, built the translators, pierced the isolation layers, and verified the signals.&lt;/p&gt;

&lt;p&gt;The nervous system is fully active. Every component of our architecture—from the silicon of the CPU to the Ruby code of GitLab—is now broadcasting a continuous stream of detailed telemetry.&lt;/p&gt;

&lt;p&gt;But currently, these signals are just vanishing into the void. We have no "Brain" to record them, analyze them, or alert us when they deviate from the norm.&lt;/p&gt;

&lt;p&gt;In the next chapter, we will deploy &lt;strong&gt;Prometheus&lt;/strong&gt;. We will configure it to scrape these validated endpoints, storing the history of our city in a Time Series Database so we can finally visualize the invisible.&lt;/p&gt;
&lt;h1&gt;
  
  
  Chapter 7: The Brain – Deploying Prometheus
&lt;/h1&gt;
&lt;h2&gt;
  
  
  7.1 The Time-Series Database (TSDB)
&lt;/h2&gt;

&lt;p&gt;We have successfully built a nervous system. Across our "City," sensors are firing—reporting CPU temperatures, garbage collection pauses, and API latency. However, these signals are ephemeral. If Node Exporter reports that CPU usage spiked to 100% at 3:00 AM, but no one was watching at that exact second, the information is lost forever.&lt;/p&gt;

&lt;p&gt;We need a memory. We need a system that can ingest these millions of fleeting data points, timestamp them, and store them efficiently for retrieval.&lt;/p&gt;

&lt;p&gt;You might ask: &lt;em&gt;Why not just use Postgres?&lt;/em&gt; We already have a robust Postgres deployment running for SonarQube and GitLab.&lt;/p&gt;

&lt;p&gt;The answer lies in the shape of the data. Operational telemetry is fundamentally different from transactional data.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Transactional Data (Postgres):&lt;/strong&gt; "User A updated their profile." This is low volume, requires strict consistency (ACID), and often involves updating existing rows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time-Series Data (Prometheus):&lt;/strong&gt; "CPU is 40%... now 42%... now 45%." This is massive volume (thousands of writes per second), purely append-only (we never "update" history), and is queried by time ranges ("Show me the trend over the last hour").&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Prometheus is a specialized &lt;strong&gt;Time-Series Database (TSDB)&lt;/strong&gt; designed exactly for this workload. It does not wait for agents to send it data (the "Push" model). Instead, it acts as the "Brain" of our architecture. It maintains a list of targets (the "Map" we built in Chapter 3), and every 15 seconds, it reaches out to every limb of the infrastructure to capture its current state (the "Pull" model).&lt;/p&gt;

&lt;p&gt;This Pull model is crucial for system reliability. If a service goes down, Prometheus knows immediately because the scrape fails. In a Push model, the monitoring system can't easily distinguish between "The service is down" and "The service is just quiet."&lt;/p&gt;

&lt;p&gt;To give our Brain long-term memory, we will mount a Docker volume (&lt;code&gt;prometheus-data&lt;/code&gt;). This ensures that even if we destroy and redeploy the Prometheus container, the history of our city remains intact.&lt;/p&gt;
&lt;h2&gt;
  
  
  7.2 Security by Design (The "Nobody" User)
&lt;/h2&gt;

&lt;p&gt;Deploying Prometheus introduces a specific operational constraint that catches many engineers off guard: &lt;strong&gt;Identity&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In the Docker ecosystem, laziness is common. Most containers default to running as &lt;code&gt;root&lt;/code&gt; (UID 0) inside the container. This makes permission management easy—root can read and write anything—but it is a security nightmare. If a vulnerability is found in the application, the attacker gains root access to the container and potentially the host.&lt;/p&gt;

&lt;p&gt;Prometheus takes the high road. The official image is engineered to run as the &lt;strong&gt;&lt;code&gt;nobody&lt;/code&gt; user (UID 65534)&lt;/strong&gt; by default. This is the standard "Least Privilege" identity on Linux systems. It has no shell, no home directory, and most importantly, it owns nothing.&lt;/p&gt;

&lt;p&gt;This creates a conflict with our deployment strategy.&lt;/p&gt;

&lt;p&gt;We are injecting our "Map" (&lt;code&gt;prometheus.yml&lt;/code&gt;) and our "Shield" (&lt;code&gt;web-config.yml&lt;/code&gt;) by &lt;strong&gt;Bind Mounting&lt;/strong&gt; them from the Host machine into the container.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Host Path:&lt;/strong&gt; &lt;code&gt;~/cicd_stack/prometheus/config/prometheus.yml&lt;/code&gt; (Owned by you, UID 1000).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Container Path:&lt;/strong&gt; &lt;code&gt;/etc/prometheus/prometheus.yml&lt;/code&gt; (Read by Prometheus, UID 65534).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If we simply mounted these files, the Prometheus process would crash with &lt;code&gt;Permission Denied&lt;/code&gt; because the &lt;code&gt;nobody&lt;/code&gt; user cannot read files owned by your personal account (assuming standard strict permissions).&lt;/p&gt;

&lt;p&gt;This explains the "Surgical Strike" we performed back in &lt;strong&gt;Chapter 3&lt;/strong&gt; (&lt;code&gt;01-setup-monitoring.sh&lt;/code&gt;). By running &lt;code&gt;sudo chown -R 65534:65534&lt;/code&gt; on the configuration directory, we pre-aligned the file ownership. We ensured that when the Brain wakes up, it has the permission to read its own memories.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: For the database itself (&lt;code&gt;/prometheus&lt;/code&gt;), we utilize a **Named Volume&lt;/em&gt;* (&lt;code&gt;prometheus-data&lt;/code&gt;). Unlike the configuration files which we manage manually, the database storage is managed entirely by the Docker Engine, which handles the complex permissions required for high-throughput writes automatically.*&lt;/p&gt;
&lt;h2&gt;
  
  
  7.3 Self-Defense (TLS for Prometheus)
&lt;/h2&gt;

&lt;p&gt;We often think of Prometheus as a &lt;strong&gt;Client&lt;/strong&gt;: it reaches out to scrape metrics from other services. But Prometheus is also a &lt;strong&gt;Server&lt;/strong&gt;. It exposes a powerful HTTP API and a Web UI on port 9090.&lt;/p&gt;

&lt;p&gt;This interface is the "Brain's" primary output. It is where human operators write PromQL queries to diagnose outages, and it is where visualization tools like Grafana connect to fetch the data they need to draw charts.&lt;/p&gt;

&lt;p&gt;In a standard "quick start" tutorial, Prometheus runs on plain HTTP. This is acceptable for a hobby project, but in a production environment (and in our "City"), unencrypted HTTP is a vulnerability. It means that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Snooping:&lt;/strong&gt; Anyone on the network can read the metric stream (which often contains sensitive business intelligence like build volumes or user counts).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spoofing:&lt;/strong&gt; A malicious actor could impersonate the Prometheus server and feed false data to your dashboards.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To prevent this, we apply the same &lt;strong&gt;Zero Trust&lt;/strong&gt; standard to the monitoring system that we applied to the application stack. We configure Prometheus to speak &lt;strong&gt;HTTPS&lt;/strong&gt; natively.&lt;/p&gt;

&lt;p&gt;This is handled by the &lt;code&gt;--web.config.file&lt;/code&gt; flag in our deployment script. This flag points to the &lt;code&gt;web-config.yml&lt;/code&gt; file we generated back in Chapter 3, which contains:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;tls_server_config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;cert_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/etc/prometheus/certs/prometheus.crt&lt;/span&gt;
  &lt;span class="na"&gt;key_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/etc/prometheus/certs/prometheus.key&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When Prometheus starts, it will load these certificates and open a secure listener on port 9090.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Payoff: End-to-End Trust&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because we have meticulously managed our Public Key Infrastructure (PKI) throughout this series, we get two massive benefits immediately:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Host Browser:&lt;/strong&gt; When you visit &lt;code&gt;https://prometheus.cicd.local:9090&lt;/code&gt; from your host machine, you will see a green "Secure" padlock. This is because in 0006_cicd_part02_certificate_authority, we imported our custom Root CA into the host's system trust store. Your browser recognizes the signature and trusts the site instantly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Future Connection (Grafana):&lt;/strong&gt; In the upcoming chapters, we will deploy Grafana. Grafana will not talk to Prometheus over &lt;code&gt;http://localhost&lt;/code&gt;. It will connect securely via &lt;code&gt;https://prometheus.cicd.local:9090&lt;/code&gt;. Because we injected the Root CA into the Grafana container during the "Setup" phase (Chapter 3), this internal API connection will be fully encrypted and mutually trusted without any "insecure skip verify" hacks.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We have now secured the Brain. It scrapes securely, and it serves securely.&lt;/p&gt;

&lt;h2&gt;
  
  
  7.4 The Deployment Script (&lt;code&gt;06-deploy-prometheus.sh&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;We now have all the components: the User Identity (&lt;code&gt;nobody&lt;/code&gt;), the Storage Volume (&lt;code&gt;prometheus-data&lt;/code&gt;), the Configuration Map (&lt;code&gt;prometheus.yml&lt;/code&gt;), and the TLS Shield (&lt;code&gt;web-config.yml&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;We bring them together in the deployment script.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Analysis of the Script:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Identity Flag (&lt;code&gt;--user 65534:65534&lt;/code&gt;):&lt;/strong&gt; This forces the container to run strictly as the unprivileged &lt;code&gt;nobody&lt;/code&gt; user. It matches the file ownerships we set on the host config directories.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The "Map" Mount:&lt;/strong&gt; We mount our handcrafted &lt;code&gt;prometheus.yml&lt;/code&gt; (from Chapter 3) to &lt;code&gt;/etc/prometheus/prometheus.yml&lt;/code&gt;. This tells the Brain where the Limbs are.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The "Shield" Mount:&lt;/strong&gt; We mount the &lt;code&gt;web-config.yml&lt;/code&gt; and the &lt;code&gt;certs&lt;/code&gt; directory. This enables the HTTPS listener.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The "Memories" Mount:&lt;/strong&gt; We map the named volume &lt;code&gt;prometheus-data&lt;/code&gt; to &lt;code&gt;/prometheus&lt;/code&gt;. This is where the Time Series Database (TSDB) actually lives.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Source Code: &lt;code&gt;06-deploy-prometheus.sh&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Create this file at &lt;code&gt;~/Documents/FromFirstPrinciples/articles/0013_cicd_part09_prometheus_grafana/06-deploy-prometheus.sh&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;#           06-deploy-prometheus.sh&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  Deploys "The Brain".&lt;/span&gt;
&lt;span class="c"&gt;#  - Network: cicd-net&lt;/span&gt;
&lt;span class="c"&gt;#  - Identity: prometheus.cicd.local (Self-scraping)&lt;/span&gt;
&lt;span class="c"&gt;#  - Security: TLS on port 9090 via web-config.yml&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🚀 Deploying Prometheus (The Brain)..."&lt;/span&gt;

&lt;span class="c"&gt;# --- 1. Load Paths ---&lt;/span&gt;
&lt;span class="nv"&gt;PROMETHEUS_BASE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/cicd_stack/prometheus"&lt;/span&gt;

&lt;span class="c"&gt;# --- 2. Cleanup Old Container ---&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;prometheus&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;docker &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; prometheus
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# --- 3. Deploy ---&lt;/span&gt;
&lt;span class="c"&gt;# Note: We run as UID 65534 ('nobody') which owns the mounted volumes.&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; prometheus &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--restart&lt;/span&gt; always &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network&lt;/span&gt; cicd-net &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--hostname&lt;/span&gt; prometheus.cicd.local &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--publish&lt;/span&gt; 127.0.0.1:9090:9090 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--user&lt;/span&gt; 65534:65534 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROMETHEUS_BASE&lt;/span&gt;&lt;span class="s2"&gt;/config/prometheus.yml"&lt;/span&gt;:/etc/prometheus/prometheus.yml:ro &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROMETHEUS_BASE&lt;/span&gt;&lt;span class="s2"&gt;/config/web-config.yml"&lt;/span&gt;:/etc/prometheus/web-config.yml:ro &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROMETHEUS_BASE&lt;/span&gt;&lt;span class="s2"&gt;/config/certs"&lt;/span&gt;:/etc/prometheus/certs:ro &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; prometheus-data:/prometheus &lt;span class="se"&gt;\&lt;/span&gt;
  prom/prometheus:latest &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--config&lt;/span&gt;.file&lt;span class="o"&gt;=&lt;/span&gt;/etc/prometheus/prometheus.yml &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--storage&lt;/span&gt;.tsdb.path&lt;span class="o"&gt;=&lt;/span&gt;/prometheus &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--web&lt;/span&gt;.config.file&lt;span class="o"&gt;=&lt;/span&gt;/etc/prometheus/web-config.yml &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--web&lt;/span&gt;.external-url&lt;span class="o"&gt;=&lt;/span&gt;https://prometheus.cicd.local:9090

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✅ Prometheus Deployed."&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   URL: https://prometheus.cicd.local:9090"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Note: Browser will warn about CA unless installed."&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  7.5 First Breath (Target Verification)
&lt;/h2&gt;

&lt;p&gt;It is time to turn on the machine.&lt;/p&gt;

&lt;p&gt;Run the deployment script from your host:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x 06-deploy-prometheus.sh
./06-deploy-prometheus.sh

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the container starts successfully (check &lt;code&gt;docker ps&lt;/code&gt;), we proceed to the "Moment of Truth."&lt;/p&gt;

&lt;p&gt;Open your web browser on the host and navigate to:&lt;br&gt;
&lt;strong&gt;&lt;code&gt;https://prometheus.cicd.local:9090/targets&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: Because you installed the Root CA in the CA article you should see a secure padlock icon in the address bar. The browser trusts this internal site just as it trusts a public bank website.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  The Goal: 9/9 UP
&lt;/h3&gt;

&lt;p&gt;You will be greeted by the Prometheus Targets interface. This is the real-time status dashboard of the collector.&lt;/p&gt;

&lt;p&gt;You are looking for a table listing our 9 defined targets (Node Exporter, cAdvisor, Jenkins, GitLab, etc.).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;State:&lt;/strong&gt; Every single target should be marked &lt;strong&gt;UP&lt;/strong&gt; in green.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error:&lt;/strong&gt; There should be no red text or "Context Deadline Exceeded" errors.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This screen is the visual confirmation of everything we worked for. It proves that the Brain can reach the Limbs, authenticate, and parse the data.&lt;/p&gt;
&lt;h3&gt;
  
  
  The First Query
&lt;/h3&gt;

&lt;p&gt;To prove that data is actually being recorded to the disk, click on &lt;strong&gt;Query&lt;/strong&gt; in the top navigation bar.&lt;/p&gt;

&lt;p&gt;In the expression bar, type the simplest query possible:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;up

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Click &lt;strong&gt;Execute&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You should see a list of results with a value of &lt;code&gt;1&lt;/code&gt;.&lt;br&gt;
In PromQL, &lt;code&gt;up&lt;/code&gt; is a synthetic metric.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;1&lt;/code&gt; = The scrape was successful.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;0&lt;/code&gt; = The scrape failed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you switch to the &lt;strong&gt;Graph&lt;/strong&gt; tab (next to Table), you will see a flat line at &lt;code&gt;1&lt;/code&gt;. This is the heartbeat of your city. As long as that line stays at 1, your infrastructure is alive.&lt;/p&gt;

&lt;p&gt;The Brain is active. The data is flowing. We are now ready to give this data a face. In the next chapter, we will deploy &lt;strong&gt;Grafana&lt;/strong&gt; to transform these raw numbers into beautiful, actionable insights.&lt;/p&gt;
&lt;h1&gt;
  
  
  Chapter 8: The Face (Part 1) – Automated Provisioning
&lt;/h1&gt;
&lt;h2&gt;
  
  
  8.1 The "Infrastructure as Code" Philosophy
&lt;/h2&gt;

&lt;p&gt;We have built the "Brain" (Prometheus) and verified the "Nervous System" (The Exporters). Now we must build the "Face" of our city: the &lt;strong&gt;Grafana&lt;/strong&gt; visualization layer.&lt;/p&gt;

&lt;p&gt;In a traditional setup, an engineer would launch Grafana, log in to the web interface, manually click "Add Data Source," type in &lt;code&gt;http://prometheus:9090&lt;/code&gt;, and then manually upload JSON files one by one to create dashboards.&lt;/p&gt;

&lt;p&gt;This manual approach is &lt;strong&gt;fragile&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It is not reproducible:&lt;/strong&gt; If you destroy the Grafana container to upgrade it, you lose your configurations unless you have perfectly managed your persistent volumes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It is tedious:&lt;/strong&gt; We have nine distinct services to visualize. Configuring them by hand is prone to human error (e.g., typing &lt;code&gt;https&lt;/code&gt; instead of &lt;code&gt;http&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It violates our principles:&lt;/strong&gt; We are building a "City from Code." The state of our monitoring system should be defined in a script, not in the click-history of a web browser.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To solve this, we utilize Grafana's &lt;strong&gt;Provisioning&lt;/strong&gt; system.&lt;/p&gt;

&lt;p&gt;Grafana allows us to define "Providers" via configuration files (YAML) placed in specific directories. When the container starts, it scans these directories.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Datasources:&lt;/strong&gt; It automatically connects to Prometheus using the credentials and certificates we define in code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dashboards:&lt;/strong&gt; It automatically loads visualization definitions (JSON files) from a designated folder on the disk.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This approach effectively turns our monitoring system into &lt;strong&gt;Stateless Code&lt;/strong&gt;. We could burn down the entire monitoring stack, run our deployment scripts, and have the exact same beautiful dashboards appear within seconds, without ever touching a mouse.&lt;/p&gt;

&lt;p&gt;In this chapter, we will prepare these provisioning assets on our host machine so they are ready to be mounted into the Grafana container in Chapter 9.&lt;/p&gt;
&lt;h2&gt;
  
  
  8.2 The Provider Configuration (&lt;code&gt;dashboards.yaml&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;The first step in automated provisioning is to tell Grafana &lt;em&gt;where&lt;/em&gt; to look for our visualizations. We do this by creating a provider configuration file.&lt;/p&gt;

&lt;p&gt;This YAML file acts as a map. It instructs Grafana to watch a specific directory on the filesystem (&lt;code&gt;/etc/grafana/provisioning/dashboards/json&lt;/code&gt;). Any JSON file dropped into this folder will be instantly ingested and rendered as a dashboard in the UI.&lt;/p&gt;

&lt;p&gt;We will automate the creation of this file in our provisioning script &lt;code&gt;07-provision-dashboards.sh&lt;/code&gt;, but it is important to understand what we are writing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;

&lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Default'&lt;/span&gt;
    &lt;span class="na"&gt;orgId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
    &lt;span class="na"&gt;folder&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;file&lt;/span&gt;
    &lt;span class="na"&gt;disableDeletion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="na"&gt;updateIntervalSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
    &lt;span class="na"&gt;allowUiUpdates&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/etc/grafana/provisioning/dashboards/json&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key Configuration Details:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;path&lt;/code&gt;&lt;/strong&gt;: This is the critical link. It points to a location &lt;em&gt;inside the container&lt;/em&gt;. In Chapter 9, we will mount our host directory &lt;code&gt;~/cicd_stack/grafana/provisioning/dashboards/json&lt;/code&gt; to this container path, bridging the gap between our code repo and the running application.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;updateIntervalSeconds: 10&lt;/code&gt;&lt;/strong&gt;: This is a powerful feature for development. It tells Grafana to re-scan the folder every 10 seconds. If we edit a JSON file on our host machine, Grafana detects the change and updates the dashboard live, without requiring a container restart.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;allowUiUpdates: true&lt;/code&gt;&lt;/strong&gt;: This permits us to make tweaks in the UI if necessary (though our goal is to keep everything in code).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By defining this provider, we prepare the "empty vessel." Now we need to fill it with actual visualization definitions.&lt;/p&gt;

&lt;h2&gt;
  
  
  8.3 The Dashboard Portfolio
&lt;/h2&gt;

&lt;p&gt;We will deploy a balanced portfolio of visualizations, split into two distinct layers: the &lt;strong&gt;Infrastructure Layer&lt;/strong&gt; (Standard) and the &lt;strong&gt;Application Layer&lt;/strong&gt; (Bespoke).&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The Infrastructure Layer (Community Standards)
&lt;/h3&gt;

&lt;p&gt;For standard tools like Docker and Linux, there is no need to reinvent the wheel. The open-source community has already built excellent dashboards. In our provisioning script, we will automate the downloading of these JSON definitions directly from Grafana.com:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Node Exporter Full (ID: 1860):&lt;/strong&gt; A comprehensive view of the Host Machine. It tracks CPU core usage, RAM saturation, Disk I/O latency, and Network bandwidth. If the host is slow, this dashboard proves it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;cAdvisor Containers (ID: 14282):&lt;/strong&gt; A granular view of the Docker Engine. It allows us to drill down into specific containers (e.g., "Why is &lt;code&gt;jenkins&lt;/code&gt; using 4GB of RAM?").&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Elasticsearch (ID: 2322):&lt;/strong&gt; A health check for our logging cluster. It visualizes JVM heap usage, garbage collection times, and index health statuses.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Go Processes (ID: 6671):&lt;/strong&gt; Since many of our components (Prometheus, Node Exporter, Mattermost) are written in Go, this dashboard gives us deep insight into their runtime behavior, specifically Goroutine counts and Garbage Collection pauses.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. The Application Layer (Bespoke Injection)
&lt;/h3&gt;

&lt;p&gt;For our core CI/CD applications—GitLab, Jenkins, SonarQube, Artifactory, and Mattermost—standard community dashboards often fail. They might expect different job names (e.g., &lt;code&gt;job="kubernetes-pods"&lt;/code&gt; instead of our &lt;code&gt;job="jenkins"&lt;/code&gt;), or they might not track the specific custom metrics we verified in Chapter 6.&lt;/p&gt;

&lt;p&gt;To ensure accurate visibility, we will &lt;strong&gt;inject&lt;/strong&gt; our own custom-tailored JSON definitions. These dashboards are designed to map perfectly to the specific metrics we are emitting:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Jenkins:&lt;/strong&gt; Visualizes the &lt;code&gt;jenkins_queue_size_value&lt;/code&gt; to detect build backlogs and &lt;code&gt;jenkins_runs_failure_total&lt;/code&gt; to track pipeline stability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SonarQube:&lt;/strong&gt; Tracks the critical &lt;code&gt;sonarqube_health_web_status&lt;/code&gt; and the depth of the Compute Engine queue (&lt;code&gt;pending_tasks&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitLab:&lt;/strong&gt; Monitors the Ruby on Rails runtime, specifically &lt;code&gt;puma_active_connections&lt;/code&gt; and &lt;code&gt;http_requests_total&lt;/code&gt; to measure traffic load.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Artifactory:&lt;/strong&gt; Visualizes storage consumption (&lt;code&gt;jfrt_stg_summary_repo_size_bytes&lt;/code&gt;) so we know when our artifact warehouse is filling up.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mattermost:&lt;/strong&gt; Tracks real-time user activity via &lt;code&gt;mattermost_active_users&lt;/code&gt; and WebSocket connections.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By combining downloaded standards with injected customs, we create a complete observability suite that covers everything from the silicon to the software.&lt;/p&gt;

&lt;h2&gt;
  
  
  8.4 The Provisioning Script (&lt;code&gt;07-provision-dashboards.sh&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;We automate the entire setup process with &lt;code&gt;07-provision-dashboards.sh&lt;/code&gt;. This script is the "Construction Crew" for our visualization layer. It performs four critical tasks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Preparation:&lt;/strong&gt; It creates the directory structure and temporarily takes ownership of it (since the directory is normally owned by the Grafana user, UID 472).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configuration:&lt;/strong&gt; It generates the &lt;code&gt;dashboards.yaml&lt;/code&gt; provider file we discussed in Section 8.2.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Downloading:&lt;/strong&gt; It fetches the "Infrastructure Layer" dashboards directly from Grafana's API. Crucially, it uses &lt;code&gt;sed&lt;/code&gt; to patch them on the fly, replacing variable datasource names (like &lt;code&gt;${DS_PROMETHEUS}&lt;/code&gt;) with our hardcoded value &lt;code&gt;"Prometheus"&lt;/code&gt; to prevent "Datasource not found" errors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Injection:&lt;/strong&gt; It detects our "Application Layer" custom JSON files in the current directory and copies them into the provisioning folder.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The Source Code: &lt;code&gt;07-provision-dashboards.sh&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Create this file at &lt;code&gt;~/Documents/FromFirstPrinciples/articles/0013_cicd_part09_prometheus_grafana/07-provision-dashboards.sh&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;#           07-provision-dashboards.sh&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  Configures Grafana Dashboard Provisioning.&lt;/span&gt;
&lt;span class="c"&gt;#  1. Temporarily takes ownership of config dir.&lt;/span&gt;
&lt;span class="c"&gt;#  2. Downloads Standard Dashboards.&lt;/span&gt;
&lt;span class="c"&gt;#  3. Injects LOCAL Custom Dashboards (SonarQube, etc).&lt;/span&gt;
&lt;span class="c"&gt;#  4. Restores ownership to Grafana (UID 472).&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🎨 Provisioning Grafana Dashboards..."&lt;/span&gt;

&lt;span class="c"&gt;# --- 1. Paths ---&lt;/span&gt;
&lt;span class="nv"&gt;GRAFANA_BASE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/cicd_stack/grafana"&lt;/span&gt;
&lt;span class="nv"&gt;DASH_CONF_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GRAFANA_BASE&lt;/span&gt;&lt;span class="s2"&gt;/provisioning/dashboards"&lt;/span&gt;
&lt;span class="nv"&gt;JSON_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DASH_CONF_DIR&lt;/span&gt;&lt;span class="s2"&gt;/json"&lt;/span&gt;

&lt;span class="c"&gt;# --- 2. Prepare Permissions ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   🔓 Temporarily unlocking permissions..."&lt;/span&gt;
&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JSON_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$USER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;:&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$USER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GRAFANA_BASE&lt;/span&gt;&lt;span class="s2"&gt;/provisioning"&lt;/span&gt;

&lt;span class="c"&gt;# --- 3. Create Provider Config ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   📝 Writing Provider Config..."&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt; "&lt;/span&gt;&lt;span class="nv"&gt;$DASH_CONF_DIR&lt;/span&gt;&lt;span class="sh"&gt;/dashboards.yaml"
apiVersion: 1

providers:
  - name: 'Default'
    orgId: 1
    folder: ''
    type: file
    disableDeletion: false
    updateIntervalSeconds: 10
    allowUiUpdates: true
    options:
      path: /etc/grafana/provisioning/dashboards/json
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# --- 4. Download Standard Dashboards ---&lt;/span&gt;
download_dash&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JSON_DIR&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;NAME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.json"&lt;/span&gt;

    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   ⬇️  Downloading &lt;/span&gt;&lt;span class="nv"&gt;$NAME&lt;/span&gt;&lt;span class="s2"&gt; (ID: &lt;/span&gt;&lt;span class="nv"&gt;$ID&lt;/span&gt;&lt;span class="s2"&gt;)..."&lt;/span&gt;
    curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"https://grafana.com/api/dashboards/&lt;/span&gt;&lt;span class="nv"&gt;$ID&lt;/span&gt;&lt;span class="s2"&gt;/revisions/latest/download"&lt;/span&gt; | &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'s/\$\{DS_PROMETHEUS[^}]*\}/Prometheus/g'&lt;/span&gt; | &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'s/"datasource":.*"\${.*}"/"datasource": "Prometheus"/g'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Infrastructure Layer ---"&lt;/span&gt;
download_dash &lt;span class="s2"&gt;"1860"&lt;/span&gt; &lt;span class="s2"&gt;"node-exporter-full"&lt;/span&gt;
download_dash &lt;span class="s2"&gt;"14282"&lt;/span&gt; &lt;span class="s2"&gt;"cadvisor-containers"&lt;/span&gt;
download_dash &lt;span class="s2"&gt;"2322"&lt;/span&gt; &lt;span class="s2"&gt;"elasticsearch"&lt;/span&gt;
download_dash &lt;span class="s2"&gt;"19105"&lt;/span&gt; &lt;span class="s2"&gt;"prometheus-modern"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Application Layer ---"&lt;/span&gt;
download_dash &lt;span class="s2"&gt;"6671"&lt;/span&gt; &lt;span class="s2"&gt;"go-processes"&lt;/span&gt;

&lt;span class="c"&gt;# --- 5. Inject Local Custom Dashboard ---&lt;/span&gt;
&lt;span class="c"&gt;# The script checks for local files and copies them to the provisioning dir&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"sonarqube_dashboard.json"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   📥 Injecting local 'sonarqube_dashboard.json'..."&lt;/span&gt;
    &lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"sonarqube_dashboard.json"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JSON_DIR&lt;/span&gt;&lt;span class="s2"&gt;/sonarqube-native.json"&lt;/span&gt;
&lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   ⚠️  WARNING: 'sonarqube_dashboard.json' not found."&lt;/span&gt;
&lt;span class="k"&gt;fi

if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"artifactory_dashboard.json"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   📥 Injecting local 'artifactory_dashboard.json'..."&lt;/span&gt;
    &lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"artifactory_dashboard.json"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JSON_DIR&lt;/span&gt;&lt;span class="s2"&gt;/artifactory.json"&lt;/span&gt;
&lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   ⚠️  WARNING: 'artifactory_dashboard.json' not found."&lt;/span&gt;
&lt;span class="k"&gt;fi

if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"gitlab_dashboard.json"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   📥 Injecting local 'gitlab_dashboard.json'..."&lt;/span&gt;
    &lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"gitlab_dashboard.json"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JSON_DIR&lt;/span&gt;&lt;span class="s2"&gt;/gitlab.json"&lt;/span&gt;
&lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   ⚠️  WARNING: 'gitlab_dashboard.json' not found."&lt;/span&gt;
&lt;span class="k"&gt;fi

if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"jenkins_dashboard.json"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   📥 Injecting local 'jenkins_dashboard.json'..."&lt;/span&gt;
    &lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"jenkins_dashboard.json"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JSON_DIR&lt;/span&gt;&lt;span class="s2"&gt;/jenkins.json"&lt;/span&gt;
&lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   ⚠️  WARNING: 'jenkins_dashboard.json' not found."&lt;/span&gt;
&lt;span class="k"&gt;fi

if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"mattermost_dashboard.json"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   📥 Injecting local 'mattermost_dashboard.json'..."&lt;/span&gt;
    &lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"mattermost_dashboard.json"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JSON_DIR&lt;/span&gt;&lt;span class="s2"&gt;/mattermost.json"&lt;/span&gt;
&lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   ⚠️  WARNING: 'mattermost_dashboard.json' not found."&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# --- 6. Restore Permissions ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   🔒 Restoring permissions for Grafana (UID 472)..."&lt;/span&gt;
&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; 472:472 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GRAFANA_BASE&lt;/span&gt;&lt;span class="s2"&gt;/provisioning"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✅ All Dashboards Provisioned."&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  8.5 Staging the Custom Assets
&lt;/h2&gt;

&lt;p&gt;Before running the provisioning script, we must ensure our "Application Layer" assets are in place. The script expects five specific JSON files to exist in your current directory. It will copy these into the container's volume.&lt;/p&gt;

&lt;p&gt;Ensure you have the following files in &lt;code&gt;~/Documents/FromFirstPrinciples/articles/0013_cicd_part09_prometheus_grafana/&lt;/code&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;sonarqube_dashboard.json&lt;/code&gt;&lt;/strong&gt; (Maps to &lt;code&gt;sonarqube-native.json&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;artifactory_dashboard.json&lt;/code&gt;&lt;/strong&gt; (Maps to &lt;code&gt;artifactory.json&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;gitlab_dashboard.json&lt;/code&gt;&lt;/strong&gt; (Maps to &lt;code&gt;gitlab.json&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;jenkins_dashboard.json&lt;/code&gt;&lt;/strong&gt; (Maps to &lt;code&gt;jenkins.json&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;mattermost_dashboard.json&lt;/code&gt;&lt;/strong&gt; (Maps to &lt;code&gt;mattermost.json&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;Note: These files contain the thousands of lines of JSON definition required to draw the specific charts we verified in Chapter 6. You can download them from the repository accompanying this article series.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Once these files are staged, execute the provisioning script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x 07-provision-dashboards.sh
./07-provision-dashboards.sh

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What to Watch For:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You should see &lt;code&gt;Downloading node-exporter-full...&lt;/code&gt; as it hits the internet.&lt;/li&gt;
&lt;li&gt;You should see &lt;code&gt;Injecting local 'sonarqube_dashboard.json'...&lt;/code&gt; as it detects your custom files.&lt;/li&gt;
&lt;li&gt;The script should finish with &lt;code&gt;✅ All Dashboards Provisioned.&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you see &lt;code&gt;⚠️ WARNING: ... not found&lt;/code&gt;, it means you forgot to save the JSON file in the same folder as the script.&lt;/p&gt;

&lt;p&gt;With the provisioning complete, the visualization layer is essentially "pre-loaded" on the host disk. All that remains is to launch the Grafana container and let it mount these configurations. We will do this in the next chapter.&lt;/p&gt;

&lt;h1&gt;
  
  
  Chapter 9: The Face (Part 2) – Launching Grafana
&lt;/h1&gt;

&lt;h2&gt;
  
  
  9.1 The Visualization Layer
&lt;/h2&gt;

&lt;p&gt;We have successfully built the "Brain" of our city. Prometheus is alive, scraping metrics every 15 seconds, and storing them in its time-series database. However, a brain without a face is difficult to interact with. While Prometheus does have a built-in web interface, it is designed for debugging queries, not for operational visibility. It gives you raw data, not insights.&lt;/p&gt;

&lt;p&gt;To solve this, we introduce &lt;strong&gt;Grafana&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Grafana is the "Face" of our infrastructure. It connects to the Prometheus database, retrieves the raw time-series data, and transforms it into rich, interactive visualizations—graphs, gauges, heatmaps, and tables. If Prometheus tells you &lt;em&gt;what&lt;/em&gt; happened at a specific microsecond, Grafana tells you &lt;em&gt;why&lt;/em&gt; it matters by visualizing the trend over time.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Shift to Statelessness
&lt;/h3&gt;

&lt;p&gt;In many tutorials, Grafana is deployed as a "Pet." It uses an embedded SQLite database stored in a Docker volume. If you want to back up your users or sessions, you have to back up that specific file. If the volume corrupts, you lose your accounts.&lt;/p&gt;

&lt;p&gt;We are building a &lt;strong&gt;Production-Grade City&lt;/strong&gt;, so we will take a more robust approach. We will treat the Grafana container as &lt;strong&gt;Stateless&lt;/strong&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;State (Users &amp;amp; Sessions):&lt;/strong&gt; Instead of using a local SQLite file, we will connect Grafana to the &lt;strong&gt;PostgreSQL&lt;/strong&gt; database we deployed in the Artifactory article. This creates a centralized "Source of Truth" for identity and session data that is independent of the visualization container.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configuration (Dashboards &amp;amp; Settings):&lt;/strong&gt; Instead of manually editing settings in the UI, we will inject our configuration and dashboard definitions from the host file system.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Assets (Plugins):&lt;/strong&gt; We will use a standard Docker volume strictly for installed plugins and temporary files.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This architecture makes our Grafana container almost entirely disposable. If we destroy it and redeploy it (which we will do frequently in a CI/CD context), it simply reconnects to the database, re-reads the dashboard files, and picks up exactly where it left off.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Identity Constraint (UID 472)
&lt;/h3&gt;

&lt;p&gt;Just like Prometheus, Grafana adheres to strict security practices. The official Docker image runs as a non-privileged user named &lt;code&gt;grafana&lt;/code&gt;, which corresponds to &lt;strong&gt;UID 472&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This identity is critical for our storage strategy.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Config Maps:&lt;/strong&gt; We will bind-mount our configuration files (&lt;code&gt;grafana.ini&lt;/code&gt;, &lt;code&gt;provisioning/&lt;/code&gt;) as &lt;strong&gt;Read-Only&lt;/strong&gt;. Since standard Linux files are world-readable, the &lt;code&gt;grafana&lt;/code&gt; user can read them but cannot change them. This enforces &lt;strong&gt;Immutability&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Plugin Volume:&lt;/strong&gt; We will use a Docker Named Volume (&lt;code&gt;grafana-data&lt;/code&gt;) for the &lt;code&gt;/var/lib/grafana&lt;/code&gt; directory. Docker automatically manages the permissions of named volumes, ensuring UID 472 can write to it without us having to manually &lt;code&gt;chown&lt;/code&gt; host directories.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We have the architecture. Now, let's configure the connection to the database.&lt;/p&gt;

&lt;h2&gt;
  
  
  9.2 Database Configuration (Environment Overrides)
&lt;/h2&gt;

&lt;p&gt;Traditionally, configuring software involves editing a large file like &lt;code&gt;grafana.ini&lt;/code&gt;. While we did generate a baseline configuration in Chapter 3, editing it now to add database passwords would violate our security principles. It would force us to bake cleartext secrets into a file on the disk.&lt;/p&gt;

&lt;p&gt;Instead, we will use the &lt;strong&gt;12-Factor App&lt;/strong&gt; methodology.&lt;/p&gt;

&lt;p&gt;Grafana allows us to override &lt;em&gt;any&lt;/em&gt; setting in its configuration file using &lt;strong&gt;Environment Variables&lt;/strong&gt;. The syntax is predictable:&lt;br&gt;
&lt;code&gt;GF_&amp;lt;SectionName&amp;gt;_&amp;lt;KeyName&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We will use this mechanism to inject our PostgreSQL connection details at runtime. This keeps our static configuration files clean and our secrets strictly in memory (or passed securely by the container runtime).&lt;/p&gt;

&lt;p&gt;Here are the specific overrides we will apply:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;GF_DATABASE_TYPE=postgres&lt;/code&gt;&lt;/strong&gt;: Explicitly tells Grafana to abandon its default SQLite engine.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;GF_DATABASE_HOST=postgres.cicd.local:5432&lt;/code&gt;&lt;/strong&gt;: Points to the shared database cluster we built previously. Note that we use the internal Docker DNS name.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;GF_DATABASE_NAME=grafana&lt;/code&gt;&lt;/strong&gt;: The specific database we created for this service.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;GF_DATABASE_USER=grafana&lt;/code&gt;&lt;/strong&gt;: The dedicated user account.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;GF_DATABASE_SSL_MODE=require&lt;/code&gt;&lt;/strong&gt;: This is &lt;strong&gt;critical&lt;/strong&gt;. Our PostgreSQL cluster (configured in the Artifactory article) is set to reject any non-SSL connections from the network. If we omit this, Grafana will attempt a plain connection, and Postgres will immediately terminate it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By passing these variables, we turn a generic Grafana container into a specific, stateful node in our infrastructure.&lt;/p&gt;
&lt;h2&gt;
  
  
  9.3 The Deployment Script (&lt;code&gt;08-deploy-grafana.sh&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;We now bring all these requirements together in our deployment script. This script acts as the bridge between our secure "City" infrastructure (Certificates, Secrets, Database) and the Grafana container.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key features of this script:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Secret Loading:&lt;/strong&gt; It sources &lt;code&gt;~/cicd_stack/cicd.env&lt;/code&gt; to retrieve the &lt;code&gt;GRAFANA_DB_PASSWORD&lt;/code&gt;. This allows us to pass the database credentials securely without hardcoding them in a visible config file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Environment Injection:&lt;/strong&gt; It uses &lt;code&gt;--env-file "$GRAFANA_ENV"&lt;/code&gt; to load the base Grafana configuration variables we set up in Chapter 3.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database Connection:&lt;/strong&gt; It explicitly sets the &lt;code&gt;GF_DATABASE_*&lt;/code&gt; variables in the &lt;code&gt;docker run&lt;/code&gt; command to force the connection to our shared PostgreSQL cluster, ensuring the container remains stateless.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSL Enforcement:&lt;/strong&gt; It sets &lt;code&gt;GF_DATABASE_SSL_MODE=require&lt;/code&gt; to comply with the strict security policy we configured in &lt;code&gt;pg_hba.conf&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  The Source Code: &lt;code&gt;08-deploy-grafana.sh&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Create this file at &lt;code&gt;~/Documents/FromFirstPrinciples/articles/0013_cicd_part09_prometheus_grafana/08-deploy-grafana.sh&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;#           08-deploy-grafana.sh&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  Deploys "The Face" (Grafana).&lt;/span&gt;
&lt;span class="c"&gt;#  - Network: cicd-net&lt;/span&gt;
&lt;span class="c"&gt;#  - Identity: grafana.cicd.local&lt;/span&gt;
&lt;span class="c"&gt;#  - Security: HTTPS (Port 3000) + UID 472 (Grafana User)&lt;/span&gt;
&lt;span class="c"&gt;#  - Backend: Connects to postgres.cicd.local (Stateless Container)&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="c"&gt;# --- 1. Load Secrets ---&lt;/span&gt;
&lt;span class="nv"&gt;ENV_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/cicd_stack/cicd.env"&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"❌ ERROR: cicd.env not found."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🚀 Deploying Grafana (The Face)..."&lt;/span&gt;

&lt;span class="c"&gt;# --- 2. Define Paths ---&lt;/span&gt;
&lt;span class="nv"&gt;GRAFANA_BASE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/cicd_stack/grafana"&lt;/span&gt;
&lt;span class="nv"&gt;GRAFANA_ENV&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GRAFANA_BASE&lt;/span&gt;&lt;span class="s2"&gt;/grafana.env"&lt;/span&gt;

&lt;span class="c"&gt;# --- 3. Permission Fix (Environment File) ---&lt;/span&gt;
&lt;span class="c"&gt;# The Docker CLI needs to read this file to inject variables.&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GRAFANA_ENV&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   🔓 Unlocking env file permissions..."&lt;/span&gt;
    &lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$USER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;:&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$USER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GRAFANA_ENV&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;sudo chmod &lt;/span&gt;640 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GRAFANA_ENV&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# --- 4. Cleanup Old Container ---&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;grafana&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   ♻️  Removing existing container..."&lt;/span&gt;
    docker &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; grafana
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# --- 5. Deploy ---&lt;/span&gt;
&lt;span class="c"&gt;# We inject the Postgres config via GF_DATABASE_* variables.&lt;/span&gt;
&lt;span class="c"&gt;# We explicitly set SSL_MODE=require because our Postgres acts as a strict CA authority.&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; grafana &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--restart&lt;/span&gt; always &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network&lt;/span&gt; cicd-net &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--hostname&lt;/span&gt; grafana.cicd.local &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--publish&lt;/span&gt; 127.0.0.1:3000:3000 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--user&lt;/span&gt; 472:472 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--env-file&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GRAFANA_ENV&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;GF_DATABASE_TYPE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;postgres &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;GF_DATABASE_HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;postgres.cicd.local:5432 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;GF_DATABASE_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;grafana &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;GF_DATABASE_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;grafana &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;GF_DATABASE_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GRAFANA_DB_PASSWORD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;GF_DATABASE_SSL_MODE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;require &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GRAFANA_BASE&lt;/span&gt;&lt;span class="s2"&gt;/config/grafana.ini"&lt;/span&gt;:/etc/grafana/grafana.ini:ro &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GRAFANA_BASE&lt;/span&gt;&lt;span class="s2"&gt;/config/certs"&lt;/span&gt;:/etc/grafana/certs:ro &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GRAFANA_BASE&lt;/span&gt;&lt;span class="s2"&gt;/provisioning"&lt;/span&gt;:/etc/grafana/provisioning:ro &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; grafana-data:/var/lib/grafana &lt;span class="se"&gt;\&lt;/span&gt;
  grafana/grafana:latest

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✅ Grafana Deployed."&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"---------------------------------------------------"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   URL:      https://grafana.cicd.local:3000"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   User:     admin"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Password: (Run the command below to see it)"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   grep GRAFANA_ADMIN_PASSWORD ~/cicd_stack/cicd.env"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"---------------------------------------------------"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  9.4 First Contact (Verification)
&lt;/h2&gt;

&lt;p&gt;It is time to turn on the face of our city.&lt;/p&gt;

&lt;p&gt;Run the deployment script from your host:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x 08-deploy-grafana.sh
./08-deploy-grafana.sh

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait about 15-30 seconds for the container to start and for the database migrations to complete. You can verify it is healthy with &lt;code&gt;docker ps&lt;/code&gt;. If you want to confirm the database connection immediately, check the logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker logs grafana | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"Connecting to DB"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;You should see a message confirming the connection to &lt;code&gt;postgres&lt;/code&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Moment of Truth
&lt;/h3&gt;

&lt;p&gt;Open your web browser on the host and navigate to:&lt;br&gt;
&lt;strong&gt;&lt;code&gt;https://grafana.cicd.local:3000&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Secure Lock:&lt;/strong&gt; You should see the green padlock immediately. This confirms our Root CA trust chain is working for this new service.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Login:&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User:&lt;/strong&gt; &lt;code&gt;admin&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Password:&lt;/strong&gt; Retrieve it from your environment file:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep &lt;/span&gt;GRAFANA_ADMIN_PASSWORD ~/cicd_stack/cicd.env

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Verification 1: The Stateless Backend
&lt;/h3&gt;

&lt;p&gt;We need to prove that we are actually using the shared PostgreSQL cluster and not a hidden SQLite file.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In the Grafana sidebar, locate the &lt;strong&gt;Administration&lt;/strong&gt; section (usually a gear or shield icon at the bottom).&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Settings&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;In the search bar or list, look for the &lt;strong&gt;&lt;code&gt;[database]&lt;/code&gt;&lt;/strong&gt; section.&lt;/li&gt;
&lt;li&gt;Verify the following line:&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;type:&lt;/strong&gt; &lt;code&gt;postgres&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;host:&lt;/strong&gt; &lt;code&gt;postgres.cicd.local:5432&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If this says &lt;code&gt;sqlite3&lt;/code&gt;, the environment variable injection failed. If it says &lt;code&gt;postgres&lt;/code&gt;, you have successfully deployed a stateless application.&lt;/p&gt;

&lt;h3&gt;
  
  
  Verification 2: The Provisioned Dashboards
&lt;/h3&gt;

&lt;p&gt;We need to prove that our "Infrastructure as Code" provisioning worked.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click &lt;strong&gt;Dashboards&lt;/strong&gt; in the left sidebar.&lt;/li&gt;
&lt;li&gt;You should see a populated list of dashboards ready to go. Unlike a manual installation which starts empty, your screen will be filled with the visualizations we defined in code:&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Artifactory Native Metrics&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Cadvisor exporter&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;ElasticSearch&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;GitLab Omnibus Complete&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Jenkins: Performance&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Node Exporter Full&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;SonarQube Native Metrics&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Click on &lt;strong&gt;"Node Exporter Full"&lt;/strong&gt;.&lt;br&gt;
You should immediately see live graphs of your host's CPU, Memory, and Disk usage.&lt;/p&gt;

&lt;p&gt;Click on &lt;strong&gt;"Jenkins: Performance"&lt;/strong&gt;.&lt;br&gt;
If your Jenkins container is running, you will see the build queue status and executor counts populated.&lt;/p&gt;

&lt;p&gt;The face is active. The system is observing itself. We have achieved Full Stack Visibility.&lt;/p&gt;

&lt;h1&gt;
  
  
  Chapter 10: Conclusion – Full Stack Visibility
&lt;/h1&gt;

&lt;h2&gt;
  
  
  10.1 The Infrastructure Layer (The Streets &amp;amp; Foundations)
&lt;/h2&gt;

&lt;p&gt;We begin our tour at the foundation of our city. Just as a city planner monitors the power grid and road network, we must monitor the physical resources that power our applications. We use two key dashboards for this: &lt;strong&gt;Node Exporter Full&lt;/strong&gt; and &lt;strong&gt;cAdvisor&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Node Exporter Full (The Host)
&lt;/h3&gt;

&lt;p&gt;This dashboard is your "Check Engine" light. It tells you if the host machine itself is healthy, regardless of which specific container is consuming resources.&lt;/p&gt;

&lt;p&gt;Looking at our live metrics, we can verify the health of our host:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;System Load (8.9%):&lt;/strong&gt; Our system load is extremely low compared to our capacity. With 24 cores available, an 8.9% load means we have massive headroom for scaling our CI/CD pipelines. We are nowhere near saturation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RAM Usage (85.4%):&lt;/strong&gt; This looks high, but it is normal for a dedicated host running heavy Java applications (Jenkins, SonarQube, Artifactory) and Elasticsearch. Linux caches unused memory to speed up file access. Crucially, our &lt;strong&gt;Swap Used&lt;/strong&gt; is only 4.6%, meaning the active working set fits comfortably in physical RAM.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Root FS Used (33.6%):&lt;/strong&gt; Our disk usage is healthy. We have ample space for Docker images, build artifacts, and logs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The Insight:&lt;/strong&gt; The physical host is stable. If an application feels slow, it is likely a software configuration issue (e.g., JVM heap limit), not a lack of raw hardware power.&lt;/p&gt;

&lt;h3&gt;
  
  
  cAdvisor (The Containers)
&lt;/h3&gt;

&lt;p&gt;If Node Exporter is the view from a satellite, cAdvisor is the view from a drone hovering over each building. It breaks down resource usage &lt;em&gt;per container&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;This dashboard allows us to answer the question: &lt;em&gt;"Who is eating the RAM?"&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitLab:&lt;/strong&gt; Unsurprisingly, GitLab is our heaviest resident, consuming an average of &lt;strong&gt;4.11 GiB&lt;/strong&gt; of RAM and spiking to &lt;strong&gt;27.7% CPU&lt;/strong&gt; usage. This is expected for a monolithic Rails application.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Artifactory:&lt;/strong&gt; Consuming &lt;strong&gt;2.71 GiB&lt;/strong&gt;, typical for a Java-based artifact manager.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Elasticsearch:&lt;/strong&gt; Holding steady at &lt;strong&gt;2.65 GiB&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Jenkins Controller:&lt;/strong&gt; Surprisingly efficient at &lt;strong&gt;936 MiB&lt;/strong&gt;. This likely indicates it is currently idle; if we triggered a build pipeline, we would expect to see this metric climb.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The Insight:&lt;/strong&gt; We can see our resource allocation strategy in action. We have given the heavy lifters (GitLab, Artifactory, Elasticsearch) the room they need, while lighter services like &lt;code&gt;node-exporter&lt;/code&gt; (11 MiB) and &lt;code&gt;coturn&lt;/code&gt; (20.9 MiB) run with minimal footprint.&lt;/p&gt;

&lt;p&gt;This layer proves that our "City" has a solid foundation. The streets (Network), power (CPU), and land (RAM) are sufficient for the buildings we have constructed. Now, let's look inside the factories.&lt;/p&gt;

&lt;h2&gt;
  
  
  10.2 The Application Layer (The Factories)
&lt;/h2&gt;

&lt;p&gt;While the Infrastructure Layer monitors the "City," the Application Layer looks inside the "Factories." This is where we verify that our CI/CD business logic is actually functioning. We use our bespoke dashboards for this.&lt;/p&gt;

&lt;h3&gt;
  
  
  Jenkins: Performance (The Foreman)
&lt;/h3&gt;

&lt;p&gt;This dashboard reveals the operational state of our automation server.&lt;/p&gt;

&lt;p&gt;Looking at the live metrics, we see a distinct architectural signature:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Executors Free (0):&lt;/strong&gt; In a traditional setup, "0 Free Executors" would be a critical alert implying a total blockage. However, in our &lt;strong&gt;Controller-Agent&lt;/strong&gt; architecture (which we configured to spawn ephemeral Docker agents), this is &lt;em&gt;normal behavior&lt;/em&gt;. The Controller itself has 0 executors because it delegates all work to agents.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build Queue Size (0):&lt;/strong&gt; This is the true health indicator. Even though we just started a build, the queue is empty. This means the ephemeral agent provisioning infrastructure worked instantly. The job was picked up and is currently running.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;System Health Score (Healthy):&lt;/strong&gt; Jenkins internal self-check is passing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The Insight:&lt;/strong&gt; The "0 Executors" metric confirms that we are adhering to the "Master does no work" best practice. We aren't burning CPU cycles on the controller for builds; we are outsourcing them to the Docker socket.&lt;/p&gt;

&lt;h3&gt;
  
  
  GitLab Omnibus (The Warehouse)
&lt;/h3&gt;

&lt;p&gt;This dashboard is often the most revealing because GitLab is the heaviest component in our stack.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;RSS Memory (21.2 GiB):&lt;/strong&gt; This single number explains the high memory usage we saw on the Host Dashboard (85.4%). GitLab's "Puma" (web server) and "Sidekiq" (background workers) workers are memory-hungry processes. This validates why we allocated a dedicated host for this stack.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Latency P95 (496 ms):&lt;/strong&gt; For a complex Rails application, a sub-500ms response time at the 95th percentile is acceptable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Total Request Rate (0.022 ops/s):&lt;/strong&gt; The system is currently idle regarding user traffic, yet memory usage remains high. This is characteristic of Java and Ruby applications; they reserve memory upfront.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The Insight:&lt;/strong&gt; If we ever need to scale, &lt;strong&gt;RAM&lt;/strong&gt; will be our bottleneck, not CPU. We know this because GitLab is consuming ~70% of total system memory while the CPU sits idle.&lt;/p&gt;

&lt;h3&gt;
  
  
  SonarQube (The Inspector)
&lt;/h3&gt;

&lt;p&gt;This dashboard verifies our code quality gatekeeper.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DevOps Integrations (GitLab: OK):&lt;/strong&gt; This green "OK" is technically profound. It confirms that the OAuth2/OIDC connection we configured in previous articles is active. SonarQube can talk to GitLab to decorate Pull Requests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pending Tasks (0):&lt;/strong&gt; The Compute Engine is keeping up with the workload. If we saw this number rise during a build, it would mean our Quality Gate analysis is slower than our CI pipeline.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  10.3 The "Ah-Ha" Moment (Correlation)
&lt;/h2&gt;

&lt;p&gt;The true power of this stack isn't looking at one dashboard; it's looking at &lt;strong&gt;all of them simultaneously&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Let's trace the "Pulse" of a single CI pipeline event through our monitoring city:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Trigger:&lt;/strong&gt; A developer pushes code to GitLab.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;GitLab Dashboard:&lt;/em&gt; &lt;code&gt;http_requests_total&lt;/code&gt; spikes slightly as the webhook is fired.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Job:&lt;/strong&gt; Jenkins receives the webhook.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Jenkins Dashboard:&lt;/em&gt; &lt;code&gt;jenkins_queue_size&lt;/code&gt; momentarily bumps to 1, then drops to 0 as an agent is spawned.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Provisioning:&lt;/strong&gt; The Ephemeral Agent starts.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;cAdvisor Dashboard:&lt;/em&gt; A new container appears in the list. You see a sudden spike in CPU usage attributed to &lt;code&gt;docker&lt;/code&gt; processes as the container spins up.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Analysis:&lt;/strong&gt; The build completes and sends code to SonarQube.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;SonarQube Dashboard:&lt;/em&gt; &lt;code&gt;sonarqube_compute_engine_pending_tasks&lt;/code&gt; moves from 0 to 1. The &lt;code&gt;Compute Engine Status&lt;/code&gt; might briefly toggle states.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Impact:&lt;/strong&gt; The Host.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Node Exporter Dashboard:&lt;/em&gt; You see the aggregate effect. The "System Load" climbs, and "Network Traffic" spikes as artifacts move between GitLab (Registry), Jenkins (Build), and SonarQube (Analysis).&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The Value:&lt;/strong&gt; We have moved from "Something is slow" to "The Jenkins Agent is waiting on Disk I/O because GitLab is performing a heavy garbage collection cycle." That is the difference between guessing and engineering.&lt;/p&gt;

&lt;h2&gt;
  
  
  10.4 Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;We started this series with a blank Linux server and a commitment to "First Principles." We didn't just install tools; we architected a city.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Foundations:&lt;/strong&gt; We built a "Control Center" to manage Docker securely (Article 1).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Identity:&lt;/strong&gt; We established a private Certificate Authority so every service could prove its identity (Article 2).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Districts:&lt;/strong&gt; We constructed the Source Code Library (GitLab), the Factory (Jenkins), the Warehouse (Artifactory), the Inspection Office (SonarQube), the Public Address System (Mattermost), and the Investigation Bureau (ELK).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, with &lt;strong&gt;Article 9&lt;/strong&gt;, we have turned the lights on.&lt;/p&gt;

&lt;p&gt;By deploying Prometheus and Grafana, we have achieved &lt;strong&gt;Full Stack Visibility&lt;/strong&gt;. We are no longer guessing why a build is slow; we can see the CPU spike on the host, the memory pressure in the container, and the queue depth in the application—all on one screen. We have moved from "managing tools" to "observing a system."&lt;/p&gt;

&lt;p&gt;This marks the completion of our infrastructure. The city is built. The roads are paved. The factories are running.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Next Steps&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;There is only one thing left to do.&lt;/p&gt;

&lt;p&gt;We have a powerful city, but we are currently operating it like manual laborers—clicking buttons in UIs and running shell scripts. To truly master this stack, we need to automate the &lt;em&gt;operation&lt;/em&gt; of the city itself.&lt;/p&gt;

&lt;p&gt;In the final article of this series, &lt;strong&gt;Article 10: The Python API Package (Capstone)&lt;/strong&gt;, we will build the "Master Control Package." We will write a professional Python library that wraps the APIs of every service we have deployed, allowing us to orchestrate our entire infrastructure—from creating GitLab repos to triggering Jenkins builds—programmatically from our Control Center.&lt;/p&gt;

</description>
      <category>prometheus</category>
      <category>grafana</category>
      <category>cicd</category>
      <category>devops</category>
    </item>
    <item>
      <title>Part 08: Building a Sovereign Software Factory: Observability with the ELK Stack</title>
      <dc:creator>Warren Jitsing</dc:creator>
      <pubDate>Tue, 23 Dec 2025 05:45:13 +0000</pubDate>
      <link>https://dev.to/warren_jitsing_dd1c1d6fc6/part-08-building-a-sovereign-software-factory-observability-with-the-elk-stack-2159</link>
      <guid>https://dev.to/warren_jitsing_dd1c1d6fc6/part-08-building-a-sovereign-software-factory-observability-with-the-elk-stack-2159</guid>
      <description>&lt;p&gt;GitHub: &lt;a href="https://github.com/InfiniteConsult/0012_cicd_part08_elk" rel="noopener noreferrer"&gt;https://github.com/InfiniteConsult/0012_cicd_part08_elk&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; In this installment, we solve the "Noise Problem" where fragmented logs make debugging impossible. We deploy the &lt;strong&gt;ELK Stack (Elasticsearch, Logstash, Kibana)&lt;/strong&gt; as our &lt;strong&gt;"Radar System,"&lt;/strong&gt; but first, we must navigate the &lt;strong&gt;"Abstraction Leak"&lt;/strong&gt; of Elasticsearch by tuning the host kernel's &lt;code&gt;vm.max_map_count&lt;/code&gt; via an architect script. We implement a sophisticated &lt;strong&gt;Ingest Pipeline&lt;/strong&gt; to parse logs from GitLab, SonarQube, and Artifactory into structured data, and deploy &lt;strong&gt;Filebeat&lt;/strong&gt; as a host-level collector to harvest logs without modifying our existing containers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Sovereign Software Factory Series:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Part 01:&lt;/strong&gt; Building a Sovereign Software Factory: Docker Networking &amp;amp; Persistence&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 02:&lt;/strong&gt; Building a Sovereign Software Factory: The Local Root CA &amp;amp; Trust Chains&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 03:&lt;/strong&gt; Building a Sovereign Software Factory: Self-Hosted GitLab &amp;amp; Secrets Management&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 04:&lt;/strong&gt; Building a Sovereign Software Factory: Jenkins Configuration as Code (JCasC)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 05:&lt;/strong&gt; Building a Sovereign Software Factory: Artifactory &amp;amp; The "Strict TLS" Trap&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 06:&lt;/strong&gt; Building a Sovereign Software Factory: SonarQube Quality Gates&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 07:&lt;/strong&gt; Building a Sovereign Software Factory: ChatOps with Mattermost&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 08: Building a Sovereign Software Factory: Observability with the ELK Stack&lt;/strong&gt; (You are here)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 09:&lt;/strong&gt; Building a Sovereign Software Factory: Monitoring with Prometheus &amp;amp; Grafana&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 10:&lt;/strong&gt; Building a Sovereign Software Factory: The Python API Package (Capstone)&lt;/li&gt;
&lt;/ol&gt;




&lt;h1&gt;
  
  
  Chapter 1: The "Black Box" Problem
&lt;/h1&gt;

&lt;h2&gt;
  
  
  1.1 The 3 AM Scenario
&lt;/h2&gt;

&lt;p&gt;We have spent the last seven articles building a robust "Software Factory." We have a Git server, a Build server, a Quality Gate, an Artifact Warehouse, and a Chat bot. The lights are on, the machines are humming, and the code is flowing.&lt;/p&gt;

&lt;p&gt;But we have a problem.&lt;/p&gt;

&lt;p&gt;Imagine it is 3:00 AM. You receive a frantic message from a developer: &lt;em&gt;"The build is failing, and I can't merge the hotfix."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You log into Jenkins. The build status is red, but the console output just says &lt;code&gt;Build failed: Connection refused&lt;/code&gt;. Connection to what? Is Artifactory down? Did the SonarQube token expire? Is the GitLab webhook failing?&lt;/p&gt;

&lt;p&gt;To find out, you have to perform the "Systems Administrator Dance":&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; SSH into the host server.&lt;/li&gt;
&lt;li&gt; Run &lt;code&gt;docker ps&lt;/code&gt; to find the container IDs.&lt;/li&gt;
&lt;li&gt; Run &lt;code&gt;docker logs -f jenkins-controller&lt;/code&gt; and scroll through thousands of lines of Java stack traces.&lt;/li&gt;
&lt;li&gt; Open a second terminal.&lt;/li&gt;
&lt;li&gt; Run &lt;code&gt;docker logs -f gitlab&lt;/code&gt; and grep for Nginx errors.&lt;/li&gt;
&lt;li&gt; Open a third terminal for SonarQube.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You are manually correlating timestamps across three different windows, trying to guess if a log entry at &lt;code&gt;15:01:05&lt;/code&gt; in Jenkins matches an error at &lt;code&gt;15:01:06&lt;/code&gt; in GitLab.&lt;/p&gt;

&lt;p&gt;This is the &lt;strong&gt;"Black Box" Problem&lt;/strong&gt;. We have built a distributed system, but we are managing it like a monolith. We have excellent components, but zero visibility into how they interact.&lt;/p&gt;

&lt;p&gt;In this article, we are going to fix this. We are going to stop treating logs as text files scattered across disk drives and start treating them as &lt;strong&gt;Data&lt;/strong&gt;. We will build a Centralized Observability Pipeline that allows us to answer complex questions ("Show me all errors that happened in the last 5 minutes across &lt;em&gt;all&lt;/em&gt; services") without ever touching the SSH command.&lt;/p&gt;

&lt;h2&gt;
  
  
  1.2 Architecture from First Principles
&lt;/h2&gt;

&lt;p&gt;To solve this, we need a "Central Investigation Office." In the industry, this is typically handled by the &lt;strong&gt;ELK Stack&lt;/strong&gt;—an acronym for three open-source tools maintained by Elastic:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Elasticsearch (The Brain):&lt;/strong&gt; A search engine and database. It stores our logs not as text strings, but as structured JSON documents. It allows us to search terabytes of data in milliseconds.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Logstash (The Parser):&lt;/strong&gt; Historically, this was the middleman. It received messy text streams, used Regex to parse them into fields, and then sent the clean data to the database.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Kibana (The Interface):&lt;/strong&gt; The visualization layer. This is the dashboard where we will build graphs, charts, and maps to visualize the data stored in "The Brain."&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;However, for our platform, we are going to make a crucial architectural deviation to keep our stack efficient.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We are removing Logstash.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In a massive enterprise with thousands of servers, Logstash is useful as a heavy-duty buffer and router. But for a single-node "Factory in a Box" like ours, Logstash is unnecessary overhead. It requires a heavy JVM (Java Virtual Machine) and adds latency and complexity to our deployment.&lt;/p&gt;

&lt;p&gt;Instead, we will use &lt;strong&gt;Filebeat&lt;/strong&gt; as our shipping agent. Filebeat is lightweight, written in Go, and sits directly on the host. It will ship logs directly to Elasticsearch.&lt;/p&gt;

&lt;p&gt;But this creates a problem: if we remove the Parser (Logstash), who parses the logs? If Jenkins sends a raw text line, who turns it into a JSON object?&lt;/p&gt;

&lt;p&gt;The answer lies in &lt;strong&gt;Elasticsearch Ingest Pipelines&lt;/strong&gt;. Modern Elasticsearch has the ability to run parsing logic (Grok patterns, Date parsing, GeoIP lookups) on the incoming data itself, just before it is indexed. This simplifies our stack significantly: fewer containers to manage, less RAM consumed, and a more direct data path. We are effectively moving the "intelligence" from the middleman to the destination.&lt;/p&gt;

&lt;h2&gt;
  
  
  1.3 The "Zero-Privilege" Mandate
&lt;/h2&gt;

&lt;p&gt;When deploying a log collector, you face a critical security choice: &lt;strong&gt;How do you access the data?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Option A is &lt;strong&gt;Dynamic Autodiscovery&lt;/strong&gt;. This is a feature often used in large clusters where Filebeat automatically detects when a new container starts, identifies it via the Docker API, and begins harvesting its logs. To do this, Filebeat typically requires access to the &lt;strong&gt;Docker Socket&lt;/strong&gt; (&lt;code&gt;/var/run/docker.sock&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;We must treat the Docker Socket as a "Root Key." If a malicious actor compromises a container that has access to this socket, they can issue API commands to spin up new privileged containers, mount the host's root filesystem, and take over the entire server.&lt;/p&gt;

&lt;p&gt;Option B is &lt;strong&gt;Static File Ingestion&lt;/strong&gt;. This is the "dumb," manual approach. We explicitly tell Filebeat: &lt;em&gt;"There are log files in this specific directory. Read them."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We choose &lt;strong&gt;Option B&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We will not mount the Docker socket. Instead, we will mount the host's volume directory (&lt;code&gt;/var/lib/docker/volumes&lt;/code&gt;) into Filebeat as &lt;strong&gt;Read-Only&lt;/strong&gt;. Filebeat will look at the disk, see the log files generated by Jenkins and Nginx, and ship them. It cannot query the Docker API, it cannot inspect container metadata, and most importantly, it cannot control the Docker daemon.&lt;/p&gt;

&lt;p&gt;This makes our configuration slightly more verbose (we have to map paths manually), but it ensures that our logging infrastructure cannot be weaponized against us.&lt;/p&gt;

&lt;h1&gt;
  
  
  Chapter 2: The Architect Script
&lt;/h1&gt;

&lt;h2&gt;
  
  
  2.1 The Kernel Barrier: &lt;code&gt;vm.max_map_count&lt;/code&gt; (Again)
&lt;/h2&gt;

&lt;p&gt;Before we can launch a single container, we must address the Operating System.&lt;/p&gt;

&lt;p&gt;If you recall from &lt;strong&gt;Article 6 (SonarQube)&lt;/strong&gt;, we encountered a critical kernel tunable called &lt;code&gt;vm.max_map_count&lt;/code&gt;. SonarQube failed to start because its embedded Elasticsearch engine required more memory map areas than the default Linux kernel allows.&lt;/p&gt;

&lt;p&gt;Now that we are running a dedicated, standalone Elasticsearch cluster, this requirement is even more strict.&lt;/p&gt;

&lt;p&gt;Elasticsearch relies heavily on &lt;code&gt;mmap&lt;/code&gt; (memory-mapped files) to store its indices. This allows the database to map logical file contents directly into the process's virtual memory space, letting the OS handle the paging. It is a massive performance optimization.&lt;/p&gt;

&lt;p&gt;However, the default limit on most Linux distros (Debian included) is &lt;code&gt;65,530&lt;/code&gt; maps. Elasticsearch requires at least &lt;code&gt;262,144&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If we ignore this, the Elasticsearch container will start, print a cryptic bootstrap check failure, and immediately die. Because this is a &lt;strong&gt;Kernel&lt;/strong&gt; setting, we cannot fix it inside the &lt;code&gt;Dockerfile&lt;/code&gt;; it must be applied to the &lt;strong&gt;Host&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This necessitates a dedicated setup script. We cannot simply rely on &lt;code&gt;docker run&lt;/code&gt; commands; we need an "Architect" script (&lt;code&gt;01-setup-elk.sh&lt;/code&gt;) to prepare the physical ground before we pour the digital concrete.&lt;/p&gt;

&lt;p&gt;Here is the complete script. Save this as &lt;code&gt;01-setup-elk.sh&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;#               01-setup-elk.sh&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  The "Architect" script for the ELK Stack.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  1. Kernel: Enforces vm.max_map_count=262144.&lt;/span&gt;
&lt;span class="c"&gt;#  2. Secrets: Generates Passwords &amp;amp; Encryption Keys.&lt;/span&gt;
&lt;span class="c"&gt;#  3. Preparation: Temporarily owns config dirs for writing.&lt;/span&gt;
&lt;span class="c"&gt;#  4. Configs: Generates configs for ES, Kibana, Filebeat.&lt;/span&gt;
&lt;span class="c"&gt;#  5. Integration: Configures Jenkins sidecar logging &amp;amp; redeploys.&lt;/span&gt;
&lt;span class="c"&gt;#  6. Permissions: Enforces strict ownership (Root/UID 1000).&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="c"&gt;# --- 1. Define Paths ---&lt;/span&gt;
&lt;span class="nv"&gt;HOST_CICD_ROOT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/cicd_stack"&lt;/span&gt;
&lt;span class="nv"&gt;ELK_BASE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOST_CICD_ROOT&lt;/span&gt;&lt;span class="s2"&gt;/elk"&lt;/span&gt;
&lt;span class="nv"&gt;MASTER_ENV_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOST_CICD_ROOT&lt;/span&gt;&lt;span class="s2"&gt;/cicd.env"&lt;/span&gt;

&lt;span class="c"&gt;# Jenkins Integration Paths&lt;/span&gt;
&lt;span class="nv"&gt;JENKINS_MODULE_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/Documents/FromFirstPrinciples/articles/0008_cicd_part04_jenkins"&lt;/span&gt;
&lt;span class="nv"&gt;JENKINS_ENV_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JENKINS_MODULE_DIR&lt;/span&gt;&lt;span class="s2"&gt;/jenkins.env"&lt;/span&gt;
&lt;span class="nv"&gt;JENKINS_VOL_DATA&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/var/lib/docker/volumes/jenkins-home/_data"&lt;/span&gt;

&lt;span class="c"&gt;# Source Certificate Paths&lt;/span&gt;
&lt;span class="nv"&gt;CA_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOST_CICD_ROOT&lt;/span&gt;&lt;span class="s2"&gt;/ca"&lt;/span&gt;
&lt;span class="nv"&gt;SRC_CA_CRT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CA_DIR&lt;/span&gt;&lt;span class="s2"&gt;/pki/certs/ca.pem"&lt;/span&gt;

&lt;span class="nv"&gt;SRC_ES_CRT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CA_DIR&lt;/span&gt;&lt;span class="s2"&gt;/pki/services/elk/elasticsearch.cicd.local/elasticsearch.cicd.local.crt.pem"&lt;/span&gt;
&lt;span class="nv"&gt;SRC_ES_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CA_DIR&lt;/span&gt;&lt;span class="s2"&gt;/pki/services/elk/elasticsearch.cicd.local/elasticsearch.cicd.local.key.pem"&lt;/span&gt;

&lt;span class="nv"&gt;SRC_KIB_CRT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CA_DIR&lt;/span&gt;&lt;span class="s2"&gt;/pki/services/elk/kibana.cicd.local/kibana.cicd.local.crt.pem"&lt;/span&gt;
&lt;span class="nv"&gt;SRC_KIB_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CA_DIR&lt;/span&gt;&lt;span class="s2"&gt;/pki/services/elk/kibana.cicd.local/kibana.cicd.local.key.pem"&lt;/span&gt;

&lt;span class="c"&gt;# Config Destinations&lt;/span&gt;
&lt;span class="nv"&gt;ES_CONFIG_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ELK_BASE&lt;/span&gt;&lt;span class="s2"&gt;/elasticsearch/config"&lt;/span&gt;
&lt;span class="nv"&gt;KIBANA_CONFIG_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ELK_BASE&lt;/span&gt;&lt;span class="s2"&gt;/kibana/config"&lt;/span&gt;
&lt;span class="nv"&gt;FILEBEAT_CONFIG_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ELK_BASE&lt;/span&gt;&lt;span class="s2"&gt;/filebeat/config"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🚀 Starting ELK 'Architect' Setup..."&lt;/span&gt;

&lt;span class="c"&gt;# --- 2. Host Kernel Tuning ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Phase 1: Kernel Tuning ---"&lt;/span&gt;
&lt;span class="nv"&gt;REQUIRED_MAP_COUNT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;262144
&lt;span class="nv"&gt;SYSCTL_CONF&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/etc/sysctl.conf"&lt;/span&gt;

&lt;span class="nv"&gt;CURRENT_MAP_COUNT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;sysctl &lt;span class="nt"&gt;-n&lt;/span&gt; vm.max_map_count&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CURRENT_MAP_COUNT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-lt&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$REQUIRED_MAP_COUNT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Updating runtime limit..."&lt;/span&gt;
    &lt;span class="nb"&gt;sudo &lt;/span&gt;sysctl &lt;span class="nt"&gt;-w&lt;/span&gt; vm.max_map_count&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$REQUIRED_MAP_COUNT&lt;/span&gt;
&lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Runtime limit sufficient."&lt;/span&gt;
&lt;span class="k"&gt;fi

if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s2"&gt;"vm.max_map_count=&lt;/span&gt;&lt;span class="nv"&gt;$REQUIRED_MAP_COUNT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SYSCTL_CONF&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Persisting limit in &lt;/span&gt;&lt;span class="nv"&gt;$SYSCTL_CONF&lt;/span&gt;&lt;span class="s2"&gt;..."&lt;/span&gt;
    &lt;span class="nb"&gt;sudo sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'/vm.max_map_count/d'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SYSCTL_CONF&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"vm.max_map_count=&lt;/span&gt;&lt;span class="nv"&gt;$REQUIRED_MAP_COUNT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SYSCTL_CONF&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# --- 3. Directory &amp;amp; Secrets Setup ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Phase 2: Secrets &amp;amp; Directories ---"&lt;/span&gt;

&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ES_CONFIG_DIR&lt;/span&gt;&lt;span class="s2"&gt;/certs"&lt;/span&gt;
&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$KIBANA_CONFIG_DIR&lt;/span&gt;&lt;span class="s2"&gt;/certs"&lt;/span&gt;
&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$FILEBEAT_CONFIG_DIR&lt;/span&gt;&lt;span class="s2"&gt;/certs"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then &lt;/span&gt;&lt;span class="nb"&gt;touch&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;fi

&lt;/span&gt;key_exists&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s2"&gt;"^&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;="&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
generate_secret&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; openssl rand &lt;span class="nt"&gt;-hex&lt;/span&gt; 32&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
generate_password&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; openssl rand &lt;span class="nt"&gt;-hex&lt;/span&gt; 16&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;update_env&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false

&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; key_exists &lt;span class="s2"&gt;"ELASTIC_PASSWORD"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ELASTIC_PASSWORD=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;generate_password&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nv"&gt;update_env&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true
&lt;/span&gt;&lt;span class="k"&gt;fi
if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; key_exists &lt;span class="s2"&gt;"KIBANA_PASSWORD"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"KIBANA_PASSWORD=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;generate_password&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nv"&gt;update_env&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true
&lt;/span&gt;&lt;span class="k"&gt;fi
if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; key_exists &lt;span class="s2"&gt;"XPACK_SECURITY_ENCRYPTIONKEY"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"XPACK_SECURITY_ENCRYPTIONKEY=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;generate_secret&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;generate_secret&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"XPACK_REPORTING_ENCRYPTIONKEY=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;generate_secret&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nv"&gt;update_env&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true
&lt;/span&gt;&lt;span class="k"&gt;fi

if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$update_env&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Secrets generated."&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;else &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Secrets loaded."&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt; +a

&lt;span class="c"&gt;# --- 4. Ownership Handoff ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Phase 3: Ownership Handoff ---"&lt;/span&gt;
&lt;span class="nv"&gt;CURRENT_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;CURRENT_GROUP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CURRENT_USER&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;$CURRENT_GROUP&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ELK_BASE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# --- 5. Staging Certificates ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Phase 4: Staging Certificates ---"&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SRC_ES_CRT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ES_CONFIG_DIR&lt;/span&gt;&lt;span class="s2"&gt;/certs/elasticsearch.crt"&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SRC_ES_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ES_CONFIG_DIR&lt;/span&gt;&lt;span class="s2"&gt;/certs/elasticsearch.key"&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SRC_CA_CRT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ES_CONFIG_DIR&lt;/span&gt;&lt;span class="s2"&gt;/certs/ca.pem"&lt;/span&gt;

&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SRC_KIB_CRT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$KIBANA_CONFIG_DIR&lt;/span&gt;&lt;span class="s2"&gt;/certs/kibana.crt"&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SRC_KIB_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$KIBANA_CONFIG_DIR&lt;/span&gt;&lt;span class="s2"&gt;/certs/kibana.key"&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SRC_CA_CRT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;  &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$KIBANA_CONFIG_DIR&lt;/span&gt;&lt;span class="s2"&gt;/certs/ca.pem"&lt;/span&gt;

&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SRC_CA_CRT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;  &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$FILEBEAT_CONFIG_DIR&lt;/span&gt;&lt;span class="s2"&gt;/certs/ca.pem"&lt;/span&gt;

&lt;span class="c"&gt;# --- 6. Configuration Generation ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Phase 5: Generating Config Files ---"&lt;/span&gt;

&lt;span class="c"&gt;# A. Elasticsearch&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt; "&lt;/span&gt;&lt;span class="nv"&gt;$ES_CONFIG_DIR&lt;/span&gt;&lt;span class="sh"&gt;/elasticsearch.yml"
cluster.name: "cicd-elk"
node.name: "elasticsearch.cicd.local"
network.host: 0.0.0.0
discovery.type: single-node

path.data: /usr/share/elasticsearch/data
path.logs: /usr/share/elasticsearch/logs

xpack.security.enabled: true

xpack.security.http.ssl:
  enabled: true
  key: /usr/share/elasticsearch/config/certs/elasticsearch.key
  certificate: /usr/share/elasticsearch/config/certs/elasticsearch.crt
  certificate_authorities: [ "/usr/share/elasticsearch/config/certs/ca.pem" ]

xpack.security.transport.ssl:
  enabled: true
  key: /usr/share/elasticsearch/config/certs/elasticsearch.key
  certificate: /usr/share/elasticsearch/config/certs/elasticsearch.crt
  certificate_authorities: [ "/usr/share/elasticsearch/config/certs/ca.pem" ]

ingest.geoip.downloader.enabled: false
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# B. Kibana&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt; "&lt;/span&gt;&lt;span class="nv"&gt;$KIBANA_CONFIG_DIR&lt;/span&gt;&lt;span class="sh"&gt;/kibana.yml"
server.host: "0.0.0.0"
server.name: "kibana.cicd.local"
server.publicBaseUrl: "https://kibana.cicd.local:5601"

server.ssl.enabled: true
server.ssl.certificate: "/usr/share/kibana/config/certs/kibana.crt"
server.ssl.key: "/usr/share/kibana/config/certs/kibana.key"
server.ssl.certificateAuthorities: ["/usr/share/kibana/config/certs/ca.pem"]

elasticsearch.hosts: [ "https://elasticsearch.cicd.local:9200" ]
elasticsearch.ssl.certificateAuthorities: [ "/usr/share/kibana/config/certs/ca.pem" ]
elasticsearch.username: "kibana_system"
elasticsearch.password: "&lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="sh"&gt;{ELASTICSEARCH_PASSWORD}"

xpack.security.encryptionKey: "&lt;/span&gt;&lt;span class="nv"&gt;$XPACK_SECURITY_ENCRYPTIONKEY&lt;/span&gt;&lt;span class="sh"&gt;"
xpack.encryptedSavedObjects.encryptionKey: "&lt;/span&gt;&lt;span class="nv"&gt;$XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY&lt;/span&gt;&lt;span class="sh"&gt;"
xpack.reporting.encryptionKey: "&lt;/span&gt;&lt;span class="nv"&gt;$XPACK_REPORTING_ENCRYPTIONKEY&lt;/span&gt;&lt;span class="sh"&gt;"

telemetry.enabled: false
telemetry.optIn: false
newsfeed.enabled: false
map.includeElasticMapsService: false
xpack.fleet.enabled: false
xpack.apm.enabled: false

xpack.actions.preconfigured:
  mattermost-webhook:
    name: "Mattermost CI/CD Channel"
    actionTypeId: .webhook
    config:
      url: "&lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="sh"&gt;{MATTERMOST_WEBHOOK_URL}"
      method: post
      hasAuth: false
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# C. Filebeat&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt; "&lt;/span&gt;&lt;span class="nv"&gt;$FILEBEAT_CONFIG_DIR&lt;/span&gt;&lt;span class="sh"&gt;/filebeat.yml"
filebeat.inputs:
  # 1. Jenkins (Sidecar File)
  # Reads the file generated by standard Java Logging
  - type: filestream
    id: jenkins-log
    paths:
      - /host_volumes/jenkins-home/_data/logs/jenkins.log*
    prospector.scanner.exclude_files: ['&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sh"&gt;lck&lt;/span&gt;&lt;span class="nv"&gt;$'&lt;/span&gt;&lt;span class="sh"&gt;]
    fields: { service_name: "jenkins" }
    fields_under_root: true
    multiline.type: pattern
    multiline.pattern: '^&lt;/span&gt;&lt;span class="se"&gt;\[\d&lt;/span&gt;&lt;span class="sh"&gt;{4}-&lt;/span&gt;&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="sh"&gt;{2}-&lt;/span&gt;&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="sh"&gt;{2}'
    multiline.negate: true
    multiline.match: after

  # 2. Host System Logs (Journald)
  # Reads binary logs directly from host journal directories
  - type: journald
    id: host-system
    paths:
      - /var/log/journal
    fields: { service_name: "system" }
    fields_under_root: true

  # 3. GitLab Nginx
  - type: filestream
    id: gitlab-nginx
    paths:
      - /host_volumes/gitlab-logs/_data/nginx/*access.log
      - /host_volumes/gitlab-logs/_data/nginx/*error.log
    fields: { service_name: "gitlab-nginx" }
    fields_under_root: true

  # 4. SonarQube CE
  - type: filestream
    id: sonarqube-ce
    paths:
      - /host_volumes/sonarqube-logs/_data/ce.log
    fields: { service_name: "sonarqube" }
    fields_under_root: true
    multiline.type: pattern
    multiline.pattern: '^&lt;/span&gt;&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="sh"&gt;{4}.&lt;/span&gt;&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="sh"&gt;{2}.&lt;/span&gt;&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="sh"&gt;{2}'
    multiline.negate: true
    multiline.match: after

  # 5. Mattermost
  - type: filestream
    id: mattermost
    paths:
      - /host_volumes/mattermost-logs/_data/mattermost.log
    fields: { service_name: "mattermost" }
    fields_under_root: true

  # 6. Artifactory
  - type: filestream
    id: artifactory
    paths:
      - /host_volumes/artifactory-data/_data/log/artifactory-service.log
      - /host_volumes/artifactory-data/_data/log/artifactory-request.log
      - /host_volumes/artifactory-data/_data/log/access-service.log
    fields: { service_name: "artifactory" }
    fields_under_root: true
    multiline.type: pattern
    multiline.pattern: '^&lt;/span&gt;&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="sh"&gt;{4}-&lt;/span&gt;&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="sh"&gt;{2}-&lt;/span&gt;&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="sh"&gt;{2}'
    multiline.negate: true
    multiline.match: after

output.elasticsearch:
  hosts: ["https://elasticsearch.cicd.local:9200"]
  pipeline: "cicd-logs"
  protocol: "https"
  ssl.certificate_authorities: ["/usr/share/filebeat/certs/ca.pem"]
  username: "elastic"
  password: "&lt;/span&gt;&lt;span class="nv"&gt;$ELASTIC_PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;"

setup.ilm.enabled: false
setup.template.enabled: false
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# D. Jenkins Logging Configuration (Sidecar Strategy)&lt;/span&gt;
&lt;span class="c"&gt;# 1. Create the Java logging properties file in the Jenkins volume&lt;/span&gt;
&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JENKINS_VOL_DATA&lt;/span&gt;&lt;span class="s2"&gt;/logs"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Generating Jenkins logging.properties ---"&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; | sudo tee "&lt;/span&gt;&lt;span class="nv"&gt;$JENKINS_VOL_DATA&lt;/span&gt;&lt;span class="sh"&gt;/logging.properties" &amp;gt; /dev/null
handlers = java.util.logging.FileHandler
.level = INFO

# File Handler Configuration
# Pattern: %h = user.home (jenkins_home), %g = generation number
java.util.logging.FileHandler.pattern = %h/logs/jenkins.log
java.util.logging.FileHandler.limit = 10485760
java.util.logging.FileHandler.count = 3
java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
java.util.logging.FileHandler.append = true

# Format: [YYYY-MM-DD HH:MM:SS] [LEVEL] Logger - Message
java.util.logging.SimpleFormatter.format = [%1&lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="sh"&gt;tF %1&lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="sh"&gt;tT] [%4&lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="sh"&gt;s] %3&lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="sh"&gt;s - %5&lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="sh"&gt;s %6&lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="sh"&gt;s%n
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# Ensure Jenkins User (UID 1000) owns the config&lt;/span&gt;
&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; 1000:1000 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JENKINS_VOL_DATA&lt;/span&gt;&lt;span class="s2"&gt;/logs"&lt;/span&gt;
&lt;span class="nb"&gt;sudo chown &lt;/span&gt;1000:1000 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JENKINS_VOL_DATA&lt;/span&gt;&lt;span class="s2"&gt;/logging.properties"&lt;/span&gt;

&lt;span class="c"&gt;# 2. Update Jenkins Environment to use this config&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JENKINS_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s2"&gt;"java.util.logging.config.file"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JENKINS_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Injecting JAVA_OPTS into jenkins.env ---"&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JENKINS_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"# ELK Integration: Sidecar Logging"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JENKINS_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'JAVA_OPTS=-Djava.util.logging.config.file=/var/jenkins_home/logging.properties'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JENKINS_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;else
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Jenkins env already configured for logging."&lt;/span&gt;
    &lt;span class="k"&gt;fi&lt;/span&gt;

    &lt;span class="c"&gt;# 3. Redeploy Jenkins to apply changes&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Redeploying Jenkins Controller ---"&lt;/span&gt;
    &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JENKINS_MODULE_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; ./03-deploy-controller.sh&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"⚠️ WARNING: Jenkins module not found at &lt;/span&gt;&lt;span class="nv"&gt;$JENKINS_MODULE_DIR&lt;/span&gt;&lt;span class="s2"&gt;."&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Jenkins logging will not be active until deployed."&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# --- 7. Env Files ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Phase 6: Scoped Env Files ---"&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt; "&lt;/span&gt;&lt;span class="nv"&gt;$ELK_BASE&lt;/span&gt;&lt;span class="sh"&gt;/elasticsearch/elasticsearch.env"
ELASTIC_PASSWORD=&lt;/span&gt;&lt;span class="nv"&gt;$ELASTIC_PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;
ES_JAVA_OPTS=-Xms1g -Xmx1g
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt; "&lt;/span&gt;&lt;span class="nv"&gt;$ELK_BASE&lt;/span&gt;&lt;span class="sh"&gt;/kibana/kibana.env"
ELASTICSEARCH_PASSWORD=&lt;/span&gt;&lt;span class="nv"&gt;$KIBANA_PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;
XPACK_SECURITY_ENCRYPTIONKEY=&lt;/span&gt;&lt;span class="nv"&gt;$XPACK_SECURITY_ENCRYPTIONKEY&lt;/span&gt;&lt;span class="sh"&gt;
XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY=&lt;/span&gt;&lt;span class="nv"&gt;$XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY&lt;/span&gt;&lt;span class="sh"&gt;
XPACK_REPORTING_ENCRYPTIONKEY=&lt;/span&gt;&lt;span class="nv"&gt;$XPACK_REPORTING_ENCRYPTIONKEY&lt;/span&gt;&lt;span class="sh"&gt;
MATTERMOST_WEBHOOK_URL=&lt;/span&gt;&lt;span class="nv"&gt;$SONAR_MATTERMOST_WEBHOOK&lt;/span&gt;&lt;span class="sh"&gt;
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt; "&lt;/span&gt;&lt;span class="nv"&gt;$ELK_BASE&lt;/span&gt;&lt;span class="sh"&gt;/filebeat/filebeat.env"
ELASTIC_PASSWORD=&lt;/span&gt;&lt;span class="nv"&gt;$ELASTIC_PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# --- 8. Final Permissions Lockdown ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Phase 7: Locking Down Permissions ---"&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;600 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ES_CONFIG_DIR&lt;/span&gt;&lt;span class="s2"&gt;/certs/"&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;.key
&lt;span class="nb"&gt;chmod &lt;/span&gt;600 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$KIBANA_CONFIG_DIR&lt;/span&gt;&lt;span class="s2"&gt;/certs/"&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;.key
&lt;span class="nb"&gt;chmod &lt;/span&gt;644 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ES_CONFIG_DIR&lt;/span&gt;&lt;span class="s2"&gt;/certs/"&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;.crt
&lt;span class="nb"&gt;chmod &lt;/span&gt;644 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$KIBANA_CONFIG_DIR&lt;/span&gt;&lt;span class="s2"&gt;/certs/"&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;.crt
&lt;span class="nb"&gt;chmod &lt;/span&gt;644 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$FILEBEAT_CONFIG_DIR&lt;/span&gt;&lt;span class="s2"&gt;/certs/"&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;.pem

&lt;span class="nb"&gt;chmod &lt;/span&gt;600 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ELK_BASE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;.env

&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; 1000:0 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ES_CONFIG_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; 1000:0 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$KIBANA_CONFIG_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;sudo chown &lt;/span&gt;1000:0 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ELK_BASE&lt;/span&gt;&lt;span class="s2"&gt;/elasticsearch/elasticsearch.env"&lt;/span&gt;
&lt;span class="nb"&gt;sudo chown &lt;/span&gt;1000:0 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ELK_BASE&lt;/span&gt;&lt;span class="s2"&gt;/kibana/kibana.env"&lt;/span&gt;

&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; root:root &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$FILEBEAT_CONFIG_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;sudo chown &lt;/span&gt;root:root &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ELK_BASE&lt;/span&gt;&lt;span class="s2"&gt;/filebeat/filebeat.env"&lt;/span&gt;

&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;644 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$FILEBEAT_CONFIG_DIR&lt;/span&gt;&lt;span class="s2"&gt;/filebeat.yml"&lt;/span&gt;
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;644 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ELK_BASE&lt;/span&gt;&lt;span class="s2"&gt;/elasticsearch/elasticsearch.env"&lt;/span&gt;
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;644 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ELK_BASE&lt;/span&gt;&lt;span class="s2"&gt;/kibana/kibana.env"&lt;/span&gt;
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;644 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ELK_BASE&lt;/span&gt;&lt;span class="s2"&gt;/filebeat/filebeat.env"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✅ Architect Setup Complete."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2.2 Secrets and Security Hygiene
&lt;/h2&gt;

&lt;p&gt;In &lt;strong&gt;Phase 2&lt;/strong&gt; of the script, we implement a "Generate Once, Keep Forever" logic to handle credentials securely.&lt;/p&gt;

&lt;p&gt;We do not hardcode passwords in our configuration files. Instead, the script performs a check:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; It looks for the &lt;code&gt;cicd.env&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt; If the &lt;code&gt;ELASTIC_PASSWORD&lt;/code&gt; key is missing, it uses &lt;code&gt;openssl rand -hex 16&lt;/code&gt; to generate a high-entropy 32-character password.&lt;/li&gt;
&lt;li&gt; It appends this new secret to the environment file.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This ensures that every deployment gets a unique, strong password that persists across restarts, without requiring manual intervention.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The XPACK Encryption Keys&lt;/strong&gt;&lt;br&gt;
Beyond simple passwords, the script also generates three distinct 32-character strings for &lt;strong&gt;XPACK Security&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;code&gt;xpack.security.encryptionKey&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt; &lt;code&gt;xpack.encryptedSavedObjects.encryptionKey&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt; &lt;code&gt;xpack.reporting.encryptionKey&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Kibana uses these keys to encrypt sensitive data at rest within its internal indices—specifically "Saved Objects" like dashboards, visualization settings, and alerting rules. It is critical that these keys remain constant. If you were to regenerate them (e.g., by deleting &lt;code&gt;cicd.env&lt;/code&gt;), Kibana would lose the ability to decrypt your existing dashboards, rendering them inaccessible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Ownership Handoff&lt;/strong&gt;&lt;br&gt;
Docker has a known friction point regarding file permissions. When you mount a host directory into a container, the container process sees the directory with the numeric UID of the host.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Elasticsearch runs strictly as &lt;strong&gt;UID 1000&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;If we (the host user) create the &lt;code&gt;./elk/elasticsearch/data&lt;/code&gt; folder, it is owned by our user (likely 1000), which allows the container to write.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;However&lt;/strong&gt;, if we let Docker create the folder automatically during the first &lt;code&gt;docker run&lt;/code&gt;, it is often created as &lt;strong&gt;Root&lt;/strong&gt;. The Elasticsearch process will then crash immediately because it lacks permission to write to its own data directory.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To prevent this race condition, &lt;strong&gt;Phase 3&lt;/strong&gt; of our script preemptively creates the directory structure and explicitly executes &lt;code&gt;chown -R $CURRENT_USER:$CURRENT_GROUP&lt;/code&gt;. This ensures the storage volume is correctly owned before the database attempts to initialize.&lt;/p&gt;
&lt;h2&gt;
  
  
  2.3 Staging Certificates
&lt;/h2&gt;

&lt;p&gt;In &lt;strong&gt;Article 2&lt;/strong&gt;, we established a centralized Certificate Authority (CA) and generated certificates for all our services. These files currently reside in &lt;code&gt;~/cicd_stack/ca/pki&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;However, we do not simply mount the entire &lt;code&gt;ca&lt;/code&gt; directory into every container. That would violate the Principle of Least Privilege; Elasticsearch does not need to see GitLab's private key.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Phase 4&lt;/strong&gt; of the script, we perform a "Staging" operation. We copy only the specific certificate (&lt;code&gt;crt&lt;/code&gt;) and private key (&lt;code&gt;key&lt;/code&gt;) required for each service into its respective configuration directory:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;./elk/elasticsearch/config/certs/&lt;/code&gt; gets &lt;code&gt;elasticsearch.crt&lt;/code&gt; and &lt;code&gt;.key&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;./elk/kibana/config/certs/&lt;/code&gt; gets &lt;code&gt;kibana.crt&lt;/code&gt; and &lt;code&gt;.key&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Everyone gets &lt;code&gt;ca.pem&lt;/code&gt; (the Trust Anchor).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This physical separation ensures that if the Elasticsearch container were compromised, the attacker would only retrieve the Elasticsearch identity, not the keys to the entire kingdom.&lt;/p&gt;
&lt;h2&gt;
  
  
  2.4 Dynamic Configuration Generation
&lt;/h2&gt;

&lt;p&gt;Most tutorials ask you to manually edit &lt;code&gt;elasticsearch.yml&lt;/code&gt; and &lt;code&gt;kibana.yml&lt;/code&gt;. This is error-prone and tedious, especially when dealing with dynamic secrets like our encryption keys.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Phase 5&lt;/strong&gt;, we use Bash "Heredocs" (&lt;code&gt;cat &amp;lt;&amp;lt; EOF&lt;/code&gt;) to generate these configuration files programmatically. This allows us to inject the environment variables (&lt;code&gt;$ELASTIC_PASSWORD&lt;/code&gt;, &lt;code&gt;$XPACK_SECURITY_ENCRYPTIONKEY&lt;/code&gt;) directly into the configuration files at creation time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Configuration Details:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Elasticsearch (&lt;code&gt;elasticsearch.yml&lt;/code&gt;):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;xpack.security.enabled: true&lt;/code&gt;: This turns on the security features (Authentication and TLS). Without this, anyone on the network could query the database.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;network.host: 0.0.0.0&lt;/code&gt;: Essential for Docker networking. If left at default (localhost), the container would be unreachable from Kibana.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Kibana (&lt;code&gt;kibana.yml&lt;/code&gt;):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;elasticsearch.ssl.certificateAuthorities&lt;/code&gt;: We explicitly tell Kibana to trust our self-signed CA. Without this, Kibana would refuse to connect to Elasticsearch over HTTPS.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;xpack.actions.preconfigured&lt;/code&gt;: We inject the Mattermost Webhook URL here. This pre-configures the alerting connector so we don't have to set it up manually in the UI later.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Filebeat (&lt;code&gt;filebeat.yml&lt;/code&gt;):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This is where we define our "Inputs." Notice we have separate sections for &lt;code&gt;filestream&lt;/code&gt; (reading log files) and &lt;code&gt;journald&lt;/code&gt; (reading system logs).&lt;/li&gt;
&lt;li&gt;We also define the &lt;code&gt;setup.ilm.enabled: false&lt;/code&gt;. Since we are a single node, we disable Index Lifecycle Management (ILM) setup to keep the configuration simple and prevent Filebeat from trying to manage cluster-wide policies.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  2.5 The Jenkins "Sidecar" Injection
&lt;/h2&gt;

&lt;p&gt;One of the most complex parts of this script is &lt;strong&gt;Phase 5-D&lt;/strong&gt;, where we fix the Jenkins logging problem.&lt;/p&gt;

&lt;p&gt;Standard Jenkins logs are unstructured text printed to STDOUT. To make them parseable, we need Jenkins to write structured logs to a file. We achieve this without rebuilding the Jenkins image by using a "Configuration Injection" strategy:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Generate Config:&lt;/strong&gt; The script writes a &lt;code&gt;logging.properties&lt;/code&gt; file to the Jenkins data volume. This file defines a &lt;code&gt;java.util.logging.FileHandler&lt;/code&gt; that writes logs to &lt;code&gt;/var/jenkins_home/logs/jenkins.log&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Inject Env Var:&lt;/strong&gt; The script appends &lt;code&gt;JAVA_OPTS=-Djava.util.logging.config.file=...&lt;/code&gt; to &lt;code&gt;jenkins.env&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Redeploy:&lt;/strong&gt; It triggers the Jenkins deploy script (&lt;code&gt;03-deploy-controller.sh&lt;/code&gt;) to restart the container.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When Jenkins restarts, it reads the new environment variable, loads the logging config, and begins writing clean, rotatable log files to the volume—which Filebeat is already configured to watch.&lt;/p&gt;
&lt;h2&gt;
  
  
  2.6 Final Permissions Lockdown
&lt;/h2&gt;

&lt;p&gt;In &lt;strong&gt;Phase 7&lt;/strong&gt;, we enforce strict file permissions. This is critical because some components (like Elasticsearch) check permissions on startup and will refuse to run if their configuration files are too open (e.g., world-readable).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Private Keys (&lt;code&gt;*.key&lt;/code&gt;):&lt;/strong&gt; set to &lt;code&gt;600&lt;/code&gt; (Read/Write by owner only).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Public Certs (&lt;code&gt;*.crt&lt;/code&gt;):&lt;/strong&gt; set to &lt;code&gt;644&lt;/code&gt; (Readable by everyone).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Elasticsearch/Kibana Configs:&lt;/strong&gt; Owned by &lt;code&gt;1000:0&lt;/code&gt; (User 1000, Root Group).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Filebeat Configs:&lt;/strong&gt; Owned by &lt;code&gt;root:root&lt;/code&gt;. &lt;strong&gt;This is mandatory.&lt;/strong&gt; Filebeat requires its configuration file to be owned by root and not writable by others (&lt;code&gt;chmod 644&lt;/code&gt;). If you miss this, Filebeat will fail to start with a "config file permission error."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With the Architect script complete, the foundation is laid. The kernel is tuned, secrets are generated, configs are written, and permissions are locked. We are ready to deploy the database.&lt;/p&gt;
&lt;h1&gt;
  
  
  Chapter 3: The Bedrock – Elasticsearch
&lt;/h1&gt;
&lt;h2&gt;
  
  
  3.1 The Deployment Script
&lt;/h2&gt;

&lt;p&gt;With the "Architect" script complete, the kernel is tuned and the secrets are safely stored. We are now ready to lay the foundation of our observability stack: the Elasticsearch database.&lt;/p&gt;

&lt;p&gt;This is not a generic "Hello World" deployment. This script is designed to handle the specific startup requirements of a production-grade search engine, including memory locking, file descriptor management, and an automated security bootstrap process.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;02-deploy-elasticsearch.sh&lt;/code&gt; with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;#               02-deploy-elasticsearch.sh&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  The "Bedrock" script.&lt;/span&gt;
&lt;span class="c"&gt;#  Deploys Elasticsearch (v9.2.2) and bootstraps security.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  1. Deploy: Runs ES with strict memory/ulimit guards.&lt;/span&gt;
&lt;span class="c"&gt;#  2. Healthcheck: Waits for Green status via HTTPS (using CA).&lt;/span&gt;
&lt;span class="c"&gt;#  3. Bootstrap: Sets 'kibana_system' password via API.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🚀 Deploying Elasticsearch (The Bedrock)..."&lt;/span&gt;

&lt;span class="c"&gt;# --- 1. Load Secrets ---&lt;/span&gt;
&lt;span class="nv"&gt;HOST_CICD_ROOT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/cicd_stack"&lt;/span&gt;
&lt;span class="nv"&gt;ELK_BASE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOST_CICD_ROOT&lt;/span&gt;&lt;span class="s2"&gt;/elk"&lt;/span&gt;
&lt;span class="nv"&gt;SCOPED_ENV_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ELK_BASE&lt;/span&gt;&lt;span class="s2"&gt;/elasticsearch/elasticsearch.env"&lt;/span&gt;
&lt;span class="nv"&gt;MASTER_ENV_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOST_CICD_ROOT&lt;/span&gt;&lt;span class="s2"&gt;/cicd.env"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SCOPED_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: Scoped env file not found at &lt;/span&gt;&lt;span class="nv"&gt;$SCOPED_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Please run 01-setup-elk.sh first."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# We need KIBANA_PASSWORD from master env for the bootstrap step&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: Master env file not found."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi
&lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt; +a

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$KIBANA_PASSWORD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: KIBANA_PASSWORD not found in cicd.env"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# --- 2. Clean Slate ---&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;elasticsearch&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Stopping existing 'elasticsearch'..."&lt;/span&gt;
    docker stop elasticsearch
&lt;span class="k"&gt;fi
if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;-aq&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;elasticsearch&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Removing existing 'elasticsearch'..."&lt;/span&gt;
    docker &lt;span class="nb"&gt;rm &lt;/span&gt;elasticsearch
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# --- 3. Volume Management ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Verifying elasticsearch-data volume..."&lt;/span&gt;
docker volume create elasticsearch-data &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null

&lt;span class="c"&gt;# --- 4. Deploy ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Launching Container ---"&lt;/span&gt;

&lt;span class="c"&gt;# NOTES:&lt;/span&gt;
&lt;span class="c"&gt;# - ulimit: Essential for ES performance (avoids bootstrap checks failure)&lt;/span&gt;
&lt;span class="c"&gt;# - cap-add IPC_LOCK: Allows memory locking to prevent swapping&lt;/span&gt;
&lt;span class="c"&gt;# - publish: 9200 bound to 127.0.0.1 (Host access only)&lt;/span&gt;

docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; elasticsearch &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--restart&lt;/span&gt; always &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network&lt;/span&gt; cicd-net &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--hostname&lt;/span&gt; elasticsearch.cicd.local &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--publish&lt;/span&gt; 127.0.0.1:9200:9200 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--ulimit&lt;/span&gt; &lt;span class="nv"&gt;nofile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;65535:65535 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--ulimit&lt;/span&gt; &lt;span class="nv"&gt;memlock&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;-1&lt;/span&gt;:-1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cap-add&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;IPC_LOCK &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--env-file&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SCOPED_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; elasticsearch-data:/usr/share/elasticsearch/data &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ELK_BASE&lt;/span&gt;&lt;span class="s2"&gt;/elasticsearch/config/elasticsearch.yml"&lt;/span&gt;:/usr/share/elasticsearch/config/elasticsearch.yml &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ELK_BASE&lt;/span&gt;&lt;span class="s2"&gt;/elasticsearch/config/certs"&lt;/span&gt;:/usr/share/elasticsearch/config/certs &lt;span class="se"&gt;\&lt;/span&gt;
  docker.elastic.co/elasticsearch/elasticsearch:9.2.2

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Container started. Waiting for healthcheck..."&lt;/span&gt;

&lt;span class="c"&gt;# --- 5. Secure Bootstrap (The "Zero Touch" Logic) ---&lt;/span&gt;

&lt;span class="nv"&gt;MAX_RETRIES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;60
&lt;span class="nv"&gt;COUNT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
&lt;span class="nv"&gt;ES_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://127.0.0.1:9200"&lt;/span&gt;

&lt;span class="c"&gt;# A. Extract ELASTIC_PASSWORD securely (Avoiding 'source' errors)&lt;/span&gt;
&lt;span class="nv"&gt;ELASTIC_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"^ELASTIC_PASSWORD="&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SCOPED_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="s1"&gt;'='&lt;/span&gt; &lt;span class="nt"&gt;-f2&lt;/span&gt; | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'"'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Wait for "status" to be green OR yellow&lt;/span&gt;
&lt;span class="c"&gt;# We removed --cacert because the host trusts the CA&lt;/span&gt;
&lt;span class="k"&gt;until &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="s2"&gt;"elastic:&lt;/span&gt;&lt;span class="nv"&gt;$ELASTIC_PASSWORD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ES_URL&lt;/span&gt;&lt;span class="s2"&gt;/_cluster/health"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-qE&lt;/span&gt; &lt;span class="s1"&gt;'"status":"(green|yellow)"'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$COUNT&lt;/span&gt; &lt;span class="nt"&gt;-ge&lt;/span&gt; &lt;span class="nv"&gt;$MAX_RETRIES&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"❌ Timeout waiting for Elasticsearch."&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Check logs: docker logs elasticsearch"&lt;/span&gt;
        &lt;span class="nb"&gt;exit &lt;/span&gt;1
    &lt;span class="k"&gt;fi
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   [&lt;/span&gt;&lt;span class="nv"&gt;$COUNT&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$MAX_RETRIES&lt;/span&gt;&lt;span class="s2"&gt;] Waiting for Green/Yellow status..."&lt;/span&gt;
    &lt;span class="nb"&gt;sleep &lt;/span&gt;5
    &lt;span class="nv"&gt;COUNT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;COUNT+1&lt;span class="k"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;done

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✅ Elasticsearch is Online."&lt;/span&gt;

&lt;span class="c"&gt;# B. Set kibana_system Password&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Bootstrapping Service Accounts ---"&lt;/span&gt;

&lt;span class="c"&gt;# 1. Run the command and let it print directly to stdout&lt;/span&gt;
curl &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ES_URL&lt;/span&gt;&lt;span class="s2"&gt;/_security/user/kibana_system/_password"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="s2"&gt;"elastic:&lt;/span&gt;&lt;span class="nv"&gt;$ELASTIC_PASSWORD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;password&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$KIBANA_PASSWORD&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt;

&lt;span class="c"&gt;# 2. Check the exit status of the curl command itself (simplified check)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt; &lt;span class="nt"&gt;-eq&lt;/span&gt; 0 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt; &lt;span class="c"&gt;# Newline for formatting&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✅ 'kibana_system' password request sent."&lt;/span&gt;
&lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"❌ Failed to send password request."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Bedrock Deployed Successfully ---"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3.2 Performance Tuning &amp;amp; System Limits
&lt;/h2&gt;

&lt;p&gt;In the deployment script above, you will notice a set of aggressive flags passed to the &lt;code&gt;docker run&lt;/code&gt; command. These are not optional; they are the difference between a database that runs for months and one that crashes under load.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Memory Locking (&lt;code&gt;bootstrap.memory_lock&lt;/code&gt;)&lt;/strong&gt;&lt;br&gt;
We pass &lt;code&gt;--cap-add=IPC_LOCK&lt;/code&gt; and &lt;code&gt;--ulimit memlock=-1:-1&lt;/code&gt; to the container.&lt;/p&gt;

&lt;p&gt;Elasticsearch is a Java application. If the host operating system decides to "swap" part of the Java Heap to the hard disk to save RAM, performance falls off a cliff. Even worse, the garbage collector can pause for seconds (or minutes) trying to read memory back from the disk. By granting &lt;code&gt;IPC_LOCK&lt;/code&gt; capability, we allow Elasticsearch to "lock" its allocated memory in RAM, preventing the OS from ever swapping it out.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. File Descriptors (&lt;code&gt;ulimit nofile&lt;/code&gt;)&lt;/strong&gt;&lt;br&gt;
We set &lt;code&gt;--ulimit nofile=65535:65535&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Under the hood, Elasticsearch uses the Lucene search library. Lucene creates thousands of tiny files to manage its indices. A standard Linux process is often limited to 1,024 open files. If we don't raise this limit, Elasticsearch will hit a "wall" during heavy indexing and crash with a &lt;code&gt;Too many open files&lt;/code&gt; exception. We preemptively raise this to 65k.&lt;/p&gt;
&lt;h2&gt;
  
  
  3.3 Network Security: The Localhost Bind
&lt;/h2&gt;

&lt;p&gt;You might notice the port mapping looks different from previous articles:&lt;br&gt;
&lt;code&gt;--publish 127.0.0.1:9200:9200&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In previous scripts, we mapped ports to &lt;code&gt;0.0.0.0&lt;/code&gt; (all interfaces) or relied on the Docker bridge network. Here, we are explicitly binding port 9200 to &lt;code&gt;127.0.0.1&lt;/code&gt; (localhost) only.&lt;/p&gt;

&lt;p&gt;This is a &lt;strong&gt;Defense in Depth&lt;/strong&gt; strategy.&lt;/p&gt;

&lt;p&gt;Elasticsearch is the "Brain" of our stack. It holds all our logs, which may contain sensitive data. By binding strictly to localhost, we ensure that &lt;strong&gt;no external device&lt;/strong&gt; on the network can talk directly to the database, even if our firewall rules fail.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Who can talk to it?&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Kibana:&lt;/strong&gt; Yes, because it joins the same Docker network (&lt;code&gt;cicd-net&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Filebeat:&lt;/strong&gt; Yes, via the Docker network.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You (The Admin):&lt;/strong&gt; Yes, via &lt;code&gt;curl&lt;/code&gt; on the host machine.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Who cannot talk to it?&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Another developer's laptop.&lt;/li&gt;
&lt;li&gt;A rogue device on the Wi-Fi.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This forces all human access to go through the visualized, authenticated layer (Kibana) rather than the raw data API.&lt;/p&gt;
&lt;h2&gt;
  
  
  3.4 The "Bootstrap" Dance (Automating Service Accounts)
&lt;/h2&gt;

&lt;p&gt;The most complex part of this script is &lt;strong&gt;Section 5: Secure Bootstrap&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We have a "Chicken and Egg" problem.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Kibana needs a password to log in to Elasticsearch (&lt;code&gt;kibana_system&lt;/code&gt; user).&lt;/li&gt;
&lt;li&gt; Elasticsearch does not allow us to set this password via an environment variable at startup.&lt;/li&gt;
&lt;li&gt; We can only set this password via the REST API &lt;em&gt;after&lt;/em&gt; Elasticsearch is running.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Most tutorials solve this by telling you to "run the container, wait a minute, and then manually run this curl command." That is not automation; that is manual labor.&lt;/p&gt;

&lt;p&gt;Our script solves this with a &lt;strong&gt;Healthcheck Loop&lt;/strong&gt;.&lt;br&gt;
It uses &lt;code&gt;curl&lt;/code&gt; to poll the cluster status (&lt;code&gt;_cluster/health&lt;/code&gt;). It loops every 5 seconds until it gets a HTTP 200 OK and a status of "Green" or "Yellow."&lt;/p&gt;

&lt;p&gt;Only when the brain is fully awake does the script execute the &lt;strong&gt;API Injection&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ES_URL&lt;/span&gt;&lt;span class="s2"&gt;/_security/user/kibana_system/_password"&lt;/span&gt; ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This automatically sets the password we generated in the Architect script. The result is a "Zero Touch" deployment: you run the script, walk away, and come back to a fully secured, interconnected cluster.&lt;/p&gt;

&lt;h1&gt;
  
  
  Chapter 4: The Interface – Kibana
&lt;/h1&gt;

&lt;h2&gt;
  
  
  4.1 The Deployment Script
&lt;/h2&gt;

&lt;p&gt;With Elasticsearch running as our data store, we need a way to visualize the information. Kibana is the native interface for the Elastic Stack, providing the dashboards, search bars, and management tools we will use to operate the platform.&lt;/p&gt;

&lt;p&gt;While Elasticsearch is the "Backend," Kibana is the "Frontend." It is a Node.js application that queries the database and renders the results. Because it holds the encryption keys for our alerts and saved objects, its deployment requires careful handling of secrets and configuration files.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;03-deploy-kibana.sh&lt;/code&gt; with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;#               03-deploy-kibana.sh&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🚀 Deploying Kibana (The Interface)..."&lt;/span&gt;

&lt;span class="c"&gt;# --- 1. Load Paths ---&lt;/span&gt;
&lt;span class="nv"&gt;HOST_CICD_ROOT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/cicd_stack"&lt;/span&gt;
&lt;span class="nv"&gt;ELK_BASE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOST_CICD_ROOT&lt;/span&gt;&lt;span class="s2"&gt;/elk"&lt;/span&gt;
&lt;span class="nv"&gt;SCOPED_ENV_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ELK_BASE&lt;/span&gt;&lt;span class="s2"&gt;/kibana/kibana.env"&lt;/span&gt;

&lt;span class="c"&gt;# --- 2. Validation ---&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SCOPED_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: Scoped env file not found at &lt;/span&gt;&lt;span class="nv"&gt;$SCOPED_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Please run 01-setup-elk.sh first."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# --- 3. Clean Slate ---&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;kibana&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Stopping existing 'kibana'..."&lt;/span&gt;
    docker stop kibana
&lt;span class="k"&gt;fi
if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;-aq&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;kibana&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Removing existing 'kibana'..."&lt;/span&gt;
    docker &lt;span class="nb"&gt;rm &lt;/span&gt;kibana
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# --- 4. Deploy ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Launching Container ---"&lt;/span&gt;

&lt;span class="c"&gt;# FIX: We rely fully on the env file now.&lt;/span&gt;
&lt;span class="c"&gt;# It contains: ELASTICSEARCH_PASSWORD, XPACK Keys, and MATTERMOST_WEBHOOK_URL&lt;/span&gt;

docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; kibana &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--restart&lt;/span&gt; always &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network&lt;/span&gt; cicd-net &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--hostname&lt;/span&gt; kibana.cicd.local &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--publish&lt;/span&gt; 127.0.0.1:5601:5601 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--env-file&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SCOPED_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ELK_BASE&lt;/span&gt;&lt;span class="s2"&gt;/kibana/config/kibana.yml"&lt;/span&gt;:/usr/share/kibana/config/kibana.yml:ro &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ELK_BASE&lt;/span&gt;&lt;span class="s2"&gt;/kibana/config/certs"&lt;/span&gt;:/usr/share/kibana/config/certs:ro &lt;span class="se"&gt;\&lt;/span&gt;
  docker.elastic.co/kibana/kibana:9.2.2

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Container started. Waiting for API healthcheck..."&lt;/span&gt;

&lt;span class="c"&gt;# --- 5. Healthcheck Loop ---&lt;/span&gt;
&lt;span class="nv"&gt;KIBANA_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://127.0.0.1:5601"&lt;/span&gt;
&lt;span class="nv"&gt;MAX_RETRIES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;60
&lt;span class="nv"&gt;COUNT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0

&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$COUNT&lt;/span&gt; &lt;span class="nt"&gt;-ge&lt;/span&gt; &lt;span class="nv"&gt;$MAX_RETRIES&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"❌ Timeout waiting for Kibana."&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Check logs: docker logs kibana"&lt;/span&gt;
        &lt;span class="nb"&gt;exit &lt;/span&gt;1
    &lt;span class="k"&gt;fi

    &lt;/span&gt;&lt;span class="nv"&gt;STATUS_OUTPUT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$KIBANA_URL&lt;/span&gt;&lt;span class="s2"&gt;/api/status"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$STATUS_OUTPUT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s1"&gt;'"level":"available"'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   [&lt;/span&gt;&lt;span class="nv"&gt;$COUNT&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$MAX_RETRIES&lt;/span&gt;&lt;span class="s2"&gt;] Status: AVAILABLE"&lt;/span&gt;
        &lt;span class="nb"&gt;break
    &lt;/span&gt;&lt;span class="k"&gt;else
        &lt;/span&gt;&lt;span class="nv"&gt;CURRENT_LEVEL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$STATUS_OUTPUT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="s1"&gt;'"level":"[^"]*"'&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-n1&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"unreachable"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   [&lt;/span&gt;&lt;span class="nv"&gt;$COUNT&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$MAX_RETRIES&lt;/span&gt;&lt;span class="s2"&gt;] Status: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CURRENT_LEVEL&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;unreachable&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;fi

    &lt;/span&gt;&lt;span class="nb"&gt;sleep &lt;/span&gt;5
    &lt;span class="nv"&gt;COUNT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;COUNT+1&lt;span class="k"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;done

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✅ Kibana is Green and Available."&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Access at: https://kibana.cicd.local:5601"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4.2 Configuration Strategy (The "Glue")
&lt;/h2&gt;

&lt;p&gt;A critical aspect of this deployment is how we manage the connection between Kibana and Elasticsearch without exposing credentials in plain text.&lt;/p&gt;

&lt;p&gt;In standard Docker tutorials, you might see environment variables passed directly in the &lt;code&gt;docker run&lt;/code&gt; command (e.g., &lt;code&gt;-e ELASTICSEARCH_PASSWORD=changeme&lt;/code&gt;). This is insecure because anyone running &lt;code&gt;docker inspect&lt;/code&gt; can see the password.&lt;/p&gt;

&lt;p&gt;We rely instead on &lt;strong&gt;Environment Injection&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In our deployment script, we use the &lt;code&gt;--env-file&lt;/code&gt; flag to load &lt;code&gt;elk/kibana/kibana.env&lt;/code&gt;. This file was not written by hand; it was programmatically generated by our Architect script in Chapter 2. By doing this, we achieve three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Credential Isolation:&lt;/strong&gt; The &lt;code&gt;ELASTICSEARCH_PASSWORD&lt;/code&gt; is injected at runtime. It exists only in the secure file on the host and the environment of the running process.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Persistent Encryption:&lt;/strong&gt; The three &lt;code&gt;XPACK&lt;/code&gt; encryption keys are critical for Kibana's internal security. They encrypt "Saved Objects" (dashboards, visualizations, and alerting rules) at rest. By injecting them from a persistent file, we ensure that if we destroy and recreate the container, Kibana can still decrypt our saved dashboards.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pre-configured Integrations:&lt;/strong&gt; We inject the &lt;code&gt;MATTERMOST_WEBHOOK_URL&lt;/code&gt; here. This allows Kibana to register the connector immediately upon startup, saving us from having to manually paste the webhook URL into the UI later.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We also treat our configuration files as &lt;strong&gt;Immutable&lt;/strong&gt;. Notice that &lt;code&gt;kibana.yml&lt;/code&gt; and the &lt;code&gt;certs&lt;/code&gt; directory are mounted with the &lt;code&gt;:ro&lt;/code&gt; (Read-Only) flag.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nt"&gt;--volume&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ELK_BASE&lt;/span&gt;&lt;span class="s2"&gt;/kibana/config/kibana.yml"&lt;/span&gt;:/usr/share/kibana/config/kibana.yml:ro

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a security best practice. Even if an attacker were to gain remote code execution within the Kibana Node.js process, they would be unable to overwrite the configuration to disable security or replace the trusted certificates. The container is locked down by the host.&lt;/p&gt;

&lt;h2&gt;
  
  
  4.3 The "Startup Race" (Health Checks)
&lt;/h2&gt;

&lt;p&gt;One of the most common friction points when automating Kibana deployments is the &lt;strong&gt;Initialization Gap&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When you execute &lt;code&gt;docker run&lt;/code&gt;, the Docker daemon returns a success code almost immediately. However, inside the container, Kibana is just waking up. As a heavy Node.js application, it has a lengthy startup sequence:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It loads the configuration.&lt;/li&gt;
&lt;li&gt;It initializes plugins (APM, Maps, Graph, Security).&lt;/li&gt;
&lt;li&gt;It optimizes client-side assets (though this is faster in newer versions, it still takes time).&lt;/li&gt;
&lt;li&gt;It connects to Elasticsearch to verify the license and index templates.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This process can take anywhere from 30 to 60 seconds on a standard server.&lt;/p&gt;

&lt;p&gt;If our script were to exit immediately after the &lt;code&gt;docker run&lt;/code&gt; command, you would click the link, see a &lt;code&gt;502 Bad Gateway&lt;/code&gt; (from Nginx) or &lt;code&gt;Connection Refused&lt;/code&gt; error, and assume the deployment failed. You might start digging into logs or restarting containers unnecessarily, interrupting a process that was actually working fine.&lt;/p&gt;

&lt;p&gt;To solve this, we implement a &lt;strong&gt;State-Aware Healthcheck&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We do not simply use &lt;code&gt;sleep 60&lt;/code&gt;. Hardcoded sleeps are "brittle"—on a fast machine, you waste 30 seconds; on a slow machine, it's not enough. Instead, we poll the application itself.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;STATUS_OUTPUT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$KIBANA_URL&lt;/span&gt;&lt;span class="s2"&gt;/api/status"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Kibana Status API returns a rich JSON object detailing the state of every plugin. Crucially, it provides an overall status field. We grep specifically for:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;"level":"available"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This is the only state that matters.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the status is &lt;strong&gt;Red&lt;/strong&gt;, Kibana is initializing or cannot reach Elasticsearch.&lt;/li&gt;
&lt;li&gt;If the status is &lt;strong&gt;Yellow&lt;/strong&gt;, Kibana is migrating saved objects or indices.&lt;/li&gt;
&lt;li&gt;If the status is &lt;strong&gt;Green&lt;/strong&gt; (Available), the UI is ready to accept user traffic.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By blocking the script until this specific string appears, we guarantee that when the script says "Green and Available," the link we provide will actually load the login page. This transforms the deployment experience from "Run and Pray" to "Run and Verify."&lt;/p&gt;

&lt;h2&gt;
  
  
  4.4 Security &amp;amp; Access
&lt;/h2&gt;

&lt;p&gt;Finally, we must address the network exposure of our interface. You will notice the port mapping in our script is explicit:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;--publish 127.0.0.1:5601:5601&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We are binding strictly to the &lt;strong&gt;Loopback Interface&lt;/strong&gt; (&lt;code&gt;127.0.0.1&lt;/code&gt;), not the &lt;strong&gt;Wildcard Interface&lt;/strong&gt; (&lt;code&gt;0.0.0.0&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;This is a deliberate architectural constraint. In a "Zero Trust" model, we never expose management interfaces directly to the network.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Risk:&lt;/strong&gt; If we bound to &lt;code&gt;0.0.0.0&lt;/code&gt;, anyone on the same Wi-Fi or subnet could navigate to &lt;code&gt;http://&amp;lt;your-ip&amp;gt;:5601&lt;/code&gt;. While they would still face the login screen (thanks to our password setup), we prefer to hide the door entirely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Access Path:&lt;/strong&gt; By locking it to localhost, the only way to access Kibana is from the machine itself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For Setup:&lt;/strong&gt; We can use &lt;code&gt;curl&lt;/code&gt; on the host (as our healthcheck does).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For Browsing:&lt;/strong&gt; In a real-world production setup, we would place an Nginx Reverse Proxy in front of this container (listening on port 443 with SSL) and route traffic internally to 127.0.0.1:5601. Alternatively, as administrators, we can use an SSH Tunnel (&lt;code&gt;ssh -L 5601:localhost:5601 user@server&lt;/code&gt;) to securely bridge our laptop to the server.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This restriction ensures that during the vulnerable setup phase—before we have configured complex firewall rules or VPNs—our visualization tool is effectively invisible to the outside world. We are building a "Dark" control center: fully functional, but only accessible to those with the keys to the host.&lt;/p&gt;

&lt;h1&gt;
  
  
  Chapter 5: The Parsing Engine – Ingest Pipelines
&lt;/h1&gt;

&lt;h2&gt;
  
  
  5.1 The Pipeline Script
&lt;/h2&gt;

&lt;p&gt;We have a database, and we have a UI. But currently, our logs are just messy strings of text like &lt;code&gt;[INFO] 2025-12-15 Build Started&lt;/code&gt;. If we send these directly to Elasticsearch, they will be stored as a single blob of text. You won't be able to filter by "Error Level" or "Client IP" because the database doesn't know those fields exist yet.&lt;/p&gt;

&lt;p&gt;In the traditional ELK stack, a tool called &lt;strong&gt;Logstash&lt;/strong&gt; would sit in the middle to parse this text. However, Logstash is resource-heavy. Since we are optimizing for a lean "Factory in a Box," we will use &lt;strong&gt;Elasticsearch Ingest Pipelines&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This feature allows us to define a chain of "Processors" inside the database itself. When a log document arrives, Elasticsearch runs it through this chain—extracting fields, fixing timestamps, and removing noise—milliseconds before writing it to disk.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;04-setup-pipelines.sh&lt;/code&gt; with the following content. This script defines a single pipeline called &lt;code&gt;cicd-logs&lt;/code&gt; that acts as a "Universal Adapter" for every service in our stack.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;#           04-setup-pipelines.sh&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  Configures Elasticsearch Ingest Pipelines.&lt;/span&gt;
&lt;span class="c"&gt;#  Defines parsing logic for all CICD stack components.&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🚀 Configuring Elasticsearch Ingest Pipelines..."&lt;/span&gt;

&lt;span class="c"&gt;# --- 1. Load Secrets ---&lt;/span&gt;
&lt;span class="nv"&gt;HOST_CICD_ROOT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/cicd_stack"&lt;/span&gt;
&lt;span class="nv"&gt;ELK_BASE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOST_CICD_ROOT&lt;/span&gt;&lt;span class="s2"&gt;/elk"&lt;/span&gt;
&lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ELK_BASE&lt;/span&gt;&lt;span class="s2"&gt;/filebeat/filebeat.env"&lt;/span&gt;

&lt;span class="c"&gt;# --- 2. Define the Pipeline JSON ---&lt;/span&gt;
&lt;span class="c"&gt;# CRITICAL: We use 'EOF' (quoted) to prevent Bash from stripping regex backslashes.&lt;/span&gt;

&lt;span class="nv"&gt;PIPELINE_JSON&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
{
  "description": "CICD Stack Log Parsing Pipeline",
  "processors": [
    {
      "set": {
        "field": "event.original",
        "value": "{{message}}",
        "ignore_empty_value": true
      }
    },
    {
      "grok": {
        "field": "message",
        "patterns": [
          "&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="sh"&gt;[%{TIMESTAMP_ISO8601:timestamp}&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="sh"&gt;] &lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="sh"&gt;[%{LOGLEVEL:log.level}&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="sh"&gt;] %{DATA:logger_name} - %{GREEDYDATA:message}"
        ],
        "if": "ctx.service_name == 'jenkins'",
        "ignore_missing": true,
        "ignore_failure": true
      }
    },
    {
      "grok": {
        "field": "message",
        "patterns": [
          "%{IPORHOST:client.ip} - %{DATA:user.name} &lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="sh"&gt;[%{HTTPDATE:timestamp}&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="sh"&gt;] &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="sh"&gt;%{WORD:http.request.method} %{DATA:url.path} HTTP/%{NUMBER:http.version}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="sh"&gt; %{NUMBER:http.response.status_code:long} %{NUMBER:http.response.body.bytes:long} &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="sh"&gt;%{DATA:http.request.referrer}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="sh"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="sh"&gt;%{DATA:user_agent.original}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="sh"&gt;"
        ],
        "if": "ctx.service_name == 'gitlab-nginx'",
        "ignore_missing": true,
        "ignore_failure": true
      }
    },
    {
      "grok": {
        "field": "message",
        "patterns": [
          "%{YEAR:year}&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="sh"&gt;.%{MONTHNUM:month}&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="sh"&gt;.%{MONTHDAY:day} %{TIME:time} %{LOGLEVEL:log.level}&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="sh"&gt;s+%{GREEDYDATA:message}"
        ],
        "if": "ctx.service_name == 'sonarqube'",
        "ignore_missing": true,
        "ignore_failure": true
      }
    },
    {
      "script": {
        "lang": "painless",
        "source": "ctx.timestamp = ctx.year + '-' + ctx.month + '-' + ctx.day + 'T' + ctx.time + 'Z'",
        "if": "ctx.service_name == 'sonarqube' &amp;amp;&amp;amp; ctx.year != null"
      }
    },
    {
      "json": {
        "field": "message",
        "target_field": "mattermost",
        "if": "ctx.service_name == 'mattermost'",
        "ignore_failure": true
      }
    },
    {
      "set": {
        "field": "timestamp",
        "value": "{{mattermost.timestamp}}",
        "if": "ctx.service_name == 'mattermost' &amp;amp;&amp;amp; ctx.mattermost?.timestamp != null"
      }
    },
    {
      "set": {
        "field": "log.level",
        "value": "{{mattermost.level}}",
        "if": "ctx.service_name == 'mattermost' &amp;amp;&amp;amp; ctx.mattermost?.level != null"
      }
    },
    {
      "grok": {
        "field": "message",
        "patterns": [
          "%{TIMESTAMP_ISO8601:timestamp} &lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="sh"&gt;[%{DATA:service_type}&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="sh"&gt;s*&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="sh"&gt;] &lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="sh"&gt;[%{LOGLEVEL:log.level}&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="sh"&gt;s*&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="sh"&gt;] &lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="sh"&gt;[%{DATA:trace_id}&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="sh"&gt;] &lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="sh"&gt;[%{DATA:class}:%{NUMBER:line}&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="sh"&gt;] (?&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="sh"&gt;[%{DATA:thread_info}&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="sh"&gt;] )?- %{GREEDYDATA:message}",
          "%{TIMESTAMP_ISO8601:timestamp}&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="sh"&gt;|%{DATA:trace_id}&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="sh"&gt;|%{IP:client.ip}&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="sh"&gt;|%{DATA:user.name}&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="sh"&gt;|%{WORD:http.request.method}&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="sh"&gt;|%{DATA:url.path}&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="sh"&gt;|%{NUMBER:http.response.status_code:long}&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="sh"&gt;|.*"
        ],
        "if": "ctx.service_name == 'artifactory'",
        "ignore_missing": true,
        "ignore_failure": true
      }
    },
    {
      "date": {
        "field": "timestamp",
        "formats": [
          "ISO8601",
          "dd/MMM/yyyy:HH:mm:ss Z",
          "yyyy-MM-dd HH:mm:ss.SSS",
          "yyyy-MM-dd HH:mm:ss.SSS Z",
          "yyyy-MM-dd HH:mm:ss Z",
          "MMM  d HH:mm:ss",
          "MMM dd HH:mm:ss"
        ],
        "target_field": "@timestamp",
        "ignore_failure": true
      }
    },
    {
      "remove": {
        "field": ["timestamp", "year", "month", "day", "time", "mattermost"],
        "ignore_missing": true
      }
    }
  ],
  "on_failure": [
    {
      "set": {
        "field": "error.message",
        "value": "Pipeline failed: {{ _ingest.on_failure_message }}"
      }
    }
  ]
}
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# --- 3. Upload to Elasticsearch ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Uploading 'cicd-logs' Pipeline ---"&lt;/span&gt;

&lt;span class="nv"&gt;RESPONSE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-k&lt;/span&gt; &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;%{http_code}"&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; PUT &lt;span class="s2"&gt;"https://127.0.0.1:9200/_ingest/pipeline/cicd-logs"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="s2"&gt;"elastic:&lt;/span&gt;&lt;span class="nv"&gt;$ELASTIC_PASSWORD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PIPELINE_JSON&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nv"&gt;HTTP_BODY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$RESPONSE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'$d'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;HTTP_STATUS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$RESPONSE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-n1&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HTTP_STATUS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-eq&lt;/span&gt; 200 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✅ Pipeline updated successfully (HTTP 200)."&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Response: &lt;/span&gt;&lt;span class="nv"&gt;$HTTP_BODY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;else
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"❌ Error uploading pipeline (HTTP &lt;/span&gt;&lt;span class="nv"&gt;$HTTP_STATUS&lt;/span&gt;&lt;span class="s2"&gt;)."&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Elasticsearch Response:"&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HTTP_BODY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5.2 The Pipeline Blueprint (JSON Reference)
&lt;/h2&gt;

&lt;p&gt;This JSON object is the "Source Code" for our logging logic. It defines a sequential list of steps that every log entry must pass through. Let's break down the critical components of this file so you can understand exactly how we transform chaos into order.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. The Safety Net (&lt;code&gt;event.original&lt;/code&gt;)&lt;/strong&gt;&lt;br&gt;
The first processor is a &lt;code&gt;set&lt;/code&gt; operation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"set"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"field"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"event.original"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{{message}}"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before we touch anything, we copy the raw log line into a new field called &lt;code&gt;event.original&lt;/code&gt;. If our complex parsing logic fails or corrupts the data later in the pipeline, we still have the original, untouched text safe and sound.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The Conditional Switch (&lt;code&gt;if&lt;/code&gt; statements)&lt;/strong&gt;&lt;br&gt;
Notice that almost every processor has an &lt;code&gt;if&lt;/code&gt; condition attached to it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"if"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ctx.service_name == 'jenkins'"&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is our routing logic. Filebeat tags every log with a &lt;code&gt;service_name&lt;/code&gt; (e.g., "jenkins", "gitlab-nginx") before shipping it. The pipeline checks this tag. If the log is from Jenkins, it runs the Jenkins Grok pattern; if it's from Nginx, it skips to the Nginx pattern. This allows us to use a &lt;strong&gt;Single Pipeline&lt;/strong&gt; for the entire stack, rather than managing ten small ones.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The Parser (&lt;code&gt;grok&lt;/code&gt;)&lt;/strong&gt;&lt;br&gt;
Grok is the heavy lifter. It matches the raw text against predefined patterns and extracts structured variables.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pattern:&lt;/strong&gt; &lt;code&gt;%{IPORHOST:client.ip}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Translation:&lt;/strong&gt; "Find a string that looks like an IP address or Hostname. Extract it and save it into the field &lt;code&gt;client.ip&lt;/code&gt;."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Result:&lt;/strong&gt; We can now build a map in Kibana showing where users are logging in from, because &lt;code&gt;client.ip&lt;/code&gt; is a real data field, not just text.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. The Script (&lt;code&gt;painless&lt;/code&gt;)&lt;/strong&gt;&lt;br&gt;
Sometimes, standard parsers aren't enough. SonarQube logs date and time in two separate fields (&lt;code&gt;2025.12.15&lt;/code&gt; and &lt;code&gt;10:00:00&lt;/code&gt;), but Elasticsearch needs a single ISO8601 string.&lt;br&gt;
We use a tiny script written in &lt;strong&gt;Painless&lt;/strong&gt; (Elastic's secure scripting language) to stitch them together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;year&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sc"&gt;'-'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;month&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sc"&gt;'-'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;day&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sc"&gt;'T'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;time&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sc"&gt;'Z'&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This demonstrates the power of Ingest Pipelines: we can execute real programming logic inside the database to clean our data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. The Cleanup (&lt;code&gt;remove&lt;/code&gt;)&lt;/strong&gt;&lt;br&gt;
Once we have extracted &lt;code&gt;client.ip&lt;/code&gt;, &lt;code&gt;http.status&lt;/code&gt;, and &lt;code&gt;@timestamp&lt;/code&gt;, the intermediate fields (like &lt;code&gt;year&lt;/code&gt;, &lt;code&gt;month&lt;/code&gt;, &lt;code&gt;mattermost&lt;/code&gt; JSON objects) are just wasted disk space. The final step &lt;code&gt;remove&lt;/code&gt; deletes them, keeping our index lean.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Error Handling (&lt;code&gt;on_failure&lt;/code&gt;)&lt;/strong&gt;&lt;br&gt;
At the very bottom, we define an &lt;code&gt;on_failure&lt;/code&gt; block. If a log line is malformed and causes a processor to crash, the pipeline does &lt;strong&gt;not&lt;/strong&gt; discard the log. Instead, it catches the exception and adds an &lt;code&gt;error.message&lt;/code&gt; field. This ensures we never lose data, even if our code has bugs.&lt;/p&gt;
&lt;h2&gt;
  
  
  5.3 Decoding the Services
&lt;/h2&gt;

&lt;p&gt;Now we will walk through the specific parsing logic for each service. This is where we bridge the gap between the application's unique "accent" and the database's strict requirements.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;1. Jenkins (The Custom Formatter)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;In &lt;strong&gt;Chapter 2&lt;/strong&gt;, we forced Jenkins to use a custom Java logging format:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;[%1$tF %1$tT] [%4$s] %3$s - %5$s %6$s%n&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This results in log lines that look like this:&lt;br&gt;
&lt;code&gt;[2025-12-15 14:30:00] [INFO] hudson.plugins.git.GitSCM - Fetching changes...&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Our Grok pattern is a direct mirror of this structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;[%{TIMESTAMP_ISO8601:timestamp}&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;] &lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;[%{LOGLEVEL:log.level}&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;] %{DATA:logger_name} - %{GREEDYDATA:message}"&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;\\[ ... \\]&lt;/code&gt;: We escape the brackets because they are special characters in Regex.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;%{TIMESTAMP_ISO8601:timestamp}&lt;/code&gt;: Captures the &lt;code&gt;2025-12-15 14:30:00&lt;/code&gt; part.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;%{LOGLEVEL:log.level}&lt;/code&gt;: Captures &lt;code&gt;INFO&lt;/code&gt;, &lt;code&gt;WARNING&lt;/code&gt;, or &lt;code&gt;SEVERE&lt;/code&gt;. By mapping this to &lt;code&gt;log.level&lt;/code&gt;, Kibana will automatically color-code these rows (Red for Error, Yellow for Warning).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;2. GitLab Nginx (Standard Web Traffic)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;GitLab's internal Nginx uses the industry-standard "Combined Log Format."&lt;/p&gt;

&lt;p&gt;&lt;code&gt;192.168.1.50 - - [15/Dec/2025:14:30:00 +0000] "GET /api/v4/projects HTTP/1.1" 200 450 ...&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Our Grok pattern extracts the critical metrics for our security dashboard:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="s2"&gt;"%{IPORHOST:client.ip} - %{DATA:user.name} &lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;[%{HTTPDATE:timestamp}&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;] ..."&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;%{IPORHOST:client.ip}&lt;/code&gt;: This is the most valuable field. It allows us to build the "Intruder Alert" map.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;%{NUMBER:http.response.status_code:long}&lt;/code&gt;: We explicitly cast this to a &lt;code&gt;long&lt;/code&gt; (integer). If we left it as a string, we wouldn't be able to run math aggregations like "Average Response Code" or "Count of 5xx Errors."&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;3. SonarQube (The Format Outlier)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;SonarQube is difficult. It logs date and time separated by a space, using dots for the date:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;2025.12.15 14:30:00 INFO  web[][o.s.s.p.Platform] ...&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;A standard Grok pattern can extract &lt;code&gt;2025.12.15&lt;/code&gt; as &lt;code&gt;year&lt;/code&gt; and &lt;code&gt;14:30:00&lt;/code&gt; as &lt;code&gt;time&lt;/code&gt;, but Elasticsearch requires a single ISO string for time-based indexing.&lt;/p&gt;

&lt;p&gt;We solve this with a two-step combo:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Grok:&lt;/strong&gt; Extract the parts (&lt;code&gt;year&lt;/code&gt;, &lt;code&gt;month&lt;/code&gt;, &lt;code&gt;day&lt;/code&gt;, &lt;code&gt;time&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Painless Script:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;year&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sc"&gt;'-'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;month&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sc"&gt;'-'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;day&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sc"&gt;'T'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;time&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sc"&gt;'Z'&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This script manually constructs a valid ISO string (&lt;code&gt;2025-12-15T14:30:00Z&lt;/code&gt;) which the Date Processor can then understand.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;4. Mattermost (Structure-Native)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Mattermost is modern; it logs in JSON format by default.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;{"timestamp": "2025-12-15 14:30:00", "level": "error", "msg": "Database timeout"}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We don't need Grok (Regex) here. We use the &lt;strong&gt;JSON Processor&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"json"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"field"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"target_field"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mattermost"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tells Elasticsearch: "The &lt;code&gt;message&lt;/code&gt; field contains a JSON string. Parse it and put the resulting object into a new field called &lt;code&gt;mattermost&lt;/code&gt;."&lt;br&gt;
We then use &lt;code&gt;set&lt;/code&gt; processors to promote &lt;code&gt;mattermost.timestamp&lt;/code&gt; and &lt;code&gt;mattermost.level&lt;/code&gt; to the top-level &lt;code&gt;@timestamp&lt;/code&gt; and &lt;code&gt;log.level&lt;/code&gt; fields, ensuring consistency with Jenkins and Nginx.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;5. Artifactory (The Polyglot)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Artifactory is complex because it generates two distinct types of logs in the same stream:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Service Logs:&lt;/strong&gt; Java application errors (standard stack traces).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Request Logs:&lt;/strong&gt; Pipe-separated values (&lt;code&gt;|&lt;/code&gt;) tracking file downloads.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We handle this using a &lt;strong&gt;Grok Array&lt;/strong&gt;. We provide multiple patterns to the processor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"patterns"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="s2"&gt;"...Service Log Pattern..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="s2"&gt;"...Request Log Pattern..."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Elasticsearch tries the first pattern. If it fails, it tries the second. This allows a single pipeline to handle both "Application Crashed" (Pattern A) and "User downloaded generic-local/app.jar" (Pattern B) seamlessly.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;6. The Rosetta Stone: Date Normalization&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Finally, the pipeline ends with the &lt;strong&gt;Date Processor&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"formats"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ISO8601"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dd/MMM/yyyy:HH:mm:ss Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"yyyy-MM-dd HH:mm:ss.SSS"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the most critical step. Jenkins says &lt;code&gt;2025-12-15&lt;/code&gt;, Nginx says &lt;code&gt;15/Dec/2025&lt;/code&gt;. If we simply indexed these as strings, sorting by time would be impossible.&lt;br&gt;
This processor takes the &lt;code&gt;timestamp&lt;/code&gt; field we extracted from any of the services above, matches it against &lt;em&gt;any&lt;/em&gt; of the allowed formats, and converts it to the sacred &lt;code&gt;@timestamp&lt;/code&gt; field in UTC. This ensures that when you zoom in on "14:30" in Kibana, you see the events from Jenkins, Nginx, and SonarQube perfectly aligned.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;7. A Note on System Logs (The Pass-Through)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;You might notice that our &lt;strong&gt;System Logs&lt;/strong&gt; (Journald) are missing from the pipeline's logic. We have defined rules for Jenkins, Nginx, and SonarQube, but there is no &lt;code&gt;if "ctx.service_name == 'system'"&lt;/code&gt; block.&lt;/p&gt;

&lt;p&gt;This is intentional.&lt;/p&gt;

&lt;p&gt;Unlike the flat text files generated by Jenkins or Nginx, the Linux Journal (&lt;code&gt;journald&lt;/code&gt;) is a &lt;strong&gt;structured binary format&lt;/strong&gt;. When Filebeat reads from the Journal (via the &lt;code&gt;journald&lt;/code&gt; input we configured in &lt;code&gt;filebeat.yml&lt;/code&gt;), it doesn't just read a line of text. It reads a rich object containing the timestamp, the process name, the PID, and the priority level.&lt;/p&gt;

&lt;p&gt;Filebeat automatically maps these binary fields to the Elastic Common Schema (ECS) before the data even leaves the host. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Journal &lt;code&gt;_COMM&lt;/code&gt; becomes &lt;code&gt;process.name&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Journal &lt;code&gt;PRIORITY&lt;/code&gt; becomes &lt;code&gt;syslog.priority&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Journal &lt;code&gt;__REALTIME_TIMESTAMP&lt;/code&gt; becomes &lt;code&gt;@timestamp&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because the data arrives at Elasticsearch already parsed and structured, it skips every conditional &lt;code&gt;if&lt;/code&gt; block in our pipeline. It effectively "falls through" the logic untouched, landing in the index ready to be searched. We don't need to fix what isn't broken.&lt;/p&gt;
&lt;h2&gt;
  
  
  5.4 The Deployment Mechanism
&lt;/h2&gt;

&lt;p&gt;The final part of our script handles the delivery of this logic to the database.&lt;/p&gt;

&lt;p&gt;It is important to understand that Ingest Pipelines are &lt;strong&gt;not configuration files&lt;/strong&gt;. You cannot simply copy a &lt;code&gt;.json&lt;/code&gt; file into a folder on the server and expect Elasticsearch to pick it up. Pipelines are part of the &lt;strong&gt;Cluster State&lt;/strong&gt;—they live in the memory and disk of the Master Nodes, synchronized across the cluster.&lt;/p&gt;

&lt;p&gt;To install a pipeline, we must interact with the Elasticsearch REST API.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RESPONSE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-k&lt;/span&gt; &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;%{http_code}"&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; PUT &lt;span class="s2"&gt;"https://127.0.0.1:9200/_ingest/pipeline/cicd-logs"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="s2"&gt;"elastic:&lt;/span&gt;&lt;span class="nv"&gt;$ELASTIC_PASSWORD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PIPELINE_JSON&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;1. The Method (&lt;code&gt;PUT&lt;/code&gt;)&lt;/strong&gt;&lt;br&gt;
We use the HTTP &lt;code&gt;PUT&lt;/code&gt; verb. This is idempotent; if the pipeline already exists, this command overwrites it with the new version. This is perfect for CI/CD: if we update our parsing logic later, we simply re-run this script to apply the changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The Endpoint (&lt;code&gt;_ingest/pipeline/cicd-logs&lt;/code&gt;)&lt;/strong&gt;&lt;br&gt;
We are defining a resource named &lt;code&gt;cicd-logs&lt;/code&gt;. This name is critical. Later, when we configure Filebeat (Chapter 6), we will tell it specifically to use &lt;code&gt;pipeline: "cicd-logs"&lt;/code&gt;. If these names do not match, the data will bypass our parser and arrive as raw text.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The Payload (&lt;code&gt;-d "$PIPELINE_JSON"&lt;/code&gt;)&lt;/strong&gt;&lt;br&gt;
We send the JSON object we defined earlier as the body of the request. Note that we used a Bash Heredoc (&lt;code&gt;cat &amp;lt;&amp;lt;'EOF'&lt;/code&gt;) to capture that JSON. This ensures that the complex regex backslashes inside the Grok patterns are preserved and not interpreted by the shell.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. The Verification&lt;/strong&gt;&lt;br&gt;
The script does not blindly assume success.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;HTTP_STATUS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$RESPONSE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-n1&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HTTP_STATUS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-eq&lt;/span&gt; 200 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; ...

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If our JSON syntax is invalid—for example, a missing comma or an unescaped quote—Elasticsearch will reject the request with a &lt;strong&gt;400 Bad Request&lt;/strong&gt; error and a detailed message explaining &lt;em&gt;where&lt;/em&gt; the syntax failed. Our script captures this error code and prints the server's response, allowing you to debug the JSON immediately without digging through server logs.&lt;/p&gt;

&lt;p&gt;With the pipeline registered, the "Brain" now knows how to read. It is time to deploy the "Collector" to start feeding it data.&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;strong&gt;Chapter 6: The Collector – Filebeat (&lt;code&gt;05-deploy-filebeat.sh&lt;/code&gt;)&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;This chapter focuses on the "Shipper" that connects our log files to the pipeline we just built.&lt;/p&gt;

&lt;h2&gt;
  
  
  6.1 The Collector Script
&lt;/h2&gt;

&lt;p&gt;We have the database (Elasticsearch), the interface (Kibana), and the parsing logic (Ingest Pipelines). Now we need the agent to physically transport the logs from the hard drive to the cluster.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;05-deploy-filebeat.sh&lt;/code&gt; with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;#               05-deploy-filebeat.sh&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  The "Collector" Script.&lt;/span&gt;
&lt;span class="c"&gt;#  Deploys Filebeat (v9.2.2) to ship logs to Elasticsearch.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  1. Mounts Host Docker Volumes (read app logs).&lt;/span&gt;
&lt;span class="c"&gt;#  2. Mounts Host Journal (read system logs).&lt;/span&gt;
&lt;span class="c"&gt;#  3. Mounts Data Volume (PERSIST REGISTRY).&lt;/span&gt;
&lt;span class="c"&gt;#  4. Runs as ROOT to bypass host directory permissions.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🚀 Deploying Filebeat (The Collector)..."&lt;/span&gt;

&lt;span class="c"&gt;# --- 1. Load Paths ---&lt;/span&gt;
&lt;span class="nv"&gt;HOST_CICD_ROOT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/cicd_stack"&lt;/span&gt;
&lt;span class="nv"&gt;ELK_BASE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOST_CICD_ROOT&lt;/span&gt;&lt;span class="s2"&gt;/elk"&lt;/span&gt;
&lt;span class="nv"&gt;SCOPED_ENV_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ELK_BASE&lt;/span&gt;&lt;span class="s2"&gt;/filebeat/filebeat.env"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SCOPED_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: Scoped env file not found at &lt;/span&gt;&lt;span class="nv"&gt;$SCOPED_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Please run 01-setup-elk.sh first."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# --- 2. Clean Slate ---&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;filebeat&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Stopping existing 'filebeat'..."&lt;/span&gt;
    docker stop filebeat
&lt;span class="k"&gt;fi
if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;-aq&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;filebeat&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Removing existing 'filebeat'..."&lt;/span&gt;
    docker &lt;span class="nb"&gt;rm &lt;/span&gt;filebeat
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# --- 3. Volume Management (CRITICAL FIX) ---&lt;/span&gt;
&lt;span class="c"&gt;# We must persist the registry so we don't re-ingest old logs on restart.&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Verifying filebeat-data volume..."&lt;/span&gt;
docker volume create filebeat-data &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null

&lt;span class="c"&gt;# --- 4. Deploy ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Launching Container ---"&lt;/span&gt;

&lt;span class="c"&gt;# NOTES:&lt;/span&gt;
&lt;span class="c"&gt;# - user root: Required to traverse /var/lib/docker and read Journal.&lt;/span&gt;
&lt;span class="c"&gt;# - /var/log/journal: Required for native journald input.&lt;/span&gt;
&lt;span class="c"&gt;# - /etc/machine-id: Required for journald reader to track host identity.&lt;/span&gt;

docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; filebeat &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--restart&lt;/span&gt; always &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network&lt;/span&gt; cicd-net &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--user&lt;/span&gt; root &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--env-file&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SCOPED_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ELK_BASE&lt;/span&gt;&lt;span class="s2"&gt;/filebeat/config/filebeat.yml"&lt;/span&gt;:/usr/share/filebeat/filebeat.yml:ro &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ELK_BASE&lt;/span&gt;&lt;span class="s2"&gt;/filebeat/config/certs"&lt;/span&gt;:/usr/share/filebeat/certs:ro &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; filebeat-data:/usr/share/filebeat/data &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; /var/lib/docker/volumes:/host_volumes:ro &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; /var/log/journal:/var/log/journal:ro &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; /etc/machine-id:/etc/machine-id:ro &lt;span class="se"&gt;\&lt;/span&gt;
  docker.elastic.co/beats/filebeat:9.2.2

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Container started. Verifying connection..."&lt;/span&gt;

&lt;span class="c"&gt;# --- 5. Verification ---&lt;/span&gt;
&lt;span class="nv"&gt;MAX_RETRIES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;15
&lt;span class="nv"&gt;COUNT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Waiting for Filebeat to establish connection..."&lt;/span&gt;

&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$COUNT&lt;/span&gt; &lt;span class="nt"&gt;-lt&lt;/span&gt; &lt;span class="nv"&gt;$MAX_RETRIES&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nb"&gt;sleep &lt;/span&gt;2
    &lt;span class="c"&gt;# Check for successful connection message&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;docker logs filebeat 2&amp;gt;&amp;amp;1 | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s2"&gt;"Connection to backoff.*established"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✅ Filebeat successfully connected to Elasticsearch!"&lt;/span&gt;
        &lt;span class="nb"&gt;exit &lt;/span&gt;0
    &lt;span class="k"&gt;fi&lt;/span&gt;

    &lt;span class="c"&gt;# Fail fast on Pipeline errors (common misconfiguration)&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;docker logs filebeat 2&amp;gt;&amp;amp;1 | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s2"&gt;"pipeline/cicd-logs.*missing"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"❌ ERROR: Filebeat says the 'cicd-logs' pipeline is missing!"&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Did you run 04-setup-pipelines.sh?"&lt;/span&gt;
        &lt;span class="nb"&gt;exit &lt;/span&gt;1
    &lt;span class="k"&gt;fi&lt;/span&gt;

    &lt;span class="c"&gt;# Fail fast on Certificate errors&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;docker logs filebeat 2&amp;gt;&amp;amp;1 | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s2"&gt;"x509: certificate signed by unknown authority"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"❌ ERROR: SSL Certificate trust issue."&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Check that ca.pem is correctly mounted and generated."&lt;/span&gt;
        &lt;span class="nb"&gt;exit &lt;/span&gt;1
    &lt;span class="k"&gt;fi

    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   [&lt;/span&gt;&lt;span class="nv"&gt;$COUNT&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$MAX_RETRIES&lt;/span&gt;&lt;span class="s2"&gt;] Connecting..."&lt;/span&gt;
    &lt;span class="nv"&gt;COUNT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;COUNT+1&lt;span class="k"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;done

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"⚠️  Connection check timed out. Check logs manually:"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   docker logs -f filebeat"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  6.2 The "Agent" Pattern: Host vs. Sidecar
&lt;/h2&gt;

&lt;p&gt;In the world of container observability, there are two primary ways to collect logs: the &lt;strong&gt;Sidecar Pattern&lt;/strong&gt; and the &lt;strong&gt;Host Agent Pattern&lt;/strong&gt;. It is important to understand why we have chosen the latter for this architecture.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Sidecar Pattern (The Expensive Way)&lt;/strong&gt;&lt;br&gt;
In many Kubernetes tutorials, you will see a "Sidecar" approach. This involves defining a Pod that contains &lt;em&gt;two&lt;/em&gt; containers: your application (e.g., Jenkins) and a logging agent (Filebeat). The agent shares the storage volume with the app, reads the logs, and ships them.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pros:&lt;/strong&gt; Complete isolation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cons:&lt;/strong&gt; Massive resource overhead. If you have 20 services, you run 20 instances of Filebeat. If each takes 50MB of RAM, you waste 1GB of memory just on shippers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The Host Agent Pattern (The Efficient Way)&lt;/strong&gt;&lt;br&gt;
We are using the Host Agent strategy. We run &lt;strong&gt;exactly one&lt;/strong&gt; instance of Filebeat for the entire server. This agent sits on the "metal" (conceptually) and watches the Docker storage directory from the outside.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Mechanics:&lt;/strong&gt; Docker stores named volumes at &lt;code&gt;/var/lib/docker/volumes/&lt;/code&gt; on the host Linux filesystem.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Trick:&lt;/strong&gt; By mounting this host directory into Filebeat (&lt;code&gt;--volume /var/lib/docker/volumes:/host_volumes:ro&lt;/code&gt;), our single agent can peek inside the data directories of Jenkins, GitLab, SonarQube, and Mattermost simultaneously.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach reduces our memory footprint significantly (1 agent vs. 10) and simplifies management. We have one configuration file to rule them all, rather than scattering logging configs across ten different repositories.&lt;/p&gt;
&lt;h2&gt;
  
  
  6.3 Access Control: The Root CompromiseYou will notice a controversial flag in our deployment script:
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;--user root&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In almost every security guide, running containers as &lt;code&gt;root&lt;/code&gt; is considered a vulnerability. However, for a Host Agent, it is a &lt;strong&gt;functional requirement&lt;/strong&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Permission Barrier:&lt;/strong&gt; The directory &lt;code&gt;/var/lib/docker/volumes&lt;/code&gt; is owned by &lt;code&gt;root:root&lt;/code&gt; with strict permissions (typically &lt;code&gt;700&lt;/code&gt; or &lt;code&gt;750&lt;/code&gt;). If we ran Filebeat as the default &lt;code&gt;filebeat&lt;/code&gt; user (UID 1000), it would be denied access immediately. It physically cannot enter the directory to read the log files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Mitigation (Read-Only):&lt;/strong&gt; To balance the risk of running as root, we use the Docker &lt;strong&gt;Read-Only&lt;/strong&gt; flag (&lt;code&gt;:ro&lt;/code&gt;) on the volume mount:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nt"&gt;--volume&lt;/span&gt; /var/lib/docker/volumes:/host_volumes:ro

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This imposes a hard limit at the filesystem level. Even if the Filebeat process were hijacked by an attacker, they could &lt;em&gt;read&lt;/em&gt; your logs (confidentiality risk), but they could not &lt;em&gt;delete&lt;/em&gt; your data or &lt;em&gt;inject&lt;/em&gt; malicious files into your application volumes (integrity risk).&lt;/p&gt;

&lt;p&gt;This is the standard privilege model for infrastructure monitoring: the observer must have higher privileges than the observed, but we strip its ability to modify the world it watches.&lt;/p&gt;
&lt;h2&gt;
  
  
  6.4 The Memory of the Elephant: Persistent Registry
&lt;/h2&gt;

&lt;p&gt;One of the most dangerous pitfalls in deploying Filebeat is failing to persist its "Registry."&lt;/p&gt;

&lt;p&gt;Filebeat maintains a small internal database called the &lt;strong&gt;Registry&lt;/strong&gt;. This file records exactly how far it has read into every log file it tracks. It stores the unique &lt;code&gt;inode&lt;/code&gt; of the file and the byte &lt;code&gt;offset&lt;/code&gt; (e.g., "I have read 10,240 bytes of &lt;code&gt;jenkins.log&lt;/code&gt;").&lt;/p&gt;

&lt;p&gt;In our script, we explicitly handle this state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker volume create filebeat-data
...
&lt;span class="nt"&gt;--volume&lt;/span&gt; filebeat-data:/usr/share/filebeat/data

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Scenario Without Persistence:&lt;/strong&gt;&lt;br&gt;
Imagine we did &lt;em&gt;not&lt;/em&gt; map this volume.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Filebeat starts, reads 5,000 lines of Jenkins logs, and ships them to Elasticsearch.&lt;/li&gt;
&lt;li&gt;You restart the Filebeat container to change a config.&lt;/li&gt;
&lt;li&gt;The new container starts with a fresh, empty Registry.&lt;/li&gt;
&lt;li&gt;It looks at &lt;code&gt;jenkins.log&lt;/code&gt;, sees 5,000 lines, and thinks, "A new file! I must read this from the beginning."&lt;/li&gt;
&lt;li&gt;It re-ships all 5,000 lines. You now have duplicates of every single event in your database.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By mounting the &lt;code&gt;data&lt;/code&gt; directory to a named Docker volume, we ensure that Filebeat's brain survives a restart. It wakes up, checks the disk, sees it has already processed those 5,000 lines, and waits patiently for line 5,001.&lt;/p&gt;
&lt;h2&gt;
  
  
  6.5 System Visibility (Journald)
&lt;/h2&gt;

&lt;p&gt;Finally, we address the "Blind Spot" of standard Docker logging.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;docker logs&lt;/code&gt; only captures what an application writes to STDOUT (Standard Output). It does &lt;strong&gt;not&lt;/strong&gt; capture what happens to the container itself from the Operating System's perspective.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Scenario:&lt;/strong&gt; Your Jenkins server is under heavy load. It consumes all available RAM. The Linux Kernel's "Out of Memory" (OOM) Killer steps in and terminates the process to save the system.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Result:&lt;/strong&gt; Jenkins is dead. If you look at the Jenkins logs, they just stop. There is no error message because the process was killed before it could write one. You are left guessing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To solve this, we mount the Host System Journal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nt"&gt;--volume&lt;/span&gt; /var/log/journal:/var/log/journal:ro &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--volume&lt;/span&gt; /etc/machine-id:/etc/machine-id:ro

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;/var/log/journal&lt;/code&gt;&lt;/strong&gt;: This is where modern Linux systems (Ubuntu, CentOS, Debian) store system-level logs in a binary format. Filebeat reads this directly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;/etc/machine-id&lt;/code&gt;&lt;/strong&gt;: This is required for the reader to correctly associate the journal entries with the specific host machine.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By ingesting this stream, we can see the "Meta-Events." In our dashboard, we will be able to correlate a sudden stop in Jenkins logs with a simultaneous &lt;code&gt;kernel: Out of memory: Kill process 1234 (java)&lt;/code&gt; event from the system log. This context is often the difference between a 5-minute fix and a 5-hour debugging session.&lt;/p&gt;

&lt;h1&gt;
  
  
  Chapter 7: The Discovery – Verification
&lt;/h1&gt;

&lt;h2&gt;
  
  
  7.1 First Contact (The Setup)
&lt;/h2&gt;

&lt;p&gt;The scripts have finished running. The containers are green. Now comes the moment of truth: seeing your data.&lt;/p&gt;

&lt;p&gt;Before we open the browser, we must ensure your computer knows how to find the services. Our scripts used specific hostnames (&lt;code&gt;kibana.cicd.local&lt;/code&gt;) which makes handling SSL certificates and networking much cleaner than using raw IP addresses.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Update Your Hosts File&lt;/strong&gt;&lt;br&gt;
On your host machine (the one running the browser), open your &lt;code&gt;/etc/hosts&lt;/code&gt; file (or &lt;code&gt;C:\Windows\System32\drivers\etc\hosts&lt;/code&gt; on Windows) and add the following line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;127.0.0.1  elasticsearch.cicd.local kibana.cicd.local

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. The Login&lt;/strong&gt;&lt;br&gt;
Navigate to &lt;strong&gt;&lt;code&gt;https://kibana.cicd.local:5601&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You will be greeted by the Elastic login screen.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Username:&lt;/strong&gt; &lt;code&gt;elastic&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Password:&lt;/strong&gt; The value you generated in &lt;code&gt;cicd.env&lt;/code&gt; (look for &lt;code&gt;ELASTIC_PASSWORD&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Creating the Data View&lt;/strong&gt;&lt;br&gt;
When you first log in, Kibana is "blind." It knows it is connected to a database, but it doesn't know &lt;em&gt;which&lt;/em&gt; tables (indices) you care about. We need to define a &lt;strong&gt;Data View&lt;/strong&gt; (formerly known as an Index Pattern).&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the main menu (hamburger icon) and go to &lt;strong&gt;Stack Management &amp;gt; Data Views&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Create data view&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Name:&lt;/strong&gt; Give it a friendly name like &lt;code&gt;CICD Logs&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Index pattern:&lt;/strong&gt; Enter &lt;code&gt;filebeat-*&lt;/code&gt;.

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Why?&lt;/em&gt; Filebeat creates a new index every day (e.g., &lt;code&gt;filebeat-9.2.2-2025.12.17&lt;/code&gt;). By using the wildcard &lt;code&gt;*&lt;/code&gt;, we tell Kibana to look at &lt;em&gt;all&lt;/em&gt; existing and future Filebeat indices.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Confirmation:&lt;/em&gt; You should see a success message on the right listing the matching indices (e.g., &lt;code&gt;filebeat-9.2.2-2025...&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timestamp field:&lt;/strong&gt; Select &lt;code&gt;@timestamp&lt;/code&gt;.

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Critical:&lt;/em&gt; This tells Kibana which field represents "Time." If you pick the wrong field, your time-series charts will be broken.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Save data view to Kibana&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Kibana now has a lens through which it can see your data.&lt;/p&gt;
&lt;h2&gt;
  
  
  7.2 Verifying the Pipeline (The "Discover" Tab)
&lt;/h2&gt;

&lt;p&gt;Now that Kibana can see the data, let's verify that our &lt;strong&gt;Ingest Pipeline&lt;/strong&gt; (Chapter 5) is actually working. If the pipeline failed, we would see a giant block of text in the &lt;code&gt;message&lt;/code&gt; field and nothing else. If it succeeded, that text should be exploded into useful fields.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click on the &lt;strong&gt;Discover&lt;/strong&gt; icon (compass) in the left sidebar.&lt;/li&gt;
&lt;li&gt;Ensure the date picker (top right) is set to &lt;strong&gt;"Today"&lt;/strong&gt; or &lt;strong&gt;"Last 15 minutes"&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;In the search bar, type &lt;code&gt;service_name: "gitlab-nginx"&lt;/code&gt; and hit Enter.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You should see a list of log events. Click on the small arrow &lt;code&gt;&amp;gt;&lt;/code&gt; next to any document to expand it into Table/JSON view.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The "Anatomy of a Log"&lt;/strong&gt;&lt;br&gt;
Look at the JSON structure (like the example below). This is the proof of success:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;event.original&lt;/code&gt;&lt;/strong&gt;: This contains the raw, ugly text: &lt;code&gt;172.30.0.5 - - [17/Dec/2025...]&lt;/code&gt;. This is our safety net.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;client.ip&lt;/code&gt;&lt;/strong&gt;: The pipeline successfully extracted &lt;code&gt;172.30.0.5&lt;/code&gt;. This is now a searchable IP address, not just text.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;http.response.status_code&lt;/code&gt;&lt;/strong&gt;: It extracted &lt;code&gt;404&lt;/code&gt;. Because we cast this to a &lt;code&gt;long&lt;/code&gt; in our pipeline, we can now build charts aggregating "Top Error Codes."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@timestamp&lt;/code&gt;&lt;/strong&gt;: Notice the time is &lt;code&gt;2025-12-17T09:51:49.000Z&lt;/code&gt;. The pipeline took the Nginx format (&lt;code&gt;17/Dec/2025...&lt;/code&gt;) and standardized it to UTC ISO8601.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you see these fields, your "Brain" is correctly parsing the "Voice" of your infrastructure.&lt;/p&gt;
&lt;h2&gt;
  
  
  7.3 Troubleshooting Common Issues
&lt;/h2&gt;

&lt;p&gt;If you navigate to Discover and see... &lt;strong&gt;nothing&lt;/strong&gt;, don't panic. This is the "No Data" state, and it usually has three simple causes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. The Time Trap&lt;/strong&gt;&lt;br&gt;
Kibana defaults to "Last 15 minutes." If your logs are from an hour ago (or if your server time is drifting), you won't see them.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Fix:&lt;/em&gt; Set the time picker to &lt;strong&gt;"Last 24 hours"&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. The Connection Gap&lt;/strong&gt;&lt;br&gt;
If Filebeat can't reach Elasticsearch, no data arrives.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Fix:&lt;/em&gt; Check the Filebeat logs: &lt;code&gt;docker logs filebeat&lt;/code&gt;. Look for &lt;code&gt;Connection refused&lt;/code&gt;. If you see this, check that Elasticsearch is healthy (&lt;code&gt;docker ps&lt;/code&gt;) and that you are using the correct password.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. The Pipeline Reject&lt;/strong&gt;&lt;br&gt;
If your pipeline has a syntax error, Elasticsearch might reject the logs entirely.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Fix:&lt;/em&gt; Look at the Filebeat logs again. If you see &lt;code&gt;400 Bad Request&lt;/code&gt; or &lt;code&gt;pipeline [cicd-logs] missing&lt;/code&gt;, it means the data made it to the door but was turned away. Re-run &lt;code&gt;04-setup-pipelines.sh&lt;/code&gt; to ensure the logic is loaded.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;
  
  
  Chapter 8: The Dashboard – Visualization
&lt;/h1&gt;
&lt;h2&gt;
  
  
  8.1 System Health (The Blame Wheel)
&lt;/h2&gt;

&lt;p&gt;Raw logs are useful for debugging, but they are terrible for monitoring. You cannot scroll through thousands of lines of text to guess if the server is healthy. We need a visual signal—a "Traffic Light" that turns Red when things are wrong.&lt;/p&gt;

&lt;p&gt;We will build a &lt;strong&gt;Pie Chart&lt;/strong&gt; to visualize the distribution of errors across our stack.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the main menu and go to &lt;strong&gt;Dashboard&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Create dashboard&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Create visualization&lt;/strong&gt; (This opens the "Lens" editor).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configure the Chart:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;View:&lt;/strong&gt; Select &lt;strong&gt;Pie&lt;/strong&gt; from the chart type dropdown.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Metric (Slice Size):&lt;/strong&gt; &lt;code&gt;Count of records&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Slice by:&lt;/strong&gt; Drag the field &lt;code&gt;service_name.keyword&lt;/code&gt; onto the "Slices" area.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The "Unified" Query:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We have a challenge: Application logs use text levels (&lt;code&gt;ERROR&lt;/code&gt;, &lt;code&gt;WARN&lt;/code&gt;), but System logs (Journald) use numbers (&lt;code&gt;priority 3&lt;/code&gt;, &lt;code&gt;priority 4&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;In the Search bar at the top, paste this &lt;strong&gt;KQL (Kibana Query Language)&lt;/strong&gt; string:
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;log.level.keyword: ("WARN" or "error" or "WARNING") or log.syslog.priority &amp;lt;= 4
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;This query captures &lt;em&gt;both&lt;/em&gt; types of failures in a single view.

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Save:&lt;/strong&gt; Click "Save and return" and name it &lt;strong&gt;"System Health Status"&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The Value:&lt;/strong&gt;&lt;br&gt;
If this chart is empty or shows only small slices, you are fine. If you see a massive slice labeled &lt;code&gt;jenkins&lt;/code&gt;, you know exactly which service is screaming, without reading a single log line.&lt;/p&gt;
&lt;h2&gt;
  
  
  8.2 Intruder Alert (The Security Radar)
&lt;/h2&gt;

&lt;p&gt;Next, we need to secure our perimeter. We want to see if anyone is scanning our Nginx proxy or trying to brute-force URLs. We will use a &lt;strong&gt;Stacked Bar Chart&lt;/strong&gt; to show HTTP response codes over time.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;On your dashboard, click &lt;strong&gt;Create visualization&lt;/strong&gt; again.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configure the Chart:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;View:&lt;/strong&gt; Select &lt;strong&gt;Bar&lt;/strong&gt; (Stacked).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Horizontal Axis:&lt;/strong&gt; Drag &lt;code&gt;@timestamp&lt;/code&gt; here. Kibana will automatically group data into buckets (e.g., "per minute").&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vertical Axis:&lt;/strong&gt; &lt;code&gt;Count of records&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Break down by:&lt;/strong&gt; Drag &lt;code&gt;http.response.status_code&lt;/code&gt; here.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add Filters:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;In the search bar, add: &lt;code&gt;service_name: "gitlab-nginx"&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;We want to highlight specific anomalies. Click the &lt;strong&gt;Plus (+)&lt;/strong&gt; next to the Query bar to add explicit filters for interesting codes: &lt;code&gt;403&lt;/code&gt; (Forbidden), &lt;code&gt;502&lt;/code&gt; (Bad Gateway), and &lt;code&gt;302&lt;/code&gt; (Redirects/Logins).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Save:&lt;/strong&gt; Click "Save and return" and name it &lt;strong&gt;"GitLab Access Patterns"&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The Value:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A spike in 302:&lt;/strong&gt; Someone is hammering the login page (Brute Force).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A spike in 403:&lt;/strong&gt; Someone is scanning for secrets or unauthorized paths.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A spike in 502:&lt;/strong&gt; Your GitLab container has likely crashed behind the proxy.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  8.3 Factory Pulse (Build Frequency)
&lt;/h2&gt;

&lt;p&gt;Finally, we want to see the "Heartbeat" of our factory. We don't want to count logs; we want to count &lt;em&gt;work&lt;/em&gt;. We will track how often Jenkins provisions a new agent to run a build.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click &lt;strong&gt;Create visualization&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configure the Chart:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;View:&lt;/strong&gt; Select &lt;strong&gt;Area&lt;/strong&gt; (Stacked).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Horizontal Axis:&lt;/strong&gt; &lt;code&gt;@timestamp&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vertical Axis:&lt;/strong&gt; &lt;code&gt;Count of records&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The Filter Logic:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In the search bar, use this query:
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;logger_name.keyword: "hudson.slaves.NodeProvisioner" and message: "*provisioning successfully completed*"
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;A Note on Accuracy (The Double Log):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Observation:&lt;/em&gt; You might notice that for every one build, the count goes up by 2.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Cause:&lt;/em&gt; The current version of Jenkins logs this specific success message twice (once for the request, once for the completion).&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Impact:&lt;/em&gt; While the absolute number is inflated, the &lt;strong&gt;trend line&lt;/strong&gt; is accurate. A spike on this chart still represents a spike in workload. We accept this imperfection rather than over-engineering a fix.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Save:&lt;/strong&gt; Name it &lt;strong&gt;"Build Frequency"&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  8.4 Assembling the View
&lt;/h2&gt;

&lt;p&gt;You now have a dashboard with three powerful widgets.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Resize:&lt;/strong&gt; Grab the bottom-right corner of the &lt;strong&gt;Security Radar&lt;/strong&gt; and &lt;strong&gt;Build Frequency&lt;/strong&gt; charts and stretch them across the full width of the screen. Time-series data needs width to be readable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Position:&lt;/strong&gt; Place the &lt;strong&gt;Health Pie Chart&lt;/strong&gt; in the top-left corner.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Save the Dashboard:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Click the &lt;strong&gt;Save&lt;/strong&gt; button in the top right.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Title:&lt;/strong&gt; &lt;code&gt;CICD Stack&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Store time with dashboard:&lt;/strong&gt; Toggle this &lt;strong&gt;On&lt;/strong&gt;. This ensures that whenever you open this dashboard, it defaults to the correct time window.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You now have a professional-grade observability dashboard. But if you restart the containers now, this manual work might be lost. In the next chapter, we will ensure this dashboard is saved as code so it can be automatically restored.&lt;/p&gt;
&lt;h1&gt;
  
  
  Chapter 9: Disaster Recovery
&lt;/h1&gt;
&lt;h2&gt;
  
  
  9.1 The "Ephemeral" Problem
&lt;/h2&gt;

&lt;p&gt;You have just spent valuable time building a dashboard. You tweaked the colors, filtered out the noise, and aligned the charts perfectly.&lt;/p&gt;

&lt;p&gt;But in a DevOps environment, infrastructure is ephemeral. If you tear down your stack to save costs or spin it up on a new server for a demo, that dashboard is gone. Kibana stores these configurations in its internal index (&lt;code&gt;.kibana&lt;/code&gt;), and unless you have a robust snapshot strategy, your manual clicks evaporate when the volume is deleted.&lt;/p&gt;

&lt;p&gt;We need to treat our dashboard like our application code: &lt;strong&gt;Version Controlled&lt;/strong&gt; and &lt;strong&gt;Automated&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  9.2 Generating the Artifact (&lt;code&gt;export.ndjson&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;First, we need to extract the "Source Code" of the dashboard we built in Chapter 8.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In Kibana, go to &lt;strong&gt;Stack Management &amp;gt; Saved Objects&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Find your dashboard titled &lt;strong&gt;"CICD Stack"&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select the checkbox next to it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Crucial Step:&lt;/strong&gt; Click the &lt;strong&gt;Export&lt;/strong&gt; button.

&lt;ul&gt;
&lt;li&gt;A modal will appear asking if you want to include related objects. &lt;strong&gt;Toggle this ON.&lt;/strong&gt; This ensures that the Data View (&lt;code&gt;filebeat-*&lt;/code&gt;) and the individual visualizations (Pie Chart, Bar Chart) are bundled into the file.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Save the file as &lt;code&gt;export.ndjson&lt;/code&gt; in your project root.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You now have a portable snapshot of your monitoring logic. You can delete your entire Docker environment, run a script, and have this exact view back in seconds.&lt;/p&gt;
&lt;h2&gt;
  
  
  9.3 The Restoration Script
&lt;/h2&gt;

&lt;p&gt;Now we write the automation to load this file. Create &lt;code&gt;06-import-dashboard.sh&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This script uses the Kibana API to upload our saved objects. Notice that it does &lt;em&gt;not&lt;/em&gt; require the &lt;code&gt;-k&lt;/code&gt; (insecure) flag for &lt;code&gt;curl&lt;/code&gt;. Because our host machine trusts the Private Certificate Authority we generated during the setup phase, we can interact with our local HTTPS services securely and correctly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;#  06-import-dashboard.sh&lt;/span&gt;
&lt;span class="c"&gt;#  Imports the Kibana Dashboard from 'export.ndjson'.&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="c"&gt;# 1. Source the Environment Variables (Absolute Path)&lt;/span&gt;
&lt;span class="c"&gt;#    We look for the file generated by 01-setup-elk.sh in the runtime directory.&lt;/span&gt;
&lt;span class="nv"&gt;ENV_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/cicd_stack/elk/filebeat/filebeat.env"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"❌ Error: Could not find credentials at &lt;/span&gt;&lt;span class="nv"&gt;$ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Did you run 01-setup-elk.sh?"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# 2. Configuration&lt;/span&gt;
&lt;span class="nv"&gt;KIBANA_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://kibana.cicd.local:5601"&lt;/span&gt;
&lt;span class="nv"&gt;DASHBOARD_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"export.ndjson"&lt;/span&gt;

&lt;span class="c"&gt;# 3. Check for the Export File&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DASHBOARD_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"❌ Error: '&lt;/span&gt;&lt;span class="nv"&gt;$DASHBOARD_FILE&lt;/span&gt;&lt;span class="s2"&gt;' not found in current directory."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"⏳ Waiting for Kibana to be ready..."&lt;/span&gt;
&lt;span class="k"&gt;until &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$KIBANA_URL&lt;/span&gt;&lt;span class="s2"&gt;/api/status"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s2"&gt;"available"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"."&lt;/span&gt;
  &lt;span class="nb"&gt;sleep &lt;/span&gt;5
&lt;span class="k"&gt;done
&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;" Kibana is up!"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🚀 Importing Dashboard from &lt;/span&gt;&lt;span class="nv"&gt;$DASHBOARD_FILE&lt;/span&gt;&lt;span class="s2"&gt;..."&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"---------------------------------------------"&lt;/span&gt;

&lt;span class="c"&gt;# 4. Import the Dashboard&lt;/span&gt;
&lt;span class="c"&gt;#    -X POST: The API method&lt;/span&gt;
&lt;span class="c"&gt;#    -u: Basic Auth using the password from the env file&lt;/span&gt;
&lt;span class="c"&gt;#    -H "kbn-xsrf: true": Required header for Kibana API&lt;/span&gt;
&lt;span class="c"&gt;#    --form file=@...: Uploads the file&lt;/span&gt;

curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$KIBANA_URL&lt;/span&gt;&lt;span class="s2"&gt;/api/saved_objects/_import?overwrite=true"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="s2"&gt;"elastic:&lt;/span&gt;&lt;span class="nv"&gt;$ELASTIC_PASSWORD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"kbn-xsrf: true"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--form&lt;/span&gt; &lt;span class="nv"&gt;file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;@&lt;span class="nv"&gt;$DASHBOARD_FILE&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"---------------------------------------------"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✅ Import request finished."&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  9.4 The Mechanics of the Restore
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. The "Green" Gate&lt;/strong&gt;&lt;br&gt;
Just like in our deployment scripts, we cannot fire requests at Kibana before it is ready. The &lt;code&gt;until&lt;/code&gt; loop hits the &lt;code&gt;/api/status&lt;/code&gt; endpoint. It will block execution until Kibana explicitly returns &lt;code&gt;"level":"available"&lt;/code&gt;. This prevents the script from failing if you run it immediately after starting the container.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The Overwrite Flag&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;.../saved_objects/_import?overwrite=true&lt;/code&gt;&lt;br&gt;
We explicitly set &lt;code&gt;overwrite=true&lt;/code&gt;. This makes the script &lt;strong&gt;Idempotent&lt;/strong&gt;. You can run it ten times in a row; if the dashboard already exists, it updates it. If we didn't do this, the second run would crash with a "Conflict" error.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The CSRF Token&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;-H "kbn-xsrf: true"&lt;/code&gt;&lt;br&gt;
Kibana has strict security protections against Cross-Site Request Forgery. Even though we are authenticating with a valid password, the API will reject any write operation that lacks this specific header. It is a mandatory handshake that proves the request is intentional.&lt;/p&gt;

&lt;p&gt;With this script in your toolkit, your observability stack is now as reproducible as the applications it monitors.&lt;/p&gt;
&lt;h1&gt;
  
  
  Chapter 10: Validation – The Stress Test
&lt;/h1&gt;
&lt;h2&gt;
  
  
  10.1 The "Fire Drill" Philosophy
&lt;/h2&gt;

&lt;p&gt;We have built a complex machine. We have containers, pipelines, secure certificate authorities, and dashboards. But right now, it is sitting idle. A monitoring system that looks good when nothing is happening is useless; you need to know what it looks like when the world is burning.&lt;/p&gt;

&lt;p&gt;In this final chapter, we are going to attack our own infrastructure. We will execute a "Fire Drill" to prove that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Filebeat&lt;/strong&gt; can handle a sudden flood of events (Backpressure).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Pipeline&lt;/strong&gt; correctly parses logs even under load.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Dashboard&lt;/strong&gt; translates this chaos into a clear, readable signal.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  10.2 The Nuclear Option (The Script)
&lt;/h2&gt;

&lt;p&gt;Create &lt;code&gt;99-stress-test.sh&lt;/code&gt;. This script is designed to be noisy. It uses standard Linux tools to generate two distinct types of failures: &lt;strong&gt;System Stability&lt;/strong&gt; failures and &lt;strong&gt;Network Security&lt;/strong&gt; events.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;#           99-stress-test.sh&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  Generates 1000 System Errors and 1000 Access Events&lt;/span&gt;
&lt;span class="c"&gt;#  to stress-test ELK dashboards.&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🔥 Starting ELK 'Nuclear' Stress Test..."&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"---------------------------------"&lt;/span&gt;

&lt;span class="c"&gt;# 1. Generate 1000 System Events (Blame Wheel -&amp;gt; System Slice)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"1. Injecting 1000 System Errors &amp;amp; Warnings..."&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;1..1000&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;logger &lt;span class="nt"&gt;-p&lt;/span&gt; syslog.err &lt;span class="s2"&gt;"CICD-STRESS-TEST: Critical Database Failure #&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    logger &lt;span class="nt"&gt;-p&lt;/span&gt; syslog.warning &lt;span class="s2"&gt;"CICD-STRESS-TEST: Memory Threshold Exceeded #&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    &lt;span class="c"&gt;# Progress bar effect (print a dot every 50 events)&lt;/span&gt;
    &lt;span class="o"&gt;((&lt;/span&gt;i % 50 &lt;span class="o"&gt;==&lt;/span&gt; 0&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"."&lt;/span&gt;
&lt;span class="k"&gt;done
&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;" Done."&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"---------------------------------"&lt;/span&gt;

&lt;span class="c"&gt;# 2. Generate 1000 Access Events (Security Radar -&amp;gt; 302 Spike)&lt;/span&gt;
&lt;span class="c"&gt;# We target Port 10300 (GitLab HTTPS).&lt;/span&gt;
&lt;span class="c"&gt;# Expect 302 (Redirect to Login) or 401/403 depending on the endpoint.&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"2. Simulating 1000 Access Attempts on GitLab (Port 10300)..."&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;1..1000&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c"&gt;# Hit the protected admin endpoint on the correct mapped port&lt;/span&gt;
    curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="s2"&gt;"https://gitlab.cicd.local:10300/admin"&lt;/span&gt;

    &lt;span class="o"&gt;((&lt;/span&gt;i % 50 &lt;span class="o"&gt;==&lt;/span&gt; 0&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"."&lt;/span&gt;
&lt;span class="k"&gt;done
&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;" Done."&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"---------------------------------"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🎉 Stress Test Complete."&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Go to Kibana -&amp;gt; Refresh Dashboard (Last 15 Minutes)"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   You should see a massive spike in '302' events."&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  10.3 Vector 1: The System Flood (Testing Journald)
&lt;/h2&gt;

&lt;p&gt;The first loop uses the &lt;code&gt;logger&lt;/code&gt; command. This tool writes directly to the Linux System Journal, bypassing Docker completely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Test:&lt;/strong&gt;&lt;br&gt;
We are injecting 1,000 messages with specific severity levels (&lt;code&gt;syslog.err&lt;/code&gt; and &lt;code&gt;syslog.warning&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Visual Result:&lt;/strong&gt;&lt;br&gt;
Look at your &lt;strong&gt;"System Health Status"&lt;/strong&gt; Pie Chart.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Before:&lt;/strong&gt; It was likely empty or had thin slices, as a healthy system generates very few errors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;After:&lt;/strong&gt; You will see a massive new slice appear, dominating the chart. This slice represents the &lt;strong&gt;Host System&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Why this matters:&lt;/em&gt; We filtered this chart to only show "Bad Things" (&lt;code&gt;WARN&lt;/code&gt; or &lt;code&gt;ERROR&lt;/code&gt;). The fact that this slice appeared instantly proves that our &lt;strong&gt;Unified Query&lt;/strong&gt; works: it successfully caught the &lt;code&gt;syslog.priority&lt;/code&gt; signal from the host, even though it didn't come from a container.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  10.4 Vector 2: The Network Flood (Testing Nginx)
&lt;/h2&gt;

&lt;p&gt;The second loop uses &lt;code&gt;curl&lt;/code&gt; to hit the GitLab Admin interface (&lt;code&gt;/admin&lt;/code&gt;). Since our script does not provide a session cookie, GitLab rejects us.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Test:&lt;/strong&gt;&lt;br&gt;
We fire 1,000 requests at the Nginx Reverse Proxy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Visual Result:&lt;/strong&gt;&lt;br&gt;
Look at your &lt;strong&gt;"GitLab Access Patterns"&lt;/strong&gt; Bar Chart.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Wall:&lt;/strong&gt; You will see a massive vertical spike.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Code:&lt;/strong&gt; The bars will be colored for status code &lt;strong&gt;302&lt;/strong&gt; (Redirect).&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Why this matters:&lt;/em&gt; If we had failed to configure the &lt;strong&gt;Ingest Pipeline&lt;/strong&gt; (Chapter 5) correctly, these logs would just be text. Kibana wouldn't know they were "302" errors. The fact that you can see a "302" bar proves the regex parser extracted &lt;code&gt;http.response.status_code&lt;/code&gt; correctly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  10.5 Conclusion: The Single Pane of Glass
&lt;/h2&gt;

&lt;p&gt;Congratulations. You have successfully deployed a production-grade Observability Stack for a CI/CD environment.&lt;/p&gt;

&lt;p&gt;Let's review what we achieved:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Architecture:&lt;/strong&gt; We built a secure, single-node architecture using custom Docker networks and TLS encryption.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ingestion:&lt;/strong&gt; We used &lt;strong&gt;Filebeat&lt;/strong&gt; as a lightweight "Host Agent" to collect data from both containers (Docker volumes) and the OS (Journald).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parsing:&lt;/strong&gt; We replaced the heavy Logstash with lean &lt;strong&gt;Ingest Pipelines&lt;/strong&gt;, moving the logic into the database to save RAM.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Persistence:&lt;/strong&gt; We automated the dashboard recovery with &lt;code&gt;export.ndjson&lt;/code&gt;, ensuring our work is never lost.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validation:&lt;/strong&gt; We proved it works by attacking it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You have moved beyond "SSH and Grep." You now have a &lt;strong&gt;Single Pane of Glass&lt;/strong&gt;—a central nervous system that tells you the health, security, and workload of your factory in real-time. This is the foundation of modern DevOps.&lt;/p&gt;

</description>
      <category>elasticsearch</category>
      <category>monitoring</category>
      <category>tutorial</category>
      <category>devops</category>
    </item>
    <item>
      <title>Part 07: Building a Sovereign Software Factory: ChatOps with Mattermost</title>
      <dc:creator>Warren Jitsing</dc:creator>
      <pubDate>Mon, 22 Dec 2025 05:35:47 +0000</pubDate>
      <link>https://dev.to/warren_jitsing_dd1c1d6fc6/part-07-building-a-sovereign-software-factory-chatops-with-mattermost-bf2</link>
      <guid>https://dev.to/warren_jitsing_dd1c1d6fc6/part-07-building-a-sovereign-software-factory-chatops-with-mattermost-bf2</guid>
      <description>&lt;p&gt;GitHub: &lt;a href="https://github.com/InfiniteConsult/0011_cicd_part07_mattermost" rel="noopener noreferrer"&gt;https://github.com/InfiniteConsult/0011_cicd_part07_mattermost&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; In this installment, we solve the "Silent City" problem where alerts are ignored. We construct the &lt;strong&gt;"Command Center"&lt;/strong&gt; by deploying &lt;strong&gt;Mattermost&lt;/strong&gt; for ChatOps, but first, we must conquer the &lt;strong&gt;"Mobile Frontier"&lt;/strong&gt; by manually installing our Root CA on Android to enable secure HTTPS without public DNS. We solve the &lt;strong&gt;"Double NAT"&lt;/strong&gt; problem for video conferencing by deploying a &lt;strong&gt;Coturn TURN server&lt;/strong&gt; with host networking, and we wire the city's nervous system so that Jenkins builds and SonarQube alerts are pushed directly to engineering channels.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Sovereign Software Factory Series:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Part 01:&lt;/strong&gt; Building a Sovereign Software Factory: Docker Networking &amp;amp; Persistence&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 02:&lt;/strong&gt; Building a Sovereign Software Factory: The Local Root CA &amp;amp; Trust Chains&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 03:&lt;/strong&gt; Building a Sovereign Software Factory: Self-Hosted GitLab &amp;amp; Secrets Management&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 04:&lt;/strong&gt; Building a Sovereign Software Factory: Jenkins Configuration as Code (JCasC)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 05:&lt;/strong&gt; Building a Sovereign Software Factory: Artifactory &amp;amp; The "Strict TLS" Trap&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 06:&lt;/strong&gt; Building a Sovereign Software Factory: SonarQube Quality Gates&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 07: Building a Sovereign Software Factory: ChatOps with Mattermost&lt;/strong&gt; (You are here)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 08:&lt;/strong&gt; Building a Sovereign Software Factory: Observability with the ELK Stack&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 09:&lt;/strong&gt; Building a Sovereign Software Factory: Monitoring with Prometheus &amp;amp; Grafana&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 10:&lt;/strong&gt; Building a Sovereign Software Factory: The Python API Package (Capstone)&lt;/li&gt;
&lt;/ol&gt;




&lt;h1&gt;
  
  
  Chapter 1: The Challenge - The Silent City
&lt;/h1&gt;

&lt;h2&gt;
  
  
  1.1 The "Lights Out" Problem
&lt;/h2&gt;

&lt;p&gt;In the previous six articles, we have meticulously constructed a sovereign "Software Supply Chain." We started with the foundation in &lt;strong&gt;Docker&lt;/strong&gt; and a custom &lt;strong&gt;Certificate Authority&lt;/strong&gt;, then built a &lt;strong&gt;Library&lt;/strong&gt; (GitLab) to store our blueprints, a &lt;strong&gt;Factory&lt;/strong&gt; (Jenkins) to manufacture our products, an &lt;strong&gt;Inspector&lt;/strong&gt; (SonarQube) to certify their quality, and a &lt;strong&gt;Warehouse&lt;/strong&gt; (Artifactory) to store them securely.&lt;/p&gt;

&lt;p&gt;Technically, our city is perfect. The pipelines run, the code is analyzed, and the artifacts are shipped.&lt;/p&gt;

&lt;p&gt;But functionally, our city is broken. It is a "Silent City."&lt;/p&gt;

&lt;p&gt;When a build fails in the Factory, the only person who knows is the engineer staring at the Jenkins console. When the Inspector slams the Quality Gate shut, the event is logged in a database, but no alarm bells ring. To know the status of our operations, we are forced to manually patrol the dashboards of four different tools. We have built a complex machine, but we have failed to build a nervous system.&lt;/p&gt;

&lt;p&gt;In a modern DevOps environment, this latency is unacceptable. We need instantaneous, passive awareness. If the "Main Line" stops, every engineer should know immediately. If a critical security vulnerability is detected, the alert should find us where we are—whether that is at our desk or on our phone.&lt;/p&gt;

&lt;h2&gt;
  
  
  1.2 The "Command Center" (ChatOps)
&lt;/h2&gt;

&lt;p&gt;To solve this, we need to fundamentally change how we interact with our infrastructure. We need to move beyond passive monitoring and fragmented dashboards. We need a &lt;strong&gt;Command Center&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This concept is industry-known as &lt;strong&gt;ChatOps&lt;/strong&gt;. It represents a paradigm shift where the chat client ceases to be merely a "water cooler" for human conversation and becomes a shared, real-time command line interface for the entire engineering team. In a mature ChatOps environment, the chat window is the central console where operations happen. You don't alt-tab to Jenkins to trigger a build; you type &lt;code&gt;/jenkins build&lt;/code&gt; in the channel. You don't log into SonarQube to check the quality gate; the gate reports its status directly to you.&lt;/p&gt;

&lt;p&gt;By centralizing these operations, we achieve three critical goals:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Transparency:&lt;/strong&gt; Every action is visible to the team. If a senior engineer fixes a broken build, the junior engineers watch it happen in real-time, learning the diagnosis and the cure implicitly.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Context:&lt;/strong&gt; The alert is located right next to the conversation about the alert. The "What happened?" and the "Why did it happen?" live in the same timeline.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Velocity:&lt;/strong&gt; We reduce context switching. We stop jumping between four different browser tabs to understand the state of the world.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In a typical startup environment, setting this up is trivial: you sign up for Slack or Discord, generate a webhook token, and pipe your logs to the cloud. However, our "First Principles" architecture strictly forbids this. We are simulating a high-assurance, air-gapped environment—modeled after defense or financial sectors—where data sovereignty is paramount.&lt;/p&gt;

&lt;p&gt;We cannot pipe our proprietary build logs, code snippets, or vulnerability reports to a third-party SaaS cloud. That data constitutes our intellectual property and our security posture. If we use Slack, our internal state leaves our perimeter.&lt;/p&gt;

&lt;p&gt;Therefore, we will deploy &lt;strong&gt;Mattermost&lt;/strong&gt;. Mattermost is the open-source industry standard for secure, self-hosted collaboration. It offers the modern features we expect—threaded messaging, file sharing, rich media, and mobile applications—but it runs entirely on our own silicon, inside our &lt;code&gt;cicd-net&lt;/code&gt;. It gives us the usability of Silicon Valley SaaS with the security of a hardened bunker.&lt;/p&gt;

&lt;h2&gt;
  
  
  1.3 The Scope: War Room &amp;amp; Nervous System
&lt;/h2&gt;

&lt;p&gt;However, a "Command Center" is defined by more than just its ability to display text. In the heat of a production incident or a broken build pipeline, text is often the bottleneck.&lt;/p&gt;

&lt;p&gt;When the "Main Line" stops, the immediate next step is almost always a "War Room" scenario. Engineers need to escalate from asynchronous text to synchronous collaboration. They need to see each other, share screens, point at logs, and debug the issue in real-time. In a traditional setup, this is the moment the team breaks protocol: they leave the secure chat, open Zoom or Microsoft Teams, and effectively carry the conversation (and potentially sensitive screen data) out of the secure facility and onto a public cloud server.&lt;/p&gt;

&lt;p&gt;This breaks our security model. It punches a hole in our air-gapped fortress. To maintain total sovereignty, we must provide a Video Conferencing capability that is as secure and local as the code itself.&lt;/p&gt;

&lt;p&gt;So, our mission in this article is twofold:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;The Nervous System:&lt;/strong&gt; We will wire up the sensory organs of our city—Jenkins, GitLab, and SonarQube—to push rich, actionable alerts into specific Mattermost channels (&lt;code&gt;#builds&lt;/code&gt;, &lt;code&gt;#alerts&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The War Room:&lt;/strong&gt; We will deploy a fully functional, self-hosted Video Conferencing stack using the Mattermost &lt;strong&gt;Calls&lt;/strong&gt; plugin.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This second requirement will force us to confront one of the most notorious "Dragons" in self-hosted networking: &lt;strong&gt;NAT Traversal&lt;/strong&gt;. Unlike simple HTTP traffic, which flows easily through Docker containers, real-time video relies on &lt;strong&gt;WebRTC&lt;/strong&gt; (Web Real-Time Communication). This protocol is allergic to the complex layers of Network Address Translation (NAT) found in Docker. To make this work—specifically to make it work on a mobile phone over WiFi—we will have to build a dedicated "Radio Tower" (TURN Server) to relay the signal over the walls of our container fortress.&lt;/p&gt;

&lt;h1&gt;
  
  
  Chapter 2: Architecture - The Fortress and the Phone
&lt;/h1&gt;

&lt;h2&gt;
  
  
  2.1 The "Enterprise" Hack (Entry Mode)
&lt;/h2&gt;

&lt;p&gt;Our first architectural decision concerns the software edition. Mattermost offers two primary Docker images: the purely open-source &lt;strong&gt;Team Edition&lt;/strong&gt; (&lt;code&gt;mattermost-team-edition&lt;/code&gt;) and the commercial &lt;strong&gt;Enterprise Edition&lt;/strong&gt; (&lt;code&gt;mattermost-enterprise-edition&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Historically, self-hosters strictly deployed the Team Edition to avoid licensing nags. However, Mattermost has shifted its distribution model. They now encourage even free users to deploy the &lt;strong&gt;Enterprise Image&lt;/strong&gt;. When deployed without a license key, this image runs in a special state known as &lt;strong&gt;"Entry Mode."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We will adopt this modern approach.&lt;/p&gt;

&lt;p&gt;We choose the Enterprise image not because we intend to pirate software, but because the Team Edition is functionally incomplete for a modern DevOps workflow. By running the Enterprise image in Entry Mode, we unlock the &lt;strong&gt;"Intelligent Mission Environment."&lt;/strong&gt; This grants us access to powerful tools like &lt;strong&gt;Boards&lt;/strong&gt; (Kanban project management) and &lt;strong&gt;Playbooks&lt;/strong&gt; (incident response checklists)—features that are entirely stripped from the Team build.&lt;/p&gt;

&lt;p&gt;This power comes with constraints. Entry Mode imposes hard limits designed to encourage commercial upgrades:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;10,000 Message Search Limit:&lt;/strong&gt; Older messages remain in the database but vanish from search results.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Single Node Only:&lt;/strong&gt; We cannot cluster the application for High Availability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Feature Caps:&lt;/strong&gt; Limits on active Playbooks and Board cards.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For our "First Principles" laboratory, these limits are acceptable. For a production client, we would strongly advise purchasing a license to lift these gates. But for us, this strategy gives us Ferrari features on a Corolla budget.&lt;/p&gt;

&lt;p&gt;Finally, we will treat our database as a commodity. In &lt;strong&gt;Article 9&lt;/strong&gt;, we established a centralized &lt;strong&gt;PostgreSQL 17&lt;/strong&gt; cluster. Mattermost will not spawn its own private database container; it will simply be another tenant in our existing "Water Treatment Plant," connecting via our internal &lt;code&gt;cicd-net&lt;/code&gt;. This reduces our resource footprint and ensures our chat data benefits from the same backup and security policies as our artifact data.&lt;/p&gt;

&lt;h2&gt;
  
  
  2.2 The "Mobile-Ready" Trust
&lt;/h2&gt;

&lt;p&gt;The second architectural challenge is &lt;strong&gt;Trust&lt;/strong&gt;, specifically how trust varies across different devices in our ecosystem.&lt;/p&gt;

&lt;p&gt;In previous articles, we established a "Local Root of Trust" using our custom Certificate Authority (CA). On our desktop machines, this system works flawlessly. We imported our Root CA into the operating system's trust store (Debian/Ubuntu/MacOS), and our browsers immediately recognized &lt;code&gt;gitlab.cicd.local&lt;/code&gt; and &lt;code&gt;jenkins.cicd.local&lt;/code&gt; as secure. We relied on a simple &lt;code&gt;/etc/hosts&lt;/code&gt; modification to route those domain names to &lt;code&gt;127.0.0.1&lt;/code&gt;, effectively tricking the browser into believing the server was local.&lt;/p&gt;

&lt;p&gt;However, our Command Center has a requirement that our other tools did not: &lt;strong&gt;Mobile Access&lt;/strong&gt;. We want to receive alerts and join "War Room" calls from our Android or iOS devices while roaming around the office (connected to WiFi).&lt;/p&gt;

&lt;p&gt;This introduces a hostile environment. Mobile operating systems, particularly modern Android (11+), are notoriously strict about TLS security. They present two specific barriers that break our standard desktop strategy:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;The DNS Barrier:&lt;/strong&gt; You cannot easily edit the &lt;code&gt;/etc/hosts&lt;/code&gt; file on a non-rooted Android phone. This means the phone has no idea who &lt;code&gt;mattermost.cicd.local&lt;/code&gt; is. It relies entirely on the network's DNS server. Unless we run a custom DNS server on our LAN (like Pi-hole), the phone will fail to resolve the domain name.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The IP Barrier:&lt;/strong&gt; To bypass the DNS issue, we might try to connect directly via the server's LAN IP address (e.g., &lt;code&gt;https://192.168.0.105:8065&lt;/code&gt;). However, standard SSL certificates are issued to &lt;em&gt;Domain Names&lt;/em&gt;, not &lt;em&gt;IP Addresses&lt;/em&gt;. If we use our standard certificate, the app will reject the connection because the "Common Name" (domain) does not match the "Host" (IP) in the address bar.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The Trust Barrier:&lt;/strong&gt; Even if the IP matches, the Android app does not trust our custom CA by default. It will throw a generic, often cryptic error like "Trusted Anchor not found" or simply "Cannot connect to server."&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To solve this, we must engineer a &lt;strong&gt;"Mobile-Ready" Certificate&lt;/strong&gt;. We cannot use the generic certificate generation script we built in Article 6. We need a specialized issuance process that explicitly bakes the &lt;strong&gt;LAN IP Address&lt;/strong&gt; into the certificate's &lt;strong&gt;Subject Alternative Names (SANs)&lt;/strong&gt; field.&lt;/p&gt;

&lt;p&gt;By adding &lt;code&gt;IP:192.168.x.x&lt;/code&gt; to the certificate, we create a cryptographic identity that is valid even when accessed via a raw IP address. This allows us to bypass the DNS problem entirely. We simply tell the mobile app to connect to the IP, and because the certificate explicitly claims that IP, the TLS handshake succeeds—provided we also manually install the Root CA on the device (which we will cover in the deployment phase). This architectural foresight turns a "connection refused" error into a functioning mobile command post.&lt;/p&gt;

&lt;h2&gt;
  
  
  2.3 The "Radio Tower" (Coturn &amp;amp; Host Networking)
&lt;/h2&gt;

&lt;p&gt;The final and most formidable piece of our architecture is the &lt;strong&gt;Video Conferencing&lt;/strong&gt; stack. This requirement forces us to leave the comfortable world of HTTP and confront the chaotic reality of &lt;strong&gt;WebRTC&lt;/strong&gt; (Web Real-Time Communication).&lt;/p&gt;

&lt;p&gt;In our previous articles, every service we deployed—GitLab, Jenkins, SonarQube—communicated using TCP/IP over HTTP. This model is simple: the client opens a connection to the server, sends a request, and waits for a response. It is reliable, predictable, and remarkably tolerant of network layers like Docker's bridge network and Nginx reverse proxies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WebRTC is different.&lt;/strong&gt; It is designed for real-time audio and video, where latency is the enemy. It prefers &lt;strong&gt;UDP&lt;/strong&gt; over TCP because it's faster to drop a lost packet than to wait for retransmission (a glitch is better than a lag). More importantly, WebRTC attempts to establish a &lt;strong&gt;Peer-to-Peer (P2P)&lt;/strong&gt; connection directly between two devices to minimize latency.&lt;/p&gt;

&lt;p&gt;In a containerized environment, this P2P model breaks instantly.&lt;/p&gt;

&lt;p&gt;When your phone (on WiFi) tries to send video to the Mattermost server (in a container), it needs an IP address to target. However, the Mattermost container lives inside a Docker Bridge network. It has an internal IP (e.g., &lt;code&gt;172.18.0.5&lt;/code&gt;) that is completely invisible to the outside world. To make matters worse, your phone is likely behind its own NAT (Network Address Translation). This scenario is known as &lt;strong&gt;Double NAT&lt;/strong&gt;, and it acts as an unbridgeable moat for direct media streams.&lt;/p&gt;

&lt;p&gt;To bridge this moat, we need a &lt;strong&gt;TURN Server&lt;/strong&gt; (Traversal Using Relays around NAT). We will deploy &lt;strong&gt;Coturn&lt;/strong&gt;, the industry-standard open-source TURN server.&lt;/p&gt;

&lt;p&gt;Architecturally, Coturn acts as a &lt;strong&gt;"Radio Tower."&lt;/strong&gt; It sits on the absolute edge of our network. When direct P2P communication fails (which it always will in Docker), the phone sends its media packets to the Radio Tower. The Tower then relays those packets across the Docker boundary to the Mattermost container.&lt;/p&gt;

&lt;p&gt;But deploying Coturn brings its own "Dragon": &lt;strong&gt;The Port Range.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Unlike a web server that listens on a single port (443), a TURN server requires a massive range of ephemeral UDP ports—typically &lt;strong&gt;32,768 to 65,535&lt;/strong&gt;—to handle media streams for multiple users simultaneously. Every active call consumes a port.&lt;/p&gt;

&lt;p&gt;If we tried to deploy this using standard Docker Bridge networking, we would have to map every single one of these ports in the &lt;code&gt;docker run&lt;/code&gt; command or Compose file. This creates two critical problems:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;The "Docker Proxy" Bottleneck:&lt;/strong&gt; For every mapped port, Docker spins up a userland proxy process (&lt;code&gt;docker-proxy&lt;/code&gt;). Asking Docker to manage 30,000+ proxy rules explodes the memory usage and adds significant CPU latency to every packet, killing call quality.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;IPTables Bloat:&lt;/strong&gt; Creating tens of thousands of NAT rules in the host's firewall table slows down networking for the entire system.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To solve this, we will make a rare exception to our "Isolation First" rule. We will deploy the Coturn container using &lt;strong&gt;Host Networking&lt;/strong&gt; (&lt;code&gt;--network host&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;This mode effectively removes the Docker network isolation layer for this specific container. Coturn will not have a private &lt;code&gt;172.x.x.x&lt;/code&gt; IP; it will bind directly to the physical network interface of your host machine (&lt;code&gt;192.168.x.x&lt;/code&gt;). This eliminates the need for port mapping entirely. It gives our Radio Tower a clear, unobstructed line of sight to your mobile device, ensuring that when you press "Join Call," the video flows instantly and efficiently.&lt;/p&gt;

&lt;h1&gt;
  
  
  Chapter 3: The Architect - Preparing the Ground
&lt;/h1&gt;

&lt;h2&gt;
  
  
  3.1 Database Hygiene (Postgres 15+ Compliance)
&lt;/h2&gt;

&lt;p&gt;Before we write a single line of configuration code, we must attend to the soil in which we are planting our application. We are using &lt;strong&gt;PostgreSQL 17&lt;/strong&gt; as our shared data store. While this gives us performance and longevity, it also brings strict security defaults that can strangle legacy applications if we aren't careful.&lt;/p&gt;

&lt;p&gt;Specifically, PostgreSQL 15 introduced a breaking change regarding schema ownership. In older versions, any user could create tables in the &lt;code&gt;public&lt;/code&gt; schema by default. In 15+, this permission was revoked to harden the database against accidental pollution.&lt;/p&gt;

&lt;p&gt;Mattermost, like many mature applications, expects to own its schema completely. It performs complex migrations on startup—creating tables, altering columns, and indexing data. If the database user (&lt;code&gt;mattermost&lt;/code&gt;) does not explicitly own the &lt;code&gt;public&lt;/code&gt; schema, these migrations can fail. Often, this failure is silent or cryptic, manifesting as a boot loop where the logs complain about "permission denied" on a table that doesn't exist yet.&lt;/p&gt;

&lt;p&gt;To prevent this "Silent Crash," we cannot just create the user and hope for the best. Our setup script must actively intervene. We will use the &lt;code&gt;psql&lt;/code&gt; client to execute a targeted &lt;code&gt;ALTER SCHEMA&lt;/code&gt; command, explicitly transferring ownership of the &lt;code&gt;public&lt;/code&gt; schema in the &lt;code&gt;mattermost&lt;/code&gt; database to the &lt;code&gt;mattermost&lt;/code&gt; user. This restores the "God Mode" permissions the application expects within its own sandbox, ensuring smooth migrations for years to come.&lt;/p&gt;

&lt;h2&gt;
  
  
  3.2 The "Mobile-Ready" Certificate
&lt;/h2&gt;

&lt;p&gt;With our database secured, we must address the single biggest friction point in self-hosted DevOps: &lt;strong&gt;Mobile Trust&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In our previous articles, we connected our desktop browsers to our internal tools using a simple trick. We updated our &lt;code&gt;/etc/hosts&lt;/code&gt; file to map &lt;code&gt;gitlab.cicd.local&lt;/code&gt; to &lt;code&gt;127.0.0.1&lt;/code&gt;. Because we had imported our Root CA into the desktop's trust store, the browser saw a valid certificate for a valid domain and gave us the green lock.&lt;/p&gt;

&lt;p&gt;Mobile devices, however, exist in a more hostile networking environment.&lt;/p&gt;

&lt;p&gt;Modern Android (11+) and iOS devices lock down their DNS settings. You cannot simply edit a "hosts file" on a non-rooted phone to tell it that &lt;code&gt;mattermost.cicd.local&lt;/code&gt; resides on your laptop. When your phone is on the office WiFi, it uses the router's DNS. If that router doesn't know your internal domain (which it won't, because we don't have a custom DNS server like Pi-hole), the phone will fail to resolve the address.&lt;/p&gt;

&lt;p&gt;The pragmatic workaround is to bypass DNS entirely and connect via the &lt;strong&gt;LAN IP Address&lt;/strong&gt; (e.g., &lt;code&gt;https://192.168.0.105:8065&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;This solves the network path, but it breaks the &lt;strong&gt;TLS Identity Trust&lt;/strong&gt;. Standard SSL certificates are bound to &lt;strong&gt;Domain Names&lt;/strong&gt; (DNS entries). If you present a certificate issued to &lt;code&gt;mattermost.cicd.local&lt;/code&gt; but the user is visiting &lt;code&gt;192.168.0.105&lt;/code&gt;, the client will reject the connection immediately because the "Host" does not match the "Certificate Name." This is a fundamental protection against Phishing and Man-in-the-Middle attacks.&lt;/p&gt;

&lt;p&gt;To conquer this, we must engineer a &lt;strong&gt;"Mobile-Ready" Certificate&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We cannot use the generic issuance script we built in Article 6. We need a specialized signing request that explicitly creates a &lt;strong&gt;Subject Alternative Name (SAN)&lt;/strong&gt; entry for the IP address. By baking &lt;code&gt;IP:192.168.x.x&lt;/code&gt; directly into the certificate's cryptographic identity, we tell the mobile OS: &lt;em&gt;"It is legal and valid for this server to be accessed via this raw IP address."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Our architect script will automate this. It will dynamically detect your host's LAN IP (&lt;code&gt;hostname -I&lt;/code&gt;), construct a custom OpenSSL configuration file with the required SAN extensions, and mint a certificate that satisfies the strict identity requirements of modern mobile operating systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  3.3 Secrets Management
&lt;/h2&gt;

&lt;p&gt;Beyond certificates and database permissions, a secure Mattermost deployment relies on a specific set of cryptographic keys. These are not simple passwords that you can type in; they are high-entropy strings that underpin the security of the application's data at rest and in transit.&lt;/p&gt;

&lt;p&gt;If we leave these default or empty, we compromise the integrity of our fortress. Our Architect script manages three critical secrets:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The At-Rest Encryption Key (&lt;code&gt;MM_SQLSETTINGS_ATRESTENCRYPTKEY&lt;/code&gt;):&lt;/strong&gt;&lt;br&gt;
Mattermost stores sensitive data in the Postgres database, including OAuth tokens for GitLab and incoming webhook secrets for Jenkins. If an attacker managed to dump our database, these tokens would be exposed in plain text. By configuring an At-Rest Encryption Key (32-character AES), we ensure that Mattermost encrypts these sensitive fields before writing them to the disk.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Public Link Salt (&lt;code&gt;MM_FILESETTINGS_PUBLICLINKSALT&lt;/code&gt;):&lt;/strong&gt;&lt;br&gt;
When a user shares a file via a public link, the URL is generated using a hash function. Without a strong, random salt, these links become predictable, potentially allowing an external attacker to enumerate and download private files by guessing URL patterns.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The TURN Shared Secret (&lt;code&gt;MATTERMOST_TURN_SECRET&lt;/code&gt;):&lt;/strong&gt;&lt;br&gt;
This is the most critical key for our "War Room" functionality. The Coturn (Radio Tower) server and the Mattermost (Town Square) server are separate entities. To prevent unauthorized users from hijacking our bandwidth to relay their own traffic, the TURN server requires authentication.&lt;br&gt;
We do not use a static username/password for this. Instead, we use a &lt;strong&gt;Time-Limited Credential&lt;/strong&gt; mechanism. Both servers share this single, long secret key. Mattermost uses it to generate temporary, short-lived tokens for your phone when you join a call. Coturn uses the same key to validate those tokens. If these keys do not match exactly, the call fails instantly.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Our script uses &lt;code&gt;openssl rand -hex&lt;/code&gt; to generate these strings. Crucially, as we discovered during our GitLab integration debugging (Chapter 9), we must be precise about length. For AES-256 encryption, the key must be exactly &lt;strong&gt;32 bytes&lt;/strong&gt;. A 64-byte key (often generated by overzealous &lt;code&gt;openssl&lt;/code&gt; commands) will cause the application's crypto subsystem to crash on boot.&lt;/p&gt;

&lt;p&gt;We persist these keys in our master &lt;code&gt;cicd.env&lt;/code&gt; file, ensuring that our "Radio Tower" and our "Town Square" always wake up knowing the same handshake.&lt;/p&gt;

&lt;h2&gt;
  
  
  3.4 The Script (&lt;code&gt;01-setup-mattermost.sh&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;We have defined our requirements: Postgres 15+ hygiene, mobile-ready networking, and cryptographic security. Now, we codify these rules into our "Architect" script.&lt;/p&gt;

&lt;p&gt;This script is the single source of truth for the Mattermost deployment. It does not launch the container; it prepares the battlefield. It ensures that when the container finally starts, it lands in an environment that is secure, configured, and trusted.&lt;/p&gt;

&lt;p&gt;Create this file at &lt;code&gt;~/Documents/FromFirstPrinciples/articles/0011_cicd_part07_mattermost/01-setup-mattermost.sh&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;#               01-setup-mattermost.sh&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  The "Architect" script for Mattermost.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  1. Secrets: Generates TURN credentials and Salt keys.&lt;/span&gt;
&lt;span class="c"&gt;#  2. Database: Hot-patches PostgreSQL 15+ Schema Ownership.&lt;/span&gt;
&lt;span class="c"&gt;#  3. Certificates: Generates a "Mobile-Ready" SSL cert (.lan.crt.pem).&lt;/span&gt;
&lt;span class="c"&gt;#  4. Config: Generates the 'mattermost.env' 12-factor file.&lt;/span&gt;
&lt;span class="c"&gt;#  5. Permissions: Sets ownership for Bind Mounts ONLY.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="c"&gt;# --- 1. Define Paths ---&lt;/span&gt;
&lt;span class="nv"&gt;HOST_CICD_ROOT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/cicd_stack"&lt;/span&gt;
&lt;span class="nv"&gt;MATTERMOST_BASE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOST_CICD_ROOT&lt;/span&gt;&lt;span class="s2"&gt;/mattermost"&lt;/span&gt;
&lt;span class="nv"&gt;MASTER_ENV_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOST_CICD_ROOT&lt;/span&gt;&lt;span class="s2"&gt;/cicd.env"&lt;/span&gt;
&lt;span class="nv"&gt;SCOPED_ENV_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MATTERMOST_BASE&lt;/span&gt;&lt;span class="s2"&gt;/mattermost.env"&lt;/span&gt;

&lt;span class="c"&gt;# Certificate Authority Paths&lt;/span&gt;
&lt;span class="nv"&gt;CA_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOST_CICD_ROOT&lt;/span&gt;&lt;span class="s2"&gt;/ca"&lt;/span&gt;
&lt;span class="nv"&gt;SERVICE_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"mattermost.cicd.local"&lt;/span&gt;
&lt;span class="nv"&gt;CA_SERVICE_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CA_DIR&lt;/span&gt;&lt;span class="s2"&gt;/pki/services/&lt;/span&gt;&lt;span class="nv"&gt;$SERVICE_NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Ensure directories exist (Bind Mounts Only)&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MATTERMOST_BASE&lt;/span&gt;&lt;span class="s2"&gt;/config"&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MATTERMOST_BASE&lt;/span&gt;&lt;span class="s2"&gt;/certs"&lt;/span&gt;

&lt;span class="c"&gt;# FIX: Temporarily claim ownership for the host user so we can write files&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🔧 Setting temporary permissions for setup..."&lt;/span&gt;
&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$USER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;:&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$USER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MATTERMOST_BASE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🚀 Starting Mattermost 'Architect' Setup..."&lt;/span&gt;

&lt;span class="c"&gt;# --- 2. Secrets Management ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Phase 1: Secrets Management ---"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: Master env file not found at &lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Load existing secrets&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt;
&lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; +a

&lt;span class="c"&gt;# Helper: Generates 64-char string (Good for Salts/Secrets)&lt;/span&gt;
generate_secret&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    openssl rand &lt;span class="nt"&gt;-hex&lt;/span&gt; 32
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Helper: Generates 32-char string (REQUIRED for AES Encryption Keys)&lt;/span&gt;
generate_aes_key&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    openssl rand &lt;span class="nt"&gt;-hex&lt;/span&gt; 16
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Verify DB password exists (from Article 9)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MATTERMOST_DB_PASSWORD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: MATTERMOST_DB_PASSWORD not found in cicd.env"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Please run 01-setup-database.sh (Article 9) first."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Generate new Mattermost-specific secrets if missing&lt;/span&gt;
&lt;span class="nv"&gt;update_env&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false

&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MATTERMOST_TURN_SECRET&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Generating TURN Shared Secret..."&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"# Mattermost &amp;amp; Coturn Shared Secret"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"MATTERMOST_TURN_SECRET=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;generate_secret&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nv"&gt;update_env&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true
&lt;/span&gt;&lt;span class="k"&gt;fi

if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MATTERMOST_AT_REST_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Generating At-Rest Encryption Key (Core)..."&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"# Mattermost At-Rest Encryption Key"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"MATTERMOST_AT_REST_KEY=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;generate_secret&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nv"&gt;update_env&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true
&lt;/span&gt;&lt;span class="k"&gt;fi

if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MATTERMOST_PUBLIC_LINK_SALT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Generating Public Link Salt..."&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"# Mattermost Public Link Salt"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"MATTERMOST_PUBLIC_LINK_SALT=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;generate_secret&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nv"&gt;update_env&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true
&lt;/span&gt;&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Plugin Secrets (GitLab/Jenkins Interactive)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MATTERMOST_GITLAB_PLUGIN_SECRET&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Generating GitLab Plugin Webhook Secret..."&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"MATTERMOST_GITLAB_PLUGIN_SECRET=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;generate_secret&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nv"&gt;update_env&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true
&lt;/span&gt;&lt;span class="k"&gt;fi

if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MATTERMOST_GITLAB_PLUGIN_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Generating GitLab Plugin Encryption Key (AES-256)..."&lt;/span&gt;
    &lt;span class="c"&gt;# FIX: Must be exactly 32 chars&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"MATTERMOST_GITLAB_PLUGIN_KEY=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;generate_aes_key&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nv"&gt;update_env&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true
&lt;/span&gt;&lt;span class="k"&gt;fi

if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MATTERMOST_JENKINS_PLUGIN_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Generating Jenkins Plugin Encryption Key (AES-256)..."&lt;/span&gt;
    &lt;span class="c"&gt;# FIX: Must be exactly 32 chars&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"MATTERMOST_JENKINS_PLUGIN_KEY=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;generate_aes_key&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nv"&gt;update_env&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true
&lt;/span&gt;&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Reload secrets if we added any&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$update_env&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Secrets generated and persisted."&lt;/span&gt;
&lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Secrets already exist."&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# --- 3. Database Schema Ownership Fix (Postgres 15+) ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Phase 2: Verifying Database Schema Ownership ---"&lt;/span&gt;
&lt;span class="c"&gt;# Postgres 15+ revokes permission to create tables in 'public' from regular users.&lt;/span&gt;
&lt;span class="c"&gt;# We apply this fix specifically to the 'mattermost' database.&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;postgres&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Applying PostgreSQL 15+ schema ownership fix..."&lt;/span&gt;
    docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; postgres psql &lt;span class="nt"&gt;-U&lt;/span&gt; postgres &lt;span class="nt"&gt;-d&lt;/span&gt; mattermost &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"ALTER SCHEMA public OWNER TO mattermost;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true
    echo&lt;/span&gt; &lt;span class="s2"&gt;"Database schema permissions verified."&lt;/span&gt;
&lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"WARNING: Postgres container not running. Skipping DB patch."&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Ensure postgres is running before deploying Mattermost."&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# --- 4. Mobile-Ready Certificate Generation ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Phase 3: Generating Mobile-Ready SSL Certificate ---"&lt;/span&gt;

&lt;span class="c"&gt;# Detect LAN IP (First non-loopback IP)&lt;/span&gt;
&lt;span class="nv"&gt;LAN_IP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;hostname&lt;/span&gt; &lt;span class="nt"&gt;-I&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $1}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Detected LAN IP: &lt;/span&gt;&lt;span class="nv"&gt;$LAN_IP&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Define Paths for the LAN-specific cert&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CA_SERVICE_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;LAN_KEY_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CA_SERVICE_DIR&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$SERVICE_NAME&lt;/span&gt;&lt;span class="s2"&gt;.lan.key.pem"&lt;/span&gt;
&lt;span class="nv"&gt;LAN_CSR_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CA_SERVICE_DIR&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$SERVICE_NAME&lt;/span&gt;&lt;span class="s2"&gt;.lan.csr"&lt;/span&gt;
&lt;span class="nv"&gt;LAN_CERT_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CA_SERVICE_DIR&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$SERVICE_NAME&lt;/span&gt;&lt;span class="s2"&gt;.lan.crt.pem"&lt;/span&gt;
&lt;span class="nv"&gt;EXT_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CA_SERVICE_DIR&lt;/span&gt;&lt;span class="s2"&gt;/v3.lan.ext"&lt;/span&gt;

&lt;span class="c"&gt;# Destination in Mattermost volume&lt;/span&gt;
&lt;span class="nv"&gt;MM_CERTS_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MATTERMOST_BASE&lt;/span&gt;&lt;span class="s2"&gt;/certs"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LAN_CERT_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Existing LAN certificate found. Skipping generation."&lt;/span&gt;
&lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Generating new LAN certificate..."&lt;/span&gt;

    &lt;span class="c"&gt;# 1. Generate Key&lt;/span&gt;
    openssl genrsa &lt;span class="nt"&gt;-out&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LAN_KEY_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 4096

    &lt;span class="c"&gt;# 2. Create specific SAN config including the LAN IP&lt;/span&gt;
    &lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$EXT_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names

[alt_names]
DNS.1 = &lt;/span&gt;&lt;span class="nv"&gt;$SERVICE_NAME&lt;/span&gt;&lt;span class="sh"&gt;
DNS.2 = localhost
IP.1 = 127.0.0.1
IP.2 = &lt;/span&gt;&lt;span class="nv"&gt;$LAN_IP&lt;/span&gt;&lt;span class="sh"&gt;
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;    &lt;span class="c"&gt;# 3. Generate CSR&lt;/span&gt;
    openssl req &lt;span class="nt"&gt;-new&lt;/span&gt; &lt;span class="nt"&gt;-key&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LAN_KEY_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-out&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LAN_CSR_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-subj&lt;/span&gt; &lt;span class="s2"&gt;"/C=ZA/ST=Gauteng/L=Johannesburg/O=Local CICD/CN=&lt;/span&gt;&lt;span class="nv"&gt;$SERVICE_NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    &lt;span class="c"&gt;# 4. Sign with Root CA&lt;/span&gt;
    &lt;span class="nv"&gt;CA_ROOT_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CA_DIR&lt;/span&gt;&lt;span class="s2"&gt;/pki"&lt;/span&gt;
    &lt;span class="c"&gt;# Assuming CA pass is standard from previous articles&lt;/span&gt;
    openssl x509 &lt;span class="nt"&gt;-req&lt;/span&gt; &lt;span class="nt"&gt;-in&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LAN_CSR_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-CA&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CA_ROOT_DIR&lt;/span&gt;&lt;span class="s2"&gt;/certs/ca.pem"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-CAkey&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CA_ROOT_DIR&lt;/span&gt;&lt;span class="s2"&gt;/private/ca.key"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-CAcreateserial&lt;/span&gt; &lt;span class="nt"&gt;-out&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LAN_CERT_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-days&lt;/span&gt; 365 &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-sha256&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-extfile&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$EXT_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-passin&lt;/span&gt; pass:your_secure_password

    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Certificate generated: &lt;/span&gt;&lt;span class="nv"&gt;$LAN_CERT_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Always copy to the run directory&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Installing certificates to &lt;/span&gt;&lt;span class="nv"&gt;$MM_CERTS_DIR&lt;/span&gt;&lt;span class="s2"&gt;..."&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LAN_CERT_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MM_CERTS_DIR&lt;/span&gt;&lt;span class="s2"&gt;/cert.pem"&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LAN_KEY_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MM_CERTS_DIR&lt;/span&gt;&lt;span class="s2"&gt;/key.pem"&lt;/span&gt;

&lt;span class="c"&gt;# --- 5. Generate Scoped Environment File ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Phase 4: Generating mattermost.env ---"&lt;/span&gt;

&lt;span class="c"&gt;# NOTE: We remove quotes around simple string values (default_on, id_loaded)&lt;/span&gt;
&lt;span class="c"&gt;# because docker --env-file might pass the quotes literally, breaking validation.&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt; "&lt;/span&gt;&lt;span class="nv"&gt;$SCOPED_ENV_FILE&lt;/span&gt;&lt;span class="sh"&gt;"
# Scoped Environment for Mattermost
# Generated by 01-setup-mattermost.sh

# --- Core Identity ---
MM_SQLSETTINGS_DRIVERNAME=postgres
# Note: We use the internal Docker DNS 'postgres.cicd.local'
# FIX: sslmode=verify-full because we enforce SSL in Postgres and inject the CA into Mattermost
MM_SQLSETTINGS_DATASOURCE=postgres://mattermost:&lt;/span&gt;&lt;span class="nv"&gt;$MATTERMOST_DB_PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;@postgres.cicd.local:5432/mattermost?sslmode=verify-full&amp;amp;connect_timeout=10
MM_SERVICESETTINGS_SITEURL=https://&lt;/span&gt;&lt;span class="nv"&gt;$SERVICE_NAME&lt;/span&gt;&lt;span class="sh"&gt;:8065
MM_SERVICESETTINGS_LISTENADDRESS=:8065
# Security: Allow local IPs (needed for Webhooks from other containers)
# We strictly allow: localhost, Docker subnet, and Host LAN IP
MM_SERVICESETTINGS_ALLOWEDUNTRUSTEDINTERNALCONNECTIONS="127.0.0.1/8 172.30.0.0/24 &lt;/span&gt;&lt;span class="nv"&gt;$LAN_IP&lt;/span&gt;&lt;span class="sh"&gt;/32"

# --- TLS Configuration (Application Level) ---
MM_SERVICESETTINGS_CONNECTIONSECURITY=TLS
MM_SERVICESETTINGS_TLSCERTFILE=/mattermost/certs/cert.pem
MM_SERVICESETTINGS_TLSKEYFILE=/mattermost/certs/key.pem

# --- Security &amp;amp; Privacy ---
MM_SERVICESETTINGS_ENABLELOCALMODE=true
MM_EMAILSETTINGS_PUSHNOTIFICATIONCONTENTS=id_loaded
MM_FILESETTINGS_PUBLICLINKSALT=&lt;/span&gt;&lt;span class="nv"&gt;$MATTERMOST_PUBLIC_LINK_SALT&lt;/span&gt;&lt;span class="sh"&gt;
MM_SQLSETTINGS_ATRESTENCRYPTKEY=&lt;/span&gt;&lt;span class="nv"&gt;$MATTERMOST_AT_REST_KEY&lt;/span&gt;&lt;span class="sh"&gt;

# --- Developer Experience ---
MM_SERVICESETTINGS_ENABLELATEX=true
MM_SERVICESETTINGS_ENABLEINLINELATEX=true
MM_SERVICESETTINGS_COLLAPSEDTHREADS=default_on
MM_SERVICESETTINGS_ENABLECUSTOMGROUPS=true

# --- Announcements ---
MM_ANNOUNCEMENTSETTINGS_ENABLEBANNER=true
MM_ANNOUNCEMENTSETTINGS_BANNERTEXT="🚀 CI/CD City: Systems Operational"
MM_ANNOUNCEMENTSETTINGS_BANNERCOLOR="#20a83b"

# --- Advanced "Cool" Features ---

# 1. Performance Metrics (Port 8067)
MM_METRICSSETTINGS_ENABLE=true
MM_METRICSSETTINGS_LISTENADDRESS=:8067

# 2. Guest Access
MM_GUESTACCOUNTSSETTINGS_ENABLE=true

# 3. Automation Freedom
MM_SERVICESETTINGS_ENABLEBOTACCOUNTCREATION=true
MM_SERVICESETTINGS_ENABLEUSERACCESSTOKENS=true

# 4. Hardened Security (MFA)
MM_SERVICESETTINGS_ENABLEMULTIFACTORAUTHENTICATION=true

# 5. Urgent Messaging
MM_SERVICESETTINGS_POSTPRIORITY=true

# 6. Culture (Custom Emoji)
MM_SERVICESETTINGS_ENABLECUSTOMEMOJI=true

# --- Plugins (Force Enable for Entry Mode) ---
# NOTE: We only enable them here. Specific config is handled by 08-configure-plugins.py
# Added: com.mattermost.plugin-jenkins
MM_PLUGINSETTINGS_PLUGINSTATES={"playbooks":{"Enable":true},"focalboard":{"Enable":true},"com.mattermost.calls":{"Enable":true},"mattermost-ai":{"Enable":true},"com.github.manland.mattermost-plugin-gitlab":{"Enable":true},"jenkins":{"Enable":true}}

# --- WebRTC (The Radio Tower) ---
# 1. Connectivity (Moved to 8444 to avoid conflict with Artifactory on 8443)
MM_CALLS_UDP_SERVER_PORT=8444
MM_CALLS_TCP_SERVER_PORT=8444
MM_CALLS_ICE_SERVERS_CONFIGS=[{"urls":["turn:&lt;/span&gt;&lt;span class="nv"&gt;$LAN_IP&lt;/span&gt;&lt;span class="sh"&gt;:3478"],"username":"mattermost","credential":"&lt;/span&gt;&lt;span class="nv"&gt;$MATTERMOST_TURN_SECRET&lt;/span&gt;&lt;span class="sh"&gt;"}]

# 2. Network Stability
MM_CALLS_ICE_HOST_OVERRIDE=&lt;/span&gt;&lt;span class="nv"&gt;$LAN_IP&lt;/span&gt;&lt;span class="sh"&gt;

# 3. User Permissions
MM_CALLS_DEFAULT_ENABLED=true

# 4. Features
MM_CALLS_ALLOW_SCREEN_SHARING=true
MM_CALLS_ICE_HOST_PORT_OVERRIDE=8444

# 5. Mobile &amp;amp; CORS Fixes
MM_SERVICESETTINGS_ALLOWCORSFROM=*
MM_SERVICESETTINGS_ENABLEINSECUREOUTGOINGCONNECTIONS=true
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# Secure the env file (readable by owner only)&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;600 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SCOPED_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# --- 6. Final Permissions Lock ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Phase 5: Locking Permissions (UID 2000) ---"&lt;/span&gt;
&lt;span class="c"&gt;# FIX: We ONLY chown the directories that need to be bind-mounted.&lt;/span&gt;
&lt;span class="c"&gt;# We leave the .env file owned by the current user so docker run can read it.&lt;/span&gt;
&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; 2000:2000 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MATTERMOST_BASE&lt;/span&gt;&lt;span class="s2"&gt;/config"&lt;/span&gt;
&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; 2000:2000 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MATTERMOST_BASE&lt;/span&gt;&lt;span class="s2"&gt;/certs"&lt;/span&gt;

&lt;span class="c"&gt;# The Key file must be readable by the app (0600 owned by 2000)&lt;/span&gt;
&lt;span class="c"&gt;# (Already handled by the recursive chown above, but ensuring mode)&lt;/span&gt;
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;600 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MM_CERTS_DIR&lt;/span&gt;&lt;span class="s2"&gt;/key.pem"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✅ Setup Complete."&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   - Config written to &lt;/span&gt;&lt;span class="nv"&gt;$SCOPED_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;."&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   - Bind Mounts ownership transferred to UID 2000."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deconstructing the Architect
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. The "Split-Brain" Certificate Strategy (Phase 3)&lt;/strong&gt;&lt;br&gt;
Notice the &lt;code&gt;[alt_names]&lt;/code&gt; block. We explicitly define &lt;code&gt;IP.2 = $LAN_IP&lt;/code&gt;. This dynamic injection is what makes the certificate "Mobile-Ready." Unlike our standard scripts which only care about the DNS name (&lt;code&gt;mattermost.cicd.local&lt;/code&gt;), this script queries the host's actual network interface (&lt;code&gt;hostname -I&lt;/code&gt;) and bakes that physical address into the cryptographic identity. This ensures that when an Android phone connects to &lt;code&gt;192.168.0.x&lt;/code&gt;, the certificate matches the URL.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The Key Length Fix (Phase 1)&lt;/strong&gt;&lt;br&gt;
We use two different generator functions: &lt;code&gt;generate_secret&lt;/code&gt; (64 hex chars) and &lt;code&gt;generate_aes_key&lt;/code&gt; (16 hex chars). This is a critical distinction. The At-Rest Encryption Key and Plugin Encryption Keys rely on AES-256. This algorithm strictly requires a &lt;strong&gt;32-byte key&lt;/strong&gt;. If we used the standard 64-char generator (which results in 64 bytes when hex-encoded), the Mattermost server would crash on startup with a generic &lt;code&gt;invalid key size&lt;/code&gt; error. We are precise here to prevent runtime failures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The WebRTC Configuration (Phase 4)&lt;/strong&gt;&lt;br&gt;
In the environment file generation, we configure the &lt;strong&gt;Calls&lt;/strong&gt; plugin immediately.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;MM_CALLS_ICE_HOST_OVERRIDE=$LAN_IP&lt;/code&gt;: This forces the server to advertise the LAN IP, not the internal Docker IP (&lt;code&gt;172.x&lt;/code&gt;), ensuring the phone knows where to send the video packets.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;MM_CALLS_UDP_SERVER_PORT=8444&lt;/code&gt;: We shift the media port from the default (8443) to 8444. This avoids a collision with &lt;strong&gt;Artifactory&lt;/strong&gt; (Article 9), which already claimed 8443 for its HTTPS interface. In a "City," port discipline is mandatory.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;
  
  
  Chapter 4: The Radio Tower - Deploying Coturn
&lt;/h1&gt;
&lt;h2&gt;
  
  
  4.1 The NAT Traversal Challenge
&lt;/h2&gt;

&lt;p&gt;With our "Town Square" foundation laid, we must now build the infrastructure that allows us to see and hear each other. We are deploying the Video Conferencing stack using the Mattermost &lt;strong&gt;Calls&lt;/strong&gt; plugin.&lt;/p&gt;

&lt;p&gt;This requirement forces us to leave the comfortable world of HTTP and confront the chaotic reality of &lt;strong&gt;WebRTC&lt;/strong&gt; (Web Real-Time Communication).&lt;/p&gt;

&lt;p&gt;In our previous articles, every service we deployed—GitLab, Jenkins, SonarQube—communicated using TCP/IP over HTTP. This model is simple: the client opens a connection to the server, sends a request, and waits for a response. It is reliable, predictable, and remarkably tolerant of network layers like Docker's bridge network and Nginx reverse proxies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WebRTC is different.&lt;/strong&gt; It is designed for real-time audio and video, where latency is the enemy. It prefers &lt;strong&gt;UDP&lt;/strong&gt; over TCP because it's faster to drop a lost packet than to wait for retransmission (a glitch is better than a lag). More importantly, WebRTC attempts to establish a &lt;strong&gt;Peer-to-Peer (P2P)&lt;/strong&gt; connection directly between two devices to minimize latency.&lt;/p&gt;

&lt;p&gt;In a containerized environment, this P2P model breaks instantly.&lt;/p&gt;

&lt;p&gt;When your phone (on WiFi) tries to send video to the Mattermost server (in a container), it needs an IP address to target. However, the Mattermost container lives inside a Docker Bridge network. It has an internal IP (e.g., &lt;code&gt;172.18.0.5&lt;/code&gt;) that is completely invisible to the outside world. To make matters worse, your phone is likely behind its own NAT (Network Address Translation). This scenario is known as &lt;strong&gt;Double NAT&lt;/strong&gt;, and it acts as an unbridgeable moat for direct media streams.&lt;/p&gt;

&lt;p&gt;To bridge this moat, we need a &lt;strong&gt;TURN Server&lt;/strong&gt; (Traversal Using Relays around NAT). We will deploy &lt;strong&gt;Coturn&lt;/strong&gt;, the industry-standard open-source TURN server.&lt;/p&gt;

&lt;p&gt;Architecturally, Coturn acts as a &lt;strong&gt;"Radio Tower."&lt;/strong&gt; It sits on the absolute edge of our network. When direct P2P communication fails (which it always will in Docker), the phone sends its media packets to the Radio Tower. The Tower then relays those packets across the Docker boundary to the Mattermost container.&lt;/p&gt;
&lt;h2&gt;
  
  
  4.2 The Solution (&lt;code&gt;02-deploy-coturn.sh&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;But deploying Coturn brings its own "Dragon": &lt;strong&gt;The Port Range.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Unlike a web server that listens on a single port (443), a TURN server requires a massive range of ephemeral UDP ports—typically &lt;strong&gt;49,152 to 65,535&lt;/strong&gt;—to handle media streams for multiple users simultaneously. Every active call consumes a port.&lt;/p&gt;

&lt;p&gt;If we tried to deploy this using standard Docker Bridge networking, we would have to map every single one of these ports in the &lt;code&gt;docker run&lt;/code&gt; command. This creates two critical problems:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;The "Docker Proxy" Bottleneck:&lt;/strong&gt; For every mapped port, Docker spins up a userland proxy process (&lt;code&gt;docker-proxy&lt;/code&gt;). Asking Docker to manage 16,000+ proxy rules explodes the memory usage and adds significant CPU latency to every packet, killing call quality.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;IPTables Bloat:&lt;/strong&gt; Creating tens of thousands of NAT rules in the host's firewall table slows down networking for the entire system.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To solve this, we will make a rare exception to our "Isolation First" rule. We will deploy the Coturn container using &lt;strong&gt;Host Networking&lt;/strong&gt; (&lt;code&gt;--network host&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;This mode effectively removes the Docker network isolation layer for this specific container. Coturn will not have a private &lt;code&gt;172.x.x.x&lt;/code&gt; IP; it will bind directly to the physical network interface of your host machine (&lt;code&gt;192.168.x.x&lt;/code&gt;). This eliminates the need for port mapping entirely. It gives our Radio Tower a clear, unobstructed line of sight to your mobile device, ensuring that when you press "Join Call," the video flows instantly and efficiently.&lt;/p&gt;

&lt;p&gt;Create this file at &lt;code&gt;~/Documents/FromFirstPrinciples/articles/0011_cicd_part07_mattermost/02-deploy-coturn.sh&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;#               02-deploy-coturn.sh&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  The "Radio Tower" script.&lt;/span&gt;
&lt;span class="c"&gt;#  Deploys a Coturn STUN/TURN server for WebRTC media relay.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  1. Network: Uses '--network host' to bypass Docker NAT.&lt;/span&gt;
&lt;span class="c"&gt;#  2. Config:  Injects the shared secret generated in Step 01.&lt;/span&gt;
&lt;span class="c"&gt;#  3. Identity: Auto-detects LAN IP for the --external-ip flag.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🚀 Deploying Coturn (Radio Tower)..."&lt;/span&gt;

&lt;span class="c"&gt;# --- 1. Load Secrets ---&lt;/span&gt;
&lt;span class="nv"&gt;MASTER_ENV_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/cicd_stack/cicd.env"&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: Master env file not found at &lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi
&lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MATTERMOST_TURN_SECRET&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: MATTERMOST_TURN_SECRET not found in cicd.env"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Please run 01-setup-mattermost.sh first."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# --- 2. Detect Host IP ---&lt;/span&gt;
&lt;span class="c"&gt;# We need to tell Coturn what its external IP is so it can&lt;/span&gt;
&lt;span class="c"&gt;# advertise it to clients (phones/browsers).&lt;/span&gt;
&lt;span class="c"&gt;# hostname -I returns all IPs; awk '{print $1}' takes the first one.&lt;/span&gt;
&lt;span class="nv"&gt;LAN_IP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;hostname&lt;/span&gt; &lt;span class="nt"&gt;-I&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $1}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LAN_IP&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: Could not detect LAN IP."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"📡 Radio Tower Configuration:"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   - Listening IP: 0.0.0.0"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   - External IP:  &lt;/span&gt;&lt;span class="nv"&gt;$LAN_IP&lt;/span&gt;&lt;span class="s2"&gt; (Advertised to clients)"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   - Realm:        mattermost.cicd.local"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   - Network:      Host Mode (Bypassing Docker Bridge)"&lt;/span&gt;

&lt;span class="c"&gt;# --- 3. Clean Slate ---&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;coturn&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Stopping existing 'coturn'..."&lt;/span&gt;
    docker stop coturn
&lt;span class="k"&gt;fi
if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;-aq&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;coturn&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Removing existing 'coturn'..."&lt;/span&gt;
    docker &lt;span class="nb"&gt;rm &lt;/span&gt;coturn
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# --- 4. Deploy ---&lt;/span&gt;
&lt;span class="c"&gt;# We use the official coturn image.&lt;/span&gt;
&lt;span class="c"&gt;# We pass configuration flags directly to the command.&lt;/span&gt;
&lt;span class="c"&gt;# Note: --network host is critical here for UDP performance.&lt;/span&gt;

docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; coturn &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network&lt;/span&gt; host &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--restart&lt;/span&gt; always &lt;span class="se"&gt;\&lt;/span&gt;
  coturn/coturn &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--log-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;stdout &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--min-port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;49152 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--max-port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;65535 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--realm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mattermost.cicd.local &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--listening-ip&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0.0.0.0 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--external-ip&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$LAN_IP&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--use-auth-secret&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--static-auth-secret&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$MATTERMOST_TURN_SECRET&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✅ Coturn deployed."&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Verify logs with: docker logs -f coturn"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deconstructing the Radio Tower
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. The IP Detection (&lt;code&gt;hostname -I&lt;/code&gt;)&lt;/strong&gt;&lt;br&gt;
Coturn needs to know "who it is" to function correctly. When a phone asks for a relay candidate, Coturn must respond with an IP address that the phone can actually reach. We automate this by detecting the host's LAN IP at runtime and passing it to &lt;code&gt;--external-ip&lt;/code&gt;. If we hardcoded this or let it default, Coturn might advertise an internal loopback address, causing call failures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The "No-Map" Deployment (&lt;code&gt;--network host&lt;/code&gt;)&lt;/strong&gt;&lt;br&gt;
Notice that there are no &lt;code&gt;-p 3478:3478&lt;/code&gt; or &lt;code&gt;-p 49152:49152&lt;/code&gt; flags in the &lt;code&gt;docker run&lt;/code&gt; command. Because we used &lt;code&gt;--network host&lt;/code&gt;, the container essentially "becomes" the host networking stack. It opens these sockets directly on the host's interface. This is the secret to high-performance WebRTC in Docker.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The Shared Secret (&lt;code&gt;--static-auth-secret&lt;/code&gt;)&lt;/strong&gt;&lt;br&gt;
This ties back to &lt;strong&gt;Article 3&lt;/strong&gt;. We inject the &lt;code&gt;MATTERMOST_TURN_SECRET&lt;/code&gt;. This ensures that our Radio Tower isn't an open relay for the internet. It will only relay traffic for clients that present a valid token signed by our Mattermost server using this exact key.&lt;/p&gt;
&lt;h2&gt;
  
  
  4.3 The Verification (&lt;code&gt;test-turn-server.py&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;Before we try to connect a complex application like Mattermost to this Radio Tower, we must verify that the Tower is actually broadcasting. If we skip this step, debugging broken video calls later becomes a guessing game: is it the Android app? The certificate? The firewall? Or the TURN server itself?&lt;/p&gt;

&lt;p&gt;We will perform a "Smoke Test" using the industry-standard &lt;strong&gt;Trickle ICE&lt;/strong&gt; diagnostic tool.&lt;/p&gt;

&lt;p&gt;To do this securely, we cannot just guess a username and password. Our TURN server is protected by the Time-Limited Credential mechanism. We need to generate a valid, signed token that expires in 24 hours.&lt;/p&gt;

&lt;p&gt;We will write a small Python script to generate these credentials using the &lt;code&gt;MATTERMOST_TURN_SECRET&lt;/code&gt; from our environment file.&lt;/p&gt;

&lt;p&gt;Create this file at &lt;code&gt;~/Documents/FromFirstPrinciples/articles/0011_cicd_part07_mattermost/test-turn-server.py&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# https://webrtc.github.io/samples/src/content/peerconnection/trickle-ice/
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hmac&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;

&lt;span class="c1"&gt;# Load Secret
&lt;/span&gt;&lt;span class="n"&gt;env_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;home&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cicd_stack&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cicd.env&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;
&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MATTERMOST_TURN_SECRET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error: Could not find secret&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Generate Credentials (valid for 24 hours)
&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:testuser&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;dig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hmac&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sha1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dig&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;=== TURN Credentials (Valid 24h) ===&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Username: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Password: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;====================================&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deconstructing the Smoke Test
&lt;/h3&gt;

&lt;p&gt;This script implements the standard TURN REST API hashing algorithm. It combines a timestamp (valid for 24 hours) with a username, signs it with our secret key using HMAC-SHA1, and Base64 encodes the result. This matches exactly what the Mattermost server does internally when a user requests to join a call.&lt;/p&gt;

&lt;h3&gt;
  
  
  Execution: The Trickle ICE Test
&lt;/h3&gt;

&lt;p&gt;Now, we perform the physical test.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Generate Credentials:&lt;/strong&gt; Run the script on your host.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 test-turn-server.py
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Copy the &lt;strong&gt;Username&lt;/strong&gt; and &lt;strong&gt;Password&lt;/strong&gt; output.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Open the Diagnostics Tool:&lt;/strong&gt;&lt;br&gt;
On a &lt;strong&gt;different device&lt;/strong&gt; (like your phone or a laptop on the same WiFi, &lt;em&gt;not&lt;/em&gt; the host running Docker), open this URL:&lt;br&gt;
&lt;a href="https://webrtc.github.io/samples/src/content/peerconnection/trickle-ice/" rel="noopener noreferrer"&gt;https://webrtc.github.io/samples/src/content/peerconnection/trickle-ice/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Configure the Server:&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;* **STUN or TURN URI:** `turn:&amp;lt;YOUR_LAN_IP&amp;gt;:3478` (e.g., `turn:192.168.0.105:3478`)
* **TURN username:** (Paste from script)
* **TURN password:** (Paste from script)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Run the Test:&lt;/strong&gt; Click &lt;strong&gt;"Gather candidates"&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The Success Criteria:&lt;/strong&gt;&lt;br&gt;
You are looking for a specific row in the output table.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Component Type:&lt;/strong&gt; &lt;code&gt;rtcp&lt;/code&gt; or &lt;code&gt;rtp&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type:&lt;/strong&gt; &lt;strong&gt;&lt;code&gt;relay&lt;/code&gt;&lt;/strong&gt; (This is the critical keyword).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Protocol:&lt;/strong&gt; &lt;code&gt;udp&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you see a candidate of type &lt;strong&gt;&lt;code&gt;relay&lt;/code&gt;&lt;/strong&gt;, it means your device successfully contacted the Coturn server, authenticated with the secret, and received a relay address. The Radio Tower is operational. If you only see &lt;code&gt;host&lt;/code&gt; or &lt;code&gt;srflx&lt;/code&gt; candidates, the TURN server is unreachable (check your firewall) or authentication failed (check your secret).&lt;/p&gt;
&lt;h1&gt;
  
  
  Chapter 5: Deployment - Launching the Town Square
&lt;/h1&gt;
&lt;h2&gt;
  
  
  5.1 The "Clean Slate" Protocol
&lt;/h2&gt;

&lt;p&gt;We have tuned our database, generated our mobile-ready certificates, and erected our radio tower. The ground is prepared. It is time to deploy the application itself.&lt;/p&gt;

&lt;p&gt;We will use our standard &lt;strong&gt;"Launcher"&lt;/strong&gt; pattern. This script (&lt;code&gt;03-deploy-mattermost.sh&lt;/code&gt;) is the enforcement mechanism for our infrastructure. It does not just "start" the container; it ensures that every deployment begins with a predictable, clean state.&lt;/p&gt;

&lt;p&gt;This "Clean Slate" protocol—stopping the container, removing it, and verifying volumes before launching—is critical for "Immutable Infrastructure." It guarantees that if we change a configuration variable in &lt;code&gt;mattermost.env&lt;/code&gt; or update a certificate, the new container will pick up those changes immediately. We never rely on &lt;code&gt;docker restart&lt;/code&gt;, which often preserves stale state.&lt;/p&gt;

&lt;p&gt;This script also handles a specific architectural requirement for Mattermost: &lt;strong&gt;Plugin Persistence&lt;/strong&gt;. Unlike Jenkins, where plugins are baked into the image or volume in a single blob, Mattermost benefits from separating its storage concerns. We create four distinct named volumes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;code&gt;mattermost-data&lt;/code&gt;: For file uploads and images.&lt;/li&gt;
&lt;li&gt; &lt;code&gt;mattermost-logs&lt;/code&gt;: For audit trails (critical for security).&lt;/li&gt;
&lt;li&gt; &lt;code&gt;mattermost-plugins&lt;/code&gt;: For server-side plugin binaries.&lt;/li&gt;
&lt;li&gt; &lt;code&gt;mattermost-client-plugins&lt;/code&gt;: For the webapp frontend code of those plugins.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By separating these, we ensure that a plugin upgrade doesn't accidentally corrupt our file store, and that we can wipe plugins if necessary without losing user data.&lt;/p&gt;

&lt;p&gt;Create this file at &lt;code&gt;~/Documents/FromFirstPrinciples/articles/0011_cicd_part07_mattermost/03-deploy-mattermost.sh&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;#               03-deploy-mattermost.sh&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  The "Town Square" script.&lt;/span&gt;
&lt;span class="c"&gt;#  Deploys Mattermost Enterprise Edition (Entry Mode).&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  1. Network: Connects to 'cicd-net' for internal comms.&lt;/span&gt;
&lt;span class="c"&gt;#  2. Ports:&lt;/span&gt;
&lt;span class="c"&gt;#     - 8065 (TCP): Main UI/API (Exposed to LAN).&lt;/span&gt;
&lt;span class="c"&gt;#     - 8443 (UDP): Calls Plugin SFU (Exposed to LAN).&lt;/span&gt;
&lt;span class="c"&gt;#     - 8067 (TCP): Metrics (Exposed to Localhost).&lt;/span&gt;
&lt;span class="c"&gt;#  3. Trust:   Mounts Host's ca-certificates.crt (Distroless fix).&lt;/span&gt;
&lt;span class="c"&gt;#  4. Config:  Injects 'mattermost.env' (12-Factor).&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🚀 Deploying Mattermost (Town Square)..."&lt;/span&gt;

&lt;span class="c"&gt;# --- 1. Define Paths ---&lt;/span&gt;
&lt;span class="nv"&gt;HOST_CICD_ROOT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/cicd_stack"&lt;/span&gt;
&lt;span class="nv"&gt;MATTERMOST_BASE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOST_CICD_ROOT&lt;/span&gt;&lt;span class="s2"&gt;/mattermost"&lt;/span&gt;
&lt;span class="nv"&gt;SCOPED_ENV_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MATTERMOST_BASE&lt;/span&gt;&lt;span class="s2"&gt;/mattermost.env"&lt;/span&gt;

&lt;span class="c"&gt;# --- 2. Prerequisite Checks ---&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SCOPED_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: mattermost.env not found."&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Please run 01-setup-mattermost.sh first."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# --- 3. Volume Management ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Verifying Storage Volumes ---"&lt;/span&gt;
docker volume create mattermost-data &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null
docker volume create mattermost-logs &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null
docker volume create mattermost-plugins &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null
docker volume create mattermost-client-plugins &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null
&lt;span class="c"&gt;# Note: Bleve indexes volume removed (Deprecated in v11)&lt;/span&gt;

&lt;span class="c"&gt;# --- 4. Clean Slate ---&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mattermost&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Stopping existing 'mattermost'..."&lt;/span&gt;
    docker stop mattermost
&lt;span class="k"&gt;fi
if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;-aq&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mattermost&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Removing existing 'mattermost'..."&lt;/span&gt;
    docker &lt;span class="nb"&gt;rm &lt;/span&gt;mattermost
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# --- 5. Deploy ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Launching Container ---"&lt;/span&gt;

&lt;span class="c"&gt;# CRITICAL PORTS:&lt;/span&gt;
&lt;span class="c"&gt;# 8065: Main HTTP traffic (LAN access)&lt;/span&gt;
&lt;span class="c"&gt;# 8443/udp: Calls Plugin Media/SFU (LAN access for calls)&lt;/span&gt;
&lt;span class="c"&gt;# 8067: Metrics (Localhost only, for Prometheus later)&lt;/span&gt;

docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; mattermost &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--restart&lt;/span&gt; always &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network&lt;/span&gt; cicd-net &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--hostname&lt;/span&gt; mattermost.cicd.local &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--publish&lt;/span&gt; 0.0.0.0:8065:8065 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--publish&lt;/span&gt; 0.0.0.0:8444:8444/udp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--publish&lt;/span&gt; 0.0.0.0:8444:8444/tcp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--publish&lt;/span&gt; 127.0.0.1:8067:8067 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--env-file&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SCOPED_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MATTERMOST_BASE&lt;/span&gt;&lt;span class="s2"&gt;/config"&lt;/span&gt;:/mattermost/config:rw &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MATTERMOST_BASE&lt;/span&gt;&lt;span class="s2"&gt;/certs"&lt;/span&gt;:/mattermost/certs:ro &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; mattermost-data:/mattermost/data &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; mattermost-logs:/mattermost/logs &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; mattermost-plugins:/mattermost/plugins &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; mattermost-client-plugins:/mattermost/client/plugins &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; /etc/ssl/certs/ca-certificates.crt:/etc/ssl/certs/ca-certificates.crt:ro &lt;span class="se"&gt;\&lt;/span&gt;
  mattermost/mattermost-enterprise-edition:release-11

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✅ Mattermost deployed."&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   - Main URL: https://mattermost.cicd.local:8065"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   - Metrics:  http://127.0.0.1:8067/metrics"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   - Logs:     docker logs -f mattermost"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   - Trust:    Host CA bundle injected."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deconstructing the Launcher
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. The Trust Injection (&lt;code&gt;/etc/ssl/certs/...&lt;/code&gt;)&lt;/strong&gt;&lt;br&gt;
This single line solves the "Island Problem" we faced with SonarQube and Jenkins. We mount the host's CA bundle directly over the container's CA bundle. Because we previously installed our Root CA on the host (Article 6), this "brain transplant" allows Mattermost to instantly trust &lt;code&gt;gitlab.cicd.local&lt;/code&gt; and &lt;code&gt;jenkins.cicd.local&lt;/code&gt;. Without this, every integration webhook would fail with a certificate error.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The Port Strategy (&lt;code&gt;8444&lt;/code&gt;)&lt;/strong&gt;&lt;br&gt;
Notice we map port &lt;strong&gt;8444&lt;/strong&gt; for both TCP and UDP. This corresponds to the &lt;code&gt;MM_CALLS_UDP_SERVER_PORT&lt;/code&gt; setting we configured in the Architect script. By explicitly mapping this, we punch a hole through the Docker NAT for our relayed media packets coming from the Coturn server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The Image Selection (&lt;code&gt;enterprise-edition:release-11&lt;/code&gt;)&lt;/strong&gt;&lt;br&gt;
We explicitly pull the &lt;strong&gt;Enterprise Edition&lt;/strong&gt;. As discussed in Chapter 2, this—combined with the lack of a license key—activates "Entry Mode," giving us access to Boards and Playbooks which are absent in the Team Edition image.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. The Localhost Bind (&lt;code&gt;127.0.0.1:8067&lt;/code&gt;)&lt;/strong&gt;&lt;br&gt;
For the metrics port, we bind strictly to &lt;code&gt;127.0.0.1&lt;/code&gt;. We do not want our internal performance metrics exposed to the LAN. This allows a future Prometheus instance (running in the same &lt;code&gt;cicd-net&lt;/code&gt;) to scrape metrics, or us to curl them from the host, but prevents casual snooping from the office WiFi.&lt;/p&gt;
&lt;h1&gt;
  
  
  Chapter 6: The Mobile Frontier - Connecting Android
&lt;/h1&gt;
&lt;h2&gt;
  
  
  6.1 The "Generic Failure" Barrier
&lt;/h2&gt;

&lt;p&gt;We have launched our "Town Square." From your desktop machine, you can likely open a browser, navigate to &lt;code&gt;https://mattermost.cicd.local:8065&lt;/code&gt;, and see the login screen. The green lock icon is present because your desktop OS trusts the Root CA we installed in Article 6.&lt;/p&gt;

&lt;p&gt;Now, we attempt to extend this perimeter to the mobile frontier.&lt;/p&gt;

&lt;p&gt;Connect your Android device to the same WiFi network as your host machine. Open the official Mattermost app. Since we cannot easily configure DNS on a standard Android phone to resolve &lt;code&gt;.local&lt;/code&gt; domains, we bypass the DNS system entirely and enter the raw IP address:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Server URL:&lt;/strong&gt; &lt;code&gt;https://192.168.X.X:8065&lt;/code&gt; (Replace with your LAN IP)&lt;/p&gt;

&lt;p&gt;You will be met with an immediate, generic failure: &lt;strong&gt;"Cannot connect to the server."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This error message is dangerously misleading. It implies a network timeout or a firewall block. You might waste time checking &lt;code&gt;iptables&lt;/code&gt; or Docker port mappings. However, the connection is physically reaching the server; it is being rejected at the cryptographic layer.&lt;/p&gt;

&lt;p&gt;This is the "Trust Gap." Your Android device has its own segregated store of trusted Certificate Authorities (Google, DigiCert, Let's Encrypt). It has absolutely no knowledge of the "CICD-Root-CA" we generated on our laptop. When the server presents its "Mobile-Ready" certificate, the phone sees a valid cryptographic signature from an unknown entity. It assumes a Man-in-the-Middle attack is in progress and creates a hard stop at the TLS layer.&lt;/p&gt;

&lt;p&gt;To fix this, we cannot just change a setting in the app. We must surgically intervene in the device's operating system.&lt;/p&gt;
&lt;h2&gt;
  
  
  6.2 The Manual Trust Protocol
&lt;/h2&gt;

&lt;p&gt;To breach this barrier, we must perform a manual key exchange. We need to take the &lt;strong&gt;Root CA&lt;/strong&gt; (&lt;code&gt;ca.pem&lt;/code&gt;)—the "Master Key" that signed our Mattermost certificate—and import it into the Android "User Credentials" store. This explicitly tells the operating system: &lt;em&gt;"Trust any certificate signed by this file."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is a physical process that varies slightly by Android version, but the core protocol is universal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Transport the Key&lt;/strong&gt;&lt;br&gt;
First, we must get the file onto the device. Since we are simulating an air-gapped environment, we would typically use a USB cable. For simplicity in this lab, email the &lt;code&gt;~/cicd_stack/ca/pki/certs/ca.pem&lt;/code&gt; file to an account accessible on the phone.&lt;br&gt;
Open the email on your Android device and save the attachment to your &lt;strong&gt;Downloads&lt;/strong&gt; folder.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: The Security Settings&lt;/strong&gt;&lt;br&gt;
Android buries certificate management deep within its security menus to prevent users from accidentally installing malicious roots.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Open &lt;strong&gt;Settings&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; In the search bar at the top, type &lt;strong&gt;"certificate"&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; Select &lt;strong&gt;"CA certificate"&lt;/strong&gt; (often found under &lt;em&gt;Encryption &amp;amp; Credentials&lt;/em&gt; or &lt;em&gt;Install from storage&lt;/em&gt;).&lt;/li&gt;
&lt;li&gt; If prompted with a frightening warning ("Your data won't be private"), click &lt;strong&gt;"Install anyway"&lt;/strong&gt;. This warning exists because a malicious CA could inspect your traffic; however, in this case, &lt;em&gt;we&lt;/em&gt; are the CA.&lt;/li&gt;
&lt;li&gt; Confirm your identity by entering your &lt;strong&gt;Device PIN&lt;/strong&gt; or &lt;strong&gt;Pattern&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Step 3: The Import and Verification&lt;/strong&gt;&lt;br&gt;
The file explorer will open. Navigate to your &lt;strong&gt;Downloads&lt;/strong&gt; folder (or wherever you saved the file).&lt;br&gt;
Select the &lt;code&gt;ca.pem&lt;/code&gt; file.&lt;br&gt;
You should see a brief "toast" notification: &lt;strong&gt;"CA certificate installed."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To verify this, we must dig into the user credential store. On modern Android versions, the path is often convoluted:&lt;br&gt;
Navigate to &lt;strong&gt;Fingerprints, face data and screen lock&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Privacy&lt;/strong&gt; -&amp;gt; &lt;strong&gt;More security settings&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Encryption and credentials&lt;/strong&gt; -&amp;gt; &lt;strong&gt;User credentials&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In this list, you should see your custom CA (e.g., &lt;code&gt;Local CICD Root CA&lt;/code&gt;). With the root established, the phone now possesses the cryptographic chain of trust required to validate our server's identity.&lt;/p&gt;
&lt;h2&gt;
  
  
  6.3 The "CORS" Dragon
&lt;/h2&gt;

&lt;p&gt;You might expect that installing the certificate would be the end of the battle. You return to the Mattermost app, enter the server URL (&lt;code&gt;https://192.168.x.x:8065&lt;/code&gt;), and hit connect.&lt;/p&gt;

&lt;p&gt;If we had not carefully configured our environment variables in Chapter 3, you would likely hit a second, invisible wall. The app might let you log in, but then immediately disconnect. Or, more subtly, text messages would work, but the status indicators would never update, and video calls would fail to initiate.&lt;/p&gt;

&lt;p&gt;This is the &lt;strong&gt;WebSocket&lt;/strong&gt; layer failing.&lt;/p&gt;

&lt;p&gt;Mattermost uses a persistent WebSocket connection for real-time events (typing indicators, new messages, call signaling). When a mobile app initiates this connection, it sends an HTTP Origin header. Unlike a browser which sends the domain name, mobile apps (depending on the framework) often send &lt;code&gt;null&lt;/code&gt; or the raw IP address as the Origin.&lt;/p&gt;

&lt;p&gt;By default, the Mattermost server is strict. It checks the Origin header against its own &lt;code&gt;SiteURL&lt;/code&gt;. If they don't match exactly, it slams the door on the WebSocket to prevent Cross-Site WebSocket Hijacking (CSWSH).&lt;/p&gt;

&lt;p&gt;Because we are accessing the server via an IP address (&lt;code&gt;192.168.x.x&lt;/code&gt;) but the server thinks its name is &lt;code&gt;mattermost.cicd.local&lt;/code&gt;, this check fails. The text API (REST) works, but the real-time API (WebSocket) dies.&lt;/p&gt;

&lt;p&gt;We solved this preemptively in &lt;strong&gt;Section 3.4&lt;/strong&gt;. By setting &lt;code&gt;MM_SERVICESETTINGS_ALLOWCORSFROM=*&lt;/code&gt;, we instructed the server to drop its shield and accept WebSocket connections from any origin. In a public internet deployment, this would be a security risk. In our private &lt;code&gt;cicd-net&lt;/code&gt; fortress, it is a necessary concession to allow our mobile devices to speak freely with the Command Center.&lt;/p&gt;

&lt;p&gt;With the Certificate installed and CORS unlocked, your mobile Command Center is now fully operational. You can log in, browse channels, and—most importantly—prepare to receive signals from the city we are about to wire up.&lt;/p&gt;
&lt;h1&gt;
  
  
  Chapter 7: The Wiring (Part 1) - The Silent Observer
&lt;/h1&gt;
&lt;h2&gt;
  
  
  7.1 The "Click-Ops" Trap
&lt;/h2&gt;

&lt;p&gt;Our Command Center is online, but it is currently a ghost town. It has no teams, no channels, and no users other than the admin.&lt;/p&gt;

&lt;p&gt;In a typical "hobbyist" deployment, this is where you would start clicking. You would log into the web UI, click "Create Team," type "Engineering," click "Create Channel," type "builds," go to the System Console, create a Bot Account, copy the token, save it to a text file... and repeat this process for every tool you want to integrate.&lt;/p&gt;

&lt;p&gt;This manual approach—often called "Click-Ops"—is an architectural trap.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;It is not reproducible.&lt;/strong&gt; If your server crashes and you have to redeploy, you have to remember every single button click.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;It is insecure.&lt;/strong&gt; Copy-pasting access tokens and webhook URLs through the browser clipboard is a great way to accidentally expose secrets or lose them.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;It scales poorly.&lt;/strong&gt; Managing permissions for three tools is annoying; managing them for thirty is a full-time job.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We are building a &lt;strong&gt;Software Supply Chain&lt;/strong&gt;, not a chatroom. Our infrastructure must be defined as code. The creation of our "Engineering" team, our standard channels (&lt;code&gt;#builds&lt;/code&gt;, &lt;code&gt;#alerts&lt;/code&gt;, &lt;code&gt;#code-reviews&lt;/code&gt;), and the bot accounts that own them should be scripted, versioned, and executed automatically.&lt;/p&gt;

&lt;p&gt;We need an "Electrician"—a script that walks into the empty building and wires up the lights before the residents arrive.&lt;/p&gt;
&lt;h2&gt;
  
  
  7.2 The Electrician (&lt;code&gt;04-configure-integrations.py&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;To achieve this automation, we rely on &lt;code&gt;mmctl&lt;/code&gt;, the official command-line tool for Mattermost. Unlike the web API, &lt;code&gt;mmctl&lt;/code&gt; has a superpower: &lt;strong&gt;Local Mode&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When running inside the container (or communicating via the Docker socket), &lt;code&gt;mmctl&lt;/code&gt; can execute administrative commands without a username or password. It speaks directly to the server process over a Unix socket. This solves the "Bootstrap Paradox"—how do you authenticate to the API to create the first admin user if you don't have an admin user yet?&lt;/p&gt;

&lt;p&gt;We will wrap &lt;code&gt;mmctl&lt;/code&gt; in a Python script. While Bash is great for plumbing, Python is superior for parsing JSON responses and handling the logic flow of "If this webhook exists, don't create it; if it doesn't, create it and save the secret."&lt;/p&gt;

&lt;p&gt;This script is our &lt;strong&gt;Electrician&lt;/strong&gt;. It establishes the taxonomy of our city:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;#builds:&lt;/strong&gt; The noisy factory floor where Jenkins reports status.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;#code-reviews:&lt;/strong&gt; The library where GitLab announces changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;#alerts:&lt;/strong&gt; The dedicated red-phone line for SonarQube quality gate failures.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Create this file at &lt;code&gt;~/Documents/FromFirstPrinciples/articles/0011_cicd_part07_mattermost/04-configure-integrations.py&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env python3
&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;secrets&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;

&lt;span class="c1"&gt;# --- Configuration ---
&lt;/span&gt;&lt;span class="n"&gt;CICD_ROOT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;HOME&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cicd_stack&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;ENV_FILE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CICD_ROOT&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cicd.env&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;CONTAINER_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mattermost&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="c1"&gt;# We use --local to bypass authentication (requires EnableLocalMode=true)
&lt;/span&gt;&lt;span class="n"&gt;MMCTL_CMD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;docker&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;exec&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-i&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CONTAINER_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mmctl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--local&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;ADMIN_USER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;warren.jitsing&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="c1"&gt;# The user who will own the webhooks
&lt;/span&gt;
&lt;span class="c1"&gt;# --- Entities to Create ---
&lt;/span&gt;&lt;span class="n"&gt;TEAM_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;engineering&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;CHANNELS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;builds&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;code-reviews&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;alerts&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;town-square&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_mmctl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;allow_fail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Runs an mmctl command inside the container and returns parsed JSON.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MMCTL_CMD&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# check=True is removed so we can handle the return code manually
&lt;/span&gt;        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;capture_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;returncode&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;allow_fail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error running mmctl: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

        &lt;span class="c1"&gt;# Attempt 1: Parse the full output as JSON
&lt;/span&gt;        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JSONDecodeError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# Attempt 2: Fallback for mixed output
&lt;/span&gt;            &lt;span class="n"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;allow_fail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Unexpected error parsing mmctl output: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;read_env_file&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Reads the current state of cicd.env.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;ENV_FILE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ENV_FILE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;append_to_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Appends a secret to the master cicd.env file.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   💾 Writing &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; to cicd.env...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ENV_FILE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\"\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wait_for_server&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;⏳ Waiting for Mattermost to be ready...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;docker&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;exec&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CONTAINER_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mmctl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--local&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;system&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;version&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DEVNULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DEVNULL&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;✅ Mattermost is responding.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CalledProcessError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;❌ Timeout waiting for Mattermost.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;ENV_FILE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;❌ Error: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ENV_FILE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; not found.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;wait_for_server&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;env_content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;read_env_file&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;# 1. Create Team
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--- Configuring Team: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;TEAM_NAME&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; ---&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;run_mmctl&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;team&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;create&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TEAM_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--display-name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Engineering&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;allow_fail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   ✅ Team &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;TEAM_NAME&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; created.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   ℹ️  Team &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;TEAM_NAME&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; likely exists.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# FIX: Add Admin User to Team so they can own webhooks
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   Ensuring &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ADMIN_USER&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; is in &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;TEAM_NAME&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;run_mmctl&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;team&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;users&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;add&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TEAM_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ADMIN_USER&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;allow_fail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 2. Create Channels
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--- Configuring Channels ---&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;channel&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;CHANNELS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;docker&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;exec&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CONTAINER_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mmctl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--local&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;channel&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;create&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
             &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--team&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TEAM_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--display-name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;capitalize&lt;/span&gt;&lt;span class="p"&gt;()],&lt;/span&gt;
            &lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DEVNULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DEVNULL&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   ✅ Channel &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; ensured.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 3. Configure Jenkins Integration (Bot + Webhook)
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--- Configuring Jenkins Integration ---&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 3a. Ensure Bot User Exists (Identity)
&lt;/span&gt;    &lt;span class="n"&gt;bot_password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;JENKINS_BOT_PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;env_content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;env_content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;splitlines&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;JENKINS_BOT_PASSWORD=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;bot_password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'"'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   🎲 Generating high-entropy Bot Password...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;bot_password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;secrets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;token_urlsafe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;append_to_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;JENKINS_BOT_PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bot_password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;env_content&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;JENKINS_BOT_PASSWORD=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;bot_password&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="n"&gt;bot_email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;jenkins-bot@cicd.local&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;bot_user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;jenkins-bot&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   Ensuring Jenkins Bot User exists...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;run_mmctl&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;create&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--email&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bot_email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--username&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bot_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--password&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bot_password&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;allow_fail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;run_mmctl&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;verify&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bot_user&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;allow_fail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;run_mmctl&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;team&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;users&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;add&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TEAM_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bot_user&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;allow_fail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 3b. Create Webhook for Notifications (Required by Jenkins Notification Plugin)
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;JENKINS_MATTERMOST_WEBHOOK&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;env_content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   ℹ️  JENKINS_MATTERMOST_WEBHOOK already exists. Skipping.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   Creating Incoming Webhook for #builds...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# FIX: Use fully qualified channel name and ADMIN_USER ownership
&lt;/span&gt;        &lt;span class="n"&gt;hook_res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;run_mmctl&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;webhook&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;create-incoming&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ADMIN_USER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--channel&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;TEAM_NAME&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:builds&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--display-name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Jenkins&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--description&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Build Notifications&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;hook_id&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hook_res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;webhook_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://mattermost.cicd.local:8065/hooks/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;hook_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="nf"&gt;append_to_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;JENKINS_MATTERMOST_WEBHOOK&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;webhook_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;env_content&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;JENKINS_MATTERMOST_WEBHOOK=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;webhook_url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="c1"&gt;# 4. Create SonarQube Webhook (#alerts)
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--- Configuring SonarQube Webhook ---&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SONAR_MATTERMOST_WEBHOOK&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;env_content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   ℹ️  SONAR_MATTERMOST_WEBHOOK already exists. Skipping.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   Creating Incoming Webhook for #alerts...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;hook_res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;run_mmctl&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;webhook&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;create-incoming&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ADMIN_USER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--channel&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;TEAM_NAME&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:alerts&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--display-name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SonarQube&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--description&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Quality Gate Alerts&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;hook_id&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hook_res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;webhook_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://mattermost.cicd.local:8065/hooks/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;hook_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="nf"&gt;append_to_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SONAR_MATTERMOST_WEBHOOK&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;webhook_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;env_content&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;SONAR_MATTERMOST_WEBHOOK=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;webhook_url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="c1"&gt;# 5. Create GitLab Webhook (#code-reviews)
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--- Configuring GitLab Webhook ---&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MATTERMOST_CODE_REVIEW_WEBHOOK&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;env_content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   ℹ️  MATTERMOST_CODE_REVIEW_WEBHOOK already exists. Skipping.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   Creating Incoming Webhook for #code-reviews...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;hook_res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;run_mmctl&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;webhook&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;create-incoming&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ADMIN_USER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--channel&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;TEAM_NAME&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:code-reviews&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--display-name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GitLab&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--description&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Commit and Merge Request Events&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;hook_id&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hook_res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;webhook_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://mattermost.cicd.local:8065/hooks/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;hook_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="nf"&gt;append_to_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MATTERMOST_CODE_REVIEW_WEBHOOK&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;webhook_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;✅ Configuration Complete.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   Restart Jenkins/SonarQube to pick up new secrets.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deconstructing the Electrician
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. The "Local Mode" Bypass&lt;/strong&gt;&lt;br&gt;
The script uses &lt;code&gt;docker exec ... mmctl --local&lt;/code&gt;. This is the key. It allows us to configure the server from the outside without needing to manually create an admin user first or wrestle with login tokens. We are manipulating the server's brain directly via its command socket.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Idempotency (The "Check First" Logic)&lt;/strong&gt;&lt;br&gt;
Notice how the script checks for existing environment variables (&lt;code&gt;if "JENKINS_MATTERMOST_WEBHOOK" in env_content&lt;/code&gt;). This prevents duplicate webhooks. If you run the script ten times, it will only create the resources once. This is a core tenet of Infrastructure as Code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The Secret Extraction&lt;/strong&gt;&lt;br&gt;
When &lt;code&gt;mmctl&lt;/code&gt; creates a webhook, it returns a JSON object containing the ID. We parse this ID immediately, construct the full URL (&lt;code&gt;https://mattermost.../hooks/ID&lt;/code&gt;), and append it to our master &lt;code&gt;cicd.env&lt;/code&gt; file. This eliminates the "Copy-Paste Risk." The secret moves directly from the generator to the vault, untouched by human hands.&lt;/p&gt;
&lt;h2&gt;
  
  
  7.3 The Connector (&lt;code&gt;05-connect-jenkins.sh&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;We have created the destination (the &lt;code&gt;#builds&lt;/code&gt; channel) and generated the address (the webhook URL). Now we must hand that address to the Factory.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Article 8&lt;/strong&gt;, we deployed Jenkins using &lt;strong&gt;Configuration as Code (JCasC)&lt;/strong&gt;. We did not use the UI to set up our system message or credentials. We defined them in a YAML file (&lt;code&gt;jenkins.yaml&lt;/code&gt;). To add Mattermost notifications, we must modify that YAML file.&lt;/p&gt;

&lt;p&gt;However, we cannot simply hardcode the webhook URL into the YAML. That would be a security violation (checking secrets into git) and an idempotency failure (the secret is generated dynamically by the previous script).&lt;/p&gt;

&lt;p&gt;We need a "Hot-Patcher." We need a script that reads the fresh secret from &lt;code&gt;cicd.env&lt;/code&gt;, injects it into the &lt;code&gt;jenkins.env&lt;/code&gt; file, and then updates the &lt;code&gt;jenkins.yaml&lt;/code&gt; configuration to use that variable.&lt;/p&gt;

&lt;p&gt;We will split this into two parts: a Python helper to handle the YAML surgery, and a Bash script to orchestrate the deployment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part 1: The YAML Surgeon&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Create this file at &lt;code&gt;~/Documents/FromFirstPrinciples/articles/0011_cicd_part07_mattermost/update_jcasc_mattermost.py&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env python3
&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;yaml&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="c1"&gt;# Target the LIVE configuration
&lt;/span&gt;&lt;span class="n"&gt;JCAS_FILE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expanduser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;~/cicd_stack/jenkins/config/jenkins.yaml&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update_jcasc&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[INFO] Reading JCasC file: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;JCAS_FILE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JCAS_FILE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;jcasc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;yaml&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;safe_load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;FileNotFoundError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[ERROR] File not found: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;JCAS_FILE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Configure Mattermost Notification Plugin (Global)
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[INFO] Injecting Mattermost Global Configuration...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;unclassified&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;jcasc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;jcasc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;unclassified&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="c1"&gt;# CORRECTED SCHEMA:
&lt;/span&gt;    &lt;span class="c1"&gt;# 1. Valid attributes only (endpoint, room, buildServerUrl, icon).
&lt;/span&gt;    &lt;span class="c1"&gt;# 2. Room set to 'engineering@builds' (Team@Channel format verified by user).
&lt;/span&gt;    &lt;span class="n"&gt;jcasc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;unclassified&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;mattermostNotifier&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;endpoint&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;${MATTERMOST_JENKINS_WEBHOOK_URL}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;room&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;engineering@builds&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;buildServerUrl&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https://jenkins.cicd.local:10400/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;icon&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https://mattermost.org/wp-content/uploads/2016/04/icon.png&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;# Write back to file
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[INFO] Writing updated JCasC file...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JCAS_FILE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;w&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;yaml&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jcasc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default_flow_style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sort_keys&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[INFO] JCasC update complete.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;update_jcasc&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Part 2: The Orchestrator&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This script ties it all together. It reads the secret, runs the python helper, and then triggers a Jenkins redeployment to pick up the changes.&lt;/p&gt;

&lt;p&gt;Create this file at &lt;code&gt;~/Documents/FromFirstPrinciples/articles/0011_cicd_part07_mattermost/05-connect-jenkins.sh&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;#               05-connect-jenkins.sh&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  Integrates Jenkins with Mattermost via Webhook.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  1. Secrets: Reads JENKINS_MATTERMOST_WEBHOOK (host)&lt;/span&gt;
&lt;span class="c"&gt;#              -&amp;gt; Injects MATTERMOST_JENKINS_WEBHOOK_URL (jenkins.env).&lt;/span&gt;
&lt;span class="c"&gt;#  2. JCasC:   Updates jenkins.yaml with Notifier config.&lt;/span&gt;
&lt;span class="c"&gt;#  3. Apply:   Re-deploys Jenkins.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="c"&gt;# --- Paths ---&lt;/span&gt;
&lt;span class="nv"&gt;CICD_ROOT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/cicd_stack"&lt;/span&gt;
&lt;span class="c"&gt;# Adjust this path if your Jenkins article folder is named differently&lt;/span&gt;
&lt;span class="nv"&gt;JENKINS_MODULE_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/Documents/FromFirstPrinciples/articles/0008_cicd_part04_jenkins"&lt;/span&gt;
&lt;span class="nv"&gt;JENKINS_ENV_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JENKINS_MODULE_DIR&lt;/span&gt;&lt;span class="s2"&gt;/jenkins.env"&lt;/span&gt;
&lt;span class="nv"&gt;DEPLOY_SCRIPT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JENKINS_MODULE_DIR&lt;/span&gt;&lt;span class="s2"&gt;/03-deploy-controller.sh"&lt;/span&gt;

&lt;span class="c"&gt;# Path to the Python helper (Local to this script)&lt;/span&gt;
&lt;span class="nv"&gt;PY_HELPER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"./update_jcasc_mattermost.py"&lt;/span&gt;
&lt;span class="nv"&gt;MASTER_ENV&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CICD_ROOT&lt;/span&gt;&lt;span class="s2"&gt;/cicd.env"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[INFO] Starting Jenkins &amp;lt;-&amp;gt; Mattermost Integration..."&lt;/span&gt;

&lt;span class="c"&gt;# --- 1. Secret Injection ---&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[ERROR] Master environment file not found: &lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Load secrets&lt;/span&gt;
&lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JENKINS_MATTERMOST_WEBHOOK&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[ERROR] JENKINS_MATTERMOST_WEBHOOK not found in cicd.env."&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"       Please run 04-configure-integrations.py first."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JENKINS_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[ERROR] Jenkins env file not found at: &lt;/span&gt;&lt;span class="nv"&gt;$JENKINS_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[INFO] Injecting Mattermost Webhook into jenkins.env..."&lt;/span&gt;

&lt;span class="c"&gt;# Idempotency check using grep&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s2"&gt;"MATTERMOST_JENKINS_WEBHOOK_URL"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JENKINS_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt;&amp;gt; "&lt;/span&gt;&lt;span class="nv"&gt;$JENKINS_ENV_FILE&lt;/span&gt;&lt;span class="sh"&gt;"

# --- Mattermost Integration ---
MATTERMOST_JENKINS_WEBHOOK_URL=&lt;/span&gt;&lt;span class="nv"&gt;$JENKINS_MATTERMOST_WEBHOOK&lt;/span&gt;&lt;span class="sh"&gt;
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[INFO] Secrets injected."&lt;/span&gt;
&lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[INFO] Secrets already present."&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# --- 2. Update JCasC ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[INFO] Updating JCasC configuration..."&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PY_HELPER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[ERROR] Python helper script not found at &lt;/span&gt;&lt;span class="nv"&gt;$PY_HELPER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Install yaml if missing (on host)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"import yaml"&lt;/span&gt; 2&amp;gt;/dev/null&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update &lt;span class="nt"&gt;-qq&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nt"&gt;-qq&lt;/span&gt; python3-yaml
&lt;span class="k"&gt;fi

&lt;/span&gt;python3 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PY_HELPER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# --- 3. Re-Deploy Jenkins ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[INFO] Triggering Jenkins Re-deployment..."&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-x&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DEPLOY_SCRIPT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[ERROR] Deploy script not found: &lt;/span&gt;&lt;span class="nv"&gt;$DEPLOY_SCRIPT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JENKINS_MODULE_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; ./03-deploy-controller.sh&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[SUCCESS] Jenkins is restarting with Mattermost integration."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deconstructing the Connector
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. The Variable Hand-Off&lt;/strong&gt;&lt;br&gt;
This script performs a crucial bridging operation. It takes &lt;code&gt;JENKINS_MATTERMOST_WEBHOOK&lt;/code&gt; (which lives in the host's &lt;code&gt;cicd.env&lt;/code&gt;) and injects it into &lt;code&gt;jenkins.env&lt;/code&gt; as &lt;code&gt;MATTERMOST_JENKINS_WEBHOOK_URL&lt;/code&gt;. Why rename it? Because inside the Jenkins container, we want clear namespacing. This variable is then referenced in the JCasC file as &lt;code&gt;${MATTERMOST_JENKINS_WEBHOOK_URL}&lt;/code&gt;. This ensures the secret is never written to disk in the YAML file; it is only resolved in memory at runtime.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The Team@Channel Format&lt;/strong&gt;&lt;br&gt;
In the Python script, notice the room configuration: &lt;code&gt;'room': 'engineering@builds'&lt;/code&gt;. The Mattermost plugin requires this specific syntax to target a channel within a specific team. If we just put &lt;code&gt;builds&lt;/code&gt;, it might default to the wrong team or fail silently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The Redeployment Trigger&lt;/strong&gt;&lt;br&gt;
The script concludes by running &lt;code&gt;03-deploy-controller.sh&lt;/code&gt; from the Jenkins directory. This is not optional. JCasC reloading can sometimes be done on the fly, but injecting new environment variables (the webhook URL) requires a container restart. We force a clean deploy to ensure the new "nerve" is fully attached.&lt;/p&gt;
&lt;h1&gt;
  
  
  Chapter 7: The Wiring (Part 1) - The Silent Observer
&lt;/h1&gt;
&lt;h2&gt;
  
  
  7.4 The Pipeline Update (&lt;code&gt;Jenkinsfile&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;We have connected the cable (the webhook), but we haven't told the operator when to press the button.&lt;/p&gt;

&lt;p&gt;While the JCasC configuration we just applied sets up the &lt;em&gt;default&lt;/em&gt; connection details (the endpoint and the default room), we want granular control over &lt;em&gt;what&lt;/em&gt; gets sent and &lt;em&gt;where&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Specifically, we want to implement a routing logic that matches our "City" taxonomy:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;General Status:&lt;/strong&gt; Success/Failure notifications for the build itself should go to &lt;strong&gt;#builds&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Quality Alerts:&lt;/strong&gt; If the Inspector (SonarQube) blocks the pipeline, that specific alarm should ring in &lt;strong&gt;#alerts&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To achieve this, we must update our project's &lt;code&gt;Jenkinsfile&lt;/code&gt;. We will use the &lt;code&gt;mattermostSend&lt;/code&gt; step provided by the plugin. Note how we override the channel in the Quality Gate block to route that specific message to &lt;code&gt;engineering@alerts&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Update your &lt;code&gt;Jenkinsfile&lt;/code&gt; in the &lt;code&gt;0004_std_lib_http_client&lt;/code&gt; project (or your test repo) with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;pipeline&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="s1"&gt;'general-purpose-agent'&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;stages&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Setup &amp;amp; Build'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'--- Building Project ---'&lt;/span&gt;
                &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'chmod +x ./setup.sh'&lt;/span&gt;
                &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'./setup.sh'&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Test &amp;amp; Coverage'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'--- Running Tests ---'&lt;/span&gt;
                &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'chmod +x ./run-coverage-cicd.sh'&lt;/span&gt;
                &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'./run-coverage-cicd.sh'&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Code Analysis'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;script&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="n"&gt;sonarProjectKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sh&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;returnStdout:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;script:&lt;/span&gt; &lt;span class="s1"&gt;'grep "^sonar.projectKey=" sonar-project.properties | cut -d= -f2'&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;trim&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;

                    &lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="n"&gt;sonarHostUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"http://sonarqube.cicd.local:9000"&lt;/span&gt;

                    &lt;span class="n"&gt;withSonarQubeEnv&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'SonarQube'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                        &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'sonar-scanner'&lt;/span&gt;
                    &lt;span class="o"&gt;}&lt;/span&gt;

                    &lt;span class="c1"&gt;// 3. Wait for Quality Gate&lt;/span&gt;
                    &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;time:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;unit:&lt;/span&gt; &lt;span class="s1"&gt;'MINUTES'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                        &lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="n"&gt;qg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;waitForQualityGate&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;qg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;status&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s1"&gt;'OK'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                            &lt;span class="c1"&gt;// ROUTING: Quality Gate failures go to #alerts&lt;/span&gt;
                            &lt;span class="n"&gt;mattermostSend&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;
                                &lt;span class="nl"&gt;color:&lt;/span&gt; &lt;span class="s1"&gt;'danger'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                                &lt;span class="nl"&gt;channel:&lt;/span&gt; &lt;span class="s1"&gt;'engineering@alerts'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                                &lt;span class="nl"&gt;message:&lt;/span&gt; &lt;span class="s2"&gt;":no_entry: **Quality Gate Failed**: ${qg.status}\n&amp;lt;${sonarHostUrl}/dashboard?id=${sonarProjectKey}|View Analysis&amp;gt;"&lt;/span&gt;
                            &lt;span class="o"&gt;)&lt;/span&gt;
                            &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="s2"&gt;"Pipeline aborted due to quality gate failure: ${qg.status}"&lt;/span&gt;
                        &lt;span class="o"&gt;}&lt;/span&gt;
                    &lt;span class="o"&gt;}&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Package'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'--- Packaging Artifacts ---'&lt;/span&gt;
                &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'mkdir -p dist'&lt;/span&gt;

                &lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'build_release'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'cpack -G TGZ -C Release'&lt;/span&gt;
                    &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'mv *.tar.gz ../dist/'&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;

                &lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'src/rust'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'cargo package'&lt;/span&gt;
                    &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'cp target/package/*.crate ../../dist/'&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;

                &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'cp build_release/wheelhouse/*.whl dist/'&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Publish'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'--- Publishing to Artifactory ---'&lt;/span&gt;

                &lt;span class="n"&gt;rtUpload&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;
                    &lt;span class="nl"&gt;serverId:&lt;/span&gt; &lt;span class="s1"&gt;'artifactory'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                    &lt;span class="nl"&gt;spec:&lt;/span&gt; &lt;span class="s2"&gt;"""{
                          "files": [
                            {
                              "pattern": "dist/*",
                              "target": "generic-local/http-client/${BUILD_NUMBER}/",
                              "flat": "true"
                            }
                          ]
                    }"""&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                    &lt;span class="nl"&gt;failNoOp:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                    &lt;span class="nl"&gt;buildName:&lt;/span&gt; &lt;span class="s2"&gt;"${JOB_NAME}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                    &lt;span class="nl"&gt;buildNumber:&lt;/span&gt; &lt;span class="s2"&gt;"${BUILD_NUMBER}"&lt;/span&gt;
                &lt;span class="o"&gt;)&lt;/span&gt;

                &lt;span class="n"&gt;rtPublishBuildInfo&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;
                    &lt;span class="nl"&gt;serverId:&lt;/span&gt; &lt;span class="s1"&gt;'artifactory'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                    &lt;span class="nl"&gt;buildName:&lt;/span&gt; &lt;span class="s2"&gt;"${JOB_NAME}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                    &lt;span class="nl"&gt;buildNumber:&lt;/span&gt; &lt;span class="s2"&gt;"${BUILD_NUMBER}"&lt;/span&gt;
                &lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Global Post Actions: Standard notifications go to default channel (#builds)&lt;/span&gt;
    &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;failure&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;mattermostSend&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;
                &lt;span class="nl"&gt;color:&lt;/span&gt; &lt;span class="s1"&gt;'danger'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="nl"&gt;message:&lt;/span&gt; &lt;span class="s2"&gt;":x: **Build Failed**\n**Job:** ${env.JOB_NAME} #${env.BUILD_NUMBER}\n(&amp;lt;${env.BUILD_URL}|Open Build&amp;gt;)"&lt;/span&gt;
            &lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;success&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;mattermostSend&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;
                &lt;span class="nl"&gt;color:&lt;/span&gt; &lt;span class="s1"&gt;'good'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="nl"&gt;message:&lt;/span&gt; &lt;span class="s2"&gt;":white_check_mark: **Build Succeeded**\n**Job:** ${env.JOB_NAME} #${env.BUILD_NUMBER}\n(&amp;lt;${env.BUILD_URL}|Open Build&amp;gt;)"&lt;/span&gt;
            &lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deconstructing the Pipeline
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. The "Global Post" Block (The Heartbeat)&lt;/strong&gt;&lt;br&gt;
At the bottom of the file, the &lt;code&gt;post&lt;/code&gt; block handles the routine heartbeat of the factory. Whether the build succeeds or fails, &lt;code&gt;mattermostSend&lt;/code&gt; fires. Because we do not specify a &lt;code&gt;channel&lt;/code&gt; parameter here, it defaults to the configuration we injected via JCasC (&lt;code&gt;engineering@builds&lt;/code&gt;). This creates a steady stream of "Green/Red" status updates in our main channel.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The Conditional Alert (The Alarm Bell)&lt;/strong&gt;&lt;br&gt;
Inside the &lt;code&gt;Code Analysis&lt;/code&gt; stage, we have a specific &lt;code&gt;if (qg.status != 'OK')&lt;/code&gt; block. Here, we invoke &lt;code&gt;mattermostSend&lt;/code&gt; with &lt;code&gt;channel: 'engineering@alerts'&lt;/code&gt;. This is a critical pattern. We do not want to flood the general &lt;code&gt;#builds&lt;/code&gt; channel with nitty-gritty quality gate details. By routing this specific failure event to &lt;code&gt;#alerts&lt;/code&gt;, we ensure that the "Red Phone" only rings when something actually requires inspection.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Contextual Linking&lt;/strong&gt;&lt;br&gt;
Notice how we construct the message string: &lt;code&gt;&amp;lt;${sonarHostUrl}/dashboard...|View Analysis&amp;gt;&lt;/code&gt;. Mattermost supports Markdown-style links. We are not just saying "It failed"; we are providing a one-click path for the engineer to jump directly to the SonarQube dashboard to see &lt;em&gt;why&lt;/em&gt; it failed. This reduces the "Time to Diagnosis."&lt;/p&gt;
&lt;h2&gt;
  
  
  7.5 Verification: First Contact
&lt;/h2&gt;

&lt;p&gt;We have completed the electrical wiring. The "Electrician" (&lt;code&gt;04-configure-integrations.py&lt;/code&gt;) built the channel and the bot. The "Connector" (&lt;code&gt;05-connect-jenkins.sh&lt;/code&gt;) handed the webhook to Jenkins. The "Pipeline" (&lt;code&gt;Jenkinsfile&lt;/code&gt;) knows exactly where to route the signals.&lt;/p&gt;

&lt;p&gt;Now, we close the circuit.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Commit and Push:&lt;/strong&gt; Commit the updated &lt;code&gt;Jenkinsfile&lt;/code&gt; to your GitLab repository using our standard conventional commit style.&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;git add Jenkinsfile
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;":package: build(Jenkinsfile): add mattermost notifications"&lt;/span&gt;
git push
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Open your Mattermost Tab:&lt;/strong&gt; Navigate to the &lt;strong&gt;Engineering&lt;/strong&gt; team and open the &lt;strong&gt;#builds&lt;/strong&gt; channel. It should be empty, waiting for a signal.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Trigger the Signal:&lt;/strong&gt; If your GitLab webhook (from Article 8) is active, the push will trigger a build automatically. If not, open your Jenkins dashboard (&lt;code&gt;&lt;a href="https://jenkins.cicd.local:10400" rel="noopener noreferrer"&gt;https://jenkins.cicd.local:10400&lt;/a&gt;&lt;/code&gt;) and click &lt;strong&gt;"Build Now"&lt;/strong&gt; on the project.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Observe:&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;

&lt;/ol&gt;

&lt;p&gt;Wait for the pipeline to finish. Our configuration is designed to be low-noise: it does not spam the channel when the build &lt;em&gt;starts&lt;/em&gt;, only when it &lt;em&gt;concludes&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Once the build finishes, you will see the &lt;strong&gt;Jenkins&lt;/strong&gt; bot appear in the &lt;code&gt;#builds&lt;/code&gt; channel with a definitive verdict: either a green &lt;strong&gt;"Build Succeeded"&lt;/strong&gt; or a red &lt;strong&gt;"Build Failed"&lt;/strong&gt; message, complete with a hyperlink to the build logs.&lt;/p&gt;

&lt;p&gt;If you see this, the nervous system is live. The "Silent City" is no longer silent. Every time a build verdict is reached, the event is broadcast to the team.&lt;/p&gt;

&lt;p&gt;However, try to reply to the bot. Type: &lt;code&gt;@jenkins help&lt;/code&gt; in the channel.&lt;/p&gt;

&lt;p&gt;Nothing happens.&lt;/p&gt;

&lt;p&gt;This is a &lt;strong&gt;Unidirectional (One-Way)&lt;/strong&gt; connection. Jenkins can talk to us, but we cannot talk to Jenkins. In a true "Command Center," we demand control. We want to trigger builds, check logs, and restart servers directly from the chat window without context-switching to the Jenkins UI.&lt;/p&gt;

&lt;p&gt;To achieve this, we need to upgrade from simple Webhooks to a full &lt;strong&gt;Interactive Plugin&lt;/strong&gt;. This brings us to Chapter 8.&lt;/p&gt;

&lt;h1&gt;
  
  
  Chapter 8: The Wiring (Part 2) - The Interactive Agent
&lt;/h1&gt;

&lt;h2&gt;
  
  
  8.1 Beyond Notification: The Need for Command
&lt;/h2&gt;

&lt;p&gt;In the previous chapter, we successfully wired the nerves of our city. When Jenkins finishes a job, our Mattermost channel lights up. This is valuable—it reduces the "polling loop" where engineers obsessively refresh a browser tab.&lt;/p&gt;

&lt;p&gt;But it is passive. It is &lt;strong&gt;Read-Only&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A true "Command Center" must be &lt;strong&gt;Read-Write&lt;/strong&gt;. We don't just want to know that a build failed; we want to restart it. We don't just want to see a deployment notification; we want to &lt;em&gt;trigger&lt;/em&gt; the deployment. We want to treat the chat window as a shared CLI (Command Line Interface) for our infrastructure.&lt;/p&gt;

&lt;p&gt;This is the domain of &lt;strong&gt;Slash Commands&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We want to type &lt;code&gt;/jenkins build articles/0004_std_lib_http_client&lt;/code&gt; and have the Factory immediately spin up the turbines. We want to type &lt;code&gt;/jenkins get-log&lt;/code&gt; to debug a failure without leaving the chat. We even want the power to reboot the factory floor remotely with &lt;code&gt;/jenkins safe-restart&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To achieve this, the standard "Incoming Webhook" we used in Chapter 7 is insufficient. That was just a simple POST endpoint. For interactive control, we need the dedicated &lt;strong&gt;Mattermost Jenkins Plugin&lt;/strong&gt;. This plugin acts as a bridge, translating Mattermost slash commands into Jenkins API calls, and translating Jenkins API responses back into interactive chat messages.&lt;/p&gt;

&lt;p&gt;However, enabling this plugin in a "Code-First" environment involves overcoming a specific configuration hurdle that trips up many automated deployments: the &lt;strong&gt;Plugin ID Dragon&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  8.2 The "Missing Limb" Dragon
&lt;/h2&gt;

&lt;p&gt;In &lt;strong&gt;Chapter 3&lt;/strong&gt;, when we generated our &lt;code&gt;mattermost.env&lt;/code&gt; file, we included a specific line to enable the Jenkins plugin:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;MM_PLUGINSETTINGS_PLUGINSTATES={"jenkins":{"Enable":true} ... }&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This directive tells Mattermost to &lt;em&gt;load&lt;/em&gt; the plugin. However, unlike the "Boards" or "Playbooks" features which are baked into the Enterprise image, the Jenkins plugin is an external add-on. It does not exist on the disk. We tried to flip a switch for a lightbulb that isn't screwed in.&lt;/p&gt;

&lt;p&gt;To make this work, we have two distinct tasks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Installation:&lt;/strong&gt; We must download the plugin bundle (&lt;code&gt;.tar.gz&lt;/code&gt;) from the release repository and physically upload it to the Mattermost server.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Configuration:&lt;/strong&gt; Once installed, the plugin is a blank slate. It doesn't know where Jenkins is, and it doesn't have the encryption keys required to talk to it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here lies the architectural dragon: &lt;strong&gt;Plugin Configuration vs. Environment Variables.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For core Mattermost settings (like database URL), we can simply set &lt;code&gt;MM_SQLSETTINGS_DATASOURCE&lt;/code&gt;. But for &lt;em&gt;Plugins&lt;/em&gt;, there is no such mechanism. You cannot set &lt;code&gt;MM_PLUGINSETTINGS_JENKINS_BASEURL&lt;/code&gt;. Plugin settings live in a complex, unstructured JSON blob inside the server's state.&lt;/p&gt;

&lt;p&gt;If we were using "Click-Ops," we would manually upload the file in the System Console and then type the secrets into the UI. But we are building a reproducible city. We need a "Surgeon"—a script that can perform this transplant operation programmatically.&lt;/p&gt;

&lt;p&gt;This surgeon must:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Download&lt;/strong&gt; the latest plugin release.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Install&lt;/strong&gt; it via &lt;code&gt;mmctl&lt;/code&gt; (the CLI tool).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Inject&lt;/strong&gt; the configuration payload that binds the plugin to &lt;code&gt;http://jenkins.cicd.local:10400&lt;/code&gt; using the keys we generated in Chapter 3.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  8.3 The Surgeon (&lt;code&gt;09-install-jenkins-plugin.py&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;We will now write the script that performs this delicate operation. This is not a simple "fire and forget" command; it is a multi-step workflow.&lt;/p&gt;

&lt;p&gt;The script acts as a specialized package manager. It:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Unlocks&lt;/strong&gt; the server's write-protection (&lt;code&gt;EnableUploads&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Downloads&lt;/strong&gt; the plugin bundle (&lt;code&gt;.tar.gz&lt;/code&gt;) from the release repository.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Installs&lt;/strong&gt; and &lt;strong&gt;Enables&lt;/strong&gt; the binary using &lt;code&gt;mmctl&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Injects&lt;/strong&gt; the specific configuration keys (URL and Encryption Key) using granular &lt;code&gt;config set&lt;/code&gt; commands.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Re-locks&lt;/strong&gt; the server.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Create this file at &lt;code&gt;~/Documents/FromFirstPrinciples/articles/0011_cicd_part07_mattermost/09-install-jenkins-plugin.py&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env python3
&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;urllib.request&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;

&lt;span class="c1"&gt;# --- Configuration ---
# Paths
&lt;/span&gt;&lt;span class="n"&gt;CICD_ROOT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;HOME&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cicd_stack&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;ENV_FILE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CICD_ROOT&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cicd.env&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# Docker / Plugin Info
&lt;/span&gt;&lt;span class="n"&gt;CONTAINER_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mattermost&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;PLUGIN_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://github.com/mattermost-community/mattermost-plugin-jenkins/releases/download/v1.1.0/jenkins-1.1.0.tar.gz&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;PLUGIN_FILE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;jenkins-1.1.0.tar.gz&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;PLUGIN_ID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;jenkins&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="c1"&gt;# Community version ID
&lt;/span&gt;
&lt;span class="c1"&gt;# Commands
&lt;/span&gt;&lt;span class="n"&gt;MMCTL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;docker&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;exec&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-i&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CONTAINER_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mmctl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--local&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;load_secret_key&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Reads the Jenkins Encryption Key from cicd.env.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;ENV_FILE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;❌ Error: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ENV_FILE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; not found.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ENV_FILE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MATTERMOST_JENKINS_PLUGIN_KEY=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'"&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Runs a shell command and prints status.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   ⚙️  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flush&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;capture_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;✅&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CalledProcessError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;❌&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;      Error: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;set_config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Sets a config value via mmctl.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="c1"&gt;# Note: mmctl requires string values
&lt;/span&gt;    &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MMCTL&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;config&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;set&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="nf"&gt;run_command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Setting &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--- 🤖 Automating Jenkins Plugin (mmctl edition) ---&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 0. Pre-flight Check
&lt;/span&gt;    &lt;span class="n"&gt;jenkins_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_secret_key&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;jenkins_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;❌ Error: MATTERMOST_JENKINS_PLUGIN_KEY not found in cicd.env&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 1. Unlock Uploads
&lt;/span&gt;    &lt;span class="nf"&gt;set_config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PluginSettings.EnableUploads&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 2. Download
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   ⬇️  Downloading Plugin...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flush&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PLUGIN_FILE&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;urlretrieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PLUGIN_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PLUGIN_FILE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;✅&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;❌&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;      Download failed: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;✅ (Cached)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 3. Transfer
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   📦 Copying to container...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flush&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;docker&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PLUGIN_FILE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;CONTAINER_NAME&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:/tmp/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;PLUGIN_FILE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;✅&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 4. Install (Robust)
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   ⚙️  Installing Plugin bundle...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flush&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MMCTL&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;plugin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;add&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/tmp/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;PLUGIN_FILE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;capture_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;✅&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CalledProcessError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;already installed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;⚠️  (Already Installed)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;❌&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;      Error: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 5. Enable
&lt;/span&gt;    &lt;span class="c1"&gt;# This initializes the default config structure in the DB
&lt;/span&gt;    &lt;span class="nf"&gt;run_command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MMCTL&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;plugin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;enable&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PLUGIN_ID&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Enabling &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;PLUGIN_ID&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 6. Configure via mmctl
&lt;/span&gt;    &lt;span class="c1"&gt;# We use the exact keys confirmed from your config dump: 'jenkinsurl' and 'encryptionkey'
&lt;/span&gt;    &lt;span class="c1"&gt;# The Base Path for mmctl is PluginSettings.Plugins.jenkins
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   🔌 Configuring Plugin settings...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 6a. Set URL
&lt;/span&gt;    &lt;span class="nf"&gt;set_config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PluginSettings.Plugins.&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;PLUGIN_ID&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.jenkinsurl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://jenkins.cicd.local:10400&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 6b. Set Encryption Key
&lt;/span&gt;    &lt;span class="nf"&gt;set_config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PluginSettings.Plugins.&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;PLUGIN_ID&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.encryptionkey&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;jenkins_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 7. Re-Lock Uploads
&lt;/span&gt;    &lt;span class="nf"&gt;set_config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PluginSettings.EnableUploads&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;false&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 8. Cleanup
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PLUGIN_FILE&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PLUGIN_FILE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[SUCCESS] Jenkins Plugin Installed &amp;amp; Configured.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deconstructing the Surgeon
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. The &lt;code&gt;EnableUploads&lt;/code&gt; Toggle (Security)&lt;/strong&gt;&lt;br&gt;
By default, Mattermost prevents plugin uploads to protect the server from unauthorized code execution.&lt;br&gt;
&lt;code&gt;set_config("PluginSettings.EnableUploads", "true")&lt;/code&gt;&lt;br&gt;
The script temporarily lifts this gate, installs the software, and then immediately slams the gate shut again. This reduces the window of vulnerability to mere seconds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The Granular Config (&lt;code&gt;PluginSettings.Plugins...&lt;/code&gt;)&lt;/strong&gt;&lt;br&gt;
Unlike environment variables which are broad, &lt;code&gt;mmctl config set&lt;/code&gt; allows us to target deeply nested JSON keys using dot notation.&lt;br&gt;
&lt;code&gt;PluginSettings.Plugins.jenkins.encryptionkey&lt;/code&gt;&lt;br&gt;
This writes directly to the plugin's private storage area in the &lt;code&gt;config.json&lt;/code&gt;. It is cleaner and safer than downloading and patching the entire server configuration blob.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The Encryption Key Injection&lt;/strong&gt;&lt;br&gt;
We pull the 32-byte AES key (&lt;code&gt;MATTERMOST_JENKINS_PLUGIN_KEY&lt;/code&gt;) from our environment and inject it. This ensures that when the plugin encrypts your personal Jenkins API token in the next step, it uses a key that persists across server restarts. Without this, your handshake would break every time the container redeployed.&lt;/p&gt;
&lt;h2&gt;
  
  
  8.4 The Handshake: Establishing Command
&lt;/h2&gt;

&lt;p&gt;The Surgeon has successfully transplanted the plugin. Now, we must wake the patient.&lt;/p&gt;

&lt;p&gt;We have established the server-to-server link, but we have not yet established the &lt;strong&gt;User-to-User&lt;/strong&gt; link. When you type a command, Jenkins needs to know &lt;em&gt;who&lt;/em&gt; you are. It cannot simply trust your Mattermost username; it needs a valid Jenkins API token belonging to your user.&lt;/p&gt;

&lt;p&gt;This requires a manual credential exchange.&lt;/p&gt;
&lt;h3&gt;
  
  
  Protocol 1: The Connection (Manual)
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Generate the Token (Jenkins Side):&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;* Navigate to your Jenkins Dashboard (`https://jenkins.cicd.local:10400`).
* Click on your **Username** (top right corner) -\&amp;gt; **Configure**.
* Scroll to the **API Token** section and click **Add new Token**.
* Name it `Mattermost-Bot` and click **Generate**.
* **Copy the token immediately.** (You will never see it again).
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Perform the Handshake (Mattermost Side):&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;* Go to the **\#builds** channel in Mattermost.
* Type the connect command with your username and the token:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  ```bash
  /jenkins connect &amp;lt;your_username&amp;gt; &amp;lt;your_api_token&amp;gt;
  ```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  *(Example: `/jenkins connect warren.jitsing 11d38...9a`)*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Confirmation:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;* The bot will reply privately: *"Validating Jenkins credentials..."*
* Followed by: *"Your Jenkins account has been successfully connected to Mattermost."*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Protocol 2: The Command
&lt;/h3&gt;

&lt;p&gt;Now, let's test the control.&lt;/p&gt;

&lt;p&gt;The error &lt;code&gt;Don't have key "Location"&lt;/code&gt; is a common trap. It occurs if you try to build a job name that doesn't exist. Jenkins returns a generic page instead of a Queue ID, confusing the plugin.&lt;/p&gt;

&lt;p&gt;You must use the exact path. Since we are using Multibranch Pipelines inside a Folder (from Article 8), the path includes the folder, the repo, and the branch.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Trigger:&lt;/strong&gt;&lt;br&gt;
Type the following command:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;/jenkins build articles/0004_std_lib_http_client/main
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Response:&lt;/strong&gt;&lt;br&gt;
You will see immediate feedback confirming the command was received and processed:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Jenkins BOT&lt;/strong&gt;&lt;br&gt;
Initiated by Jenkins user: admin&lt;br&gt;
Job 'articles/0004_std_lib_http_client/main' has been triggered and is in queue.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Moments later, as the executor picks up the job:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Jenkins BOT&lt;/strong&gt;&lt;br&gt;
Initiated by Jenkins user: admin&lt;br&gt;
Job 'articles/0004_std_lib_http_client/main' - #14 has been started&lt;br&gt;
Build URL : &lt;a href="https://www.google.com/search?q=https://jenkins.cicd.local:10400/" rel="noopener noreferrer"&gt;https://jenkins.cicd.local:10400/&lt;/a&gt;...&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Notice the difference? When the &lt;em&gt;pipeline&lt;/em&gt; runs automatically (via git push), it is silent until the end. But when &lt;em&gt;you&lt;/em&gt; trigger it manually via chat, the bot confirms receipt immediately.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Protocol 3: The Investigation
&lt;/h3&gt;

&lt;p&gt;If you need to peek at the logs while the job is running (or after a failure) without leaving the chat:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Get Log:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;/jenkins get-log articles/0004_std_lib_http_client/main
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Response:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The bot will fetch the last few lines of the console output and post them as a code snippet directly in the channel.&lt;/p&gt;&lt;/li&gt;

&lt;/ol&gt;

&lt;p&gt;We have achieved the &lt;strong&gt;Interactive Loop&lt;/strong&gt;. We can Observe (Notifications), Orient (Get Log), Decide (Analyze), and Act (Rebuild)—all without touching a browser tab.&lt;/p&gt;

&lt;h1&gt;
  
  
  Chapter 9: The Wiring (Part 3) - The Library &amp;amp; The Inspector
&lt;/h1&gt;

&lt;h2&gt;
  
  
  9.1 The Library: Notifications (&lt;code&gt;06-connect-gitlab.py&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;We have connected the Factory (Jenkins) to our "Town Square." Now we turn our attention to the Library (GitLab).&lt;/p&gt;

&lt;p&gt;Our first goal is to ensure that activity in the library—commits, merge requests, and pipeline statuses—is broadcast to the &lt;code&gt;#code-reviews&lt;/code&gt; channel we established in Chapter 7.&lt;/p&gt;

&lt;p&gt;While GitLab has a native "Slack notifications" integration, it also supports Mattermost natively. We will use a Python script to programmatically configure this integration for our specific project (&lt;code&gt;0004_std_lib_http_client&lt;/code&gt;). This script acts as &lt;strong&gt;The Diplomat&lt;/strong&gt;: it takes the webhook URL generated by the Mattermost "Electrician" (&lt;code&gt;04&lt;/code&gt;) and registers it with the GitLab project.&lt;/p&gt;

&lt;p&gt;Create this file at &lt;code&gt;~/Documents/FromFirstPrinciples/articles/0011_cicd_part07_mattermost/06-connect-gitlab.py&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env python3
&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ssl&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;urllib.request&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;urllib.error&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;

&lt;span class="c1"&gt;# --- Configuration ---
&lt;/span&gt;&lt;span class="n"&gt;ENV_FILE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;HOME&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cicd_stack&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cicd.env&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;GITLAB_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://gitlab.cicd.local:10300&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;TARGET_GROUP&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Articles&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;TARGET_PROJECT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0004_std_lib_http_client&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;load_env&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;ENV_FILE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;❌ Error: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ENV_FILE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; not found.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ENV_FILE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'"&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_ssl_context&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="c1"&gt;# Uses the host's system trust store (where our CA is installed)
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_default_context&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;make_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Content-Type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PRIVATE-TOKEN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;get_ssl_context&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HTTPError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   ⛔ HTTP Error &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;      &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;pass&lt;/span&gt;
        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;load_env&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GITLAB_API_TOKEN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;webhook_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MATTERMOST_CODE_REVIEW_WEBHOOK&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--- Connecting GitLab to Mattermost ---&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;❌ Error: GITLAB_API_TOKEN not found in cicd.env.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;webhook_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;❌ Error: MATTERMOST_CODE_REVIEW_WEBHOOK not found in cicd.env.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   Please run 04-configure-integrations.py first.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 1. Find Project ID
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   🔎 Finding project &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;TARGET_GROUP&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;TARGET_PROJECT&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;projects&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;make_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;GITLAB_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/api/v4/projects?search=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;TARGET_PROJECT&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;project_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="n"&gt;target_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;TARGET_GROUP&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;TARGET_PROJECT&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;projects&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;path_with_namespace&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;target_path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;project_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;project_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   ❌ Project not found.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   ✅ Found Project ID: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;project_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 2. Configure Integration (Idempotent PUT)
&lt;/span&gt;    &lt;span class="c1"&gt;# We use PUT to enforce the state defined in our environment.
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   ⚙️  Enforcing Integration Configuration...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;config_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;webhook&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;webhook_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;username&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GitLab&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;notify_only_broken_pipelines&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;push_events&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;merge_requests_events&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pipeline_events&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tag_push_events&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;branches_to_be_notified&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;all&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;make_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;GITLAB_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/api/v4/projects/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;project_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/integrations/mattermost&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PUT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;config_data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   ✅ Integration synced. Notifications active in #code-reviews.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[SUCCESS] GitLab integration complete.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deconstructing the Diplomat
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. The Target Discovery&lt;/strong&gt;&lt;br&gt;
The script does not assume the project ID. It searches for &lt;code&gt;Articles/0004_std_lib_http_client&lt;/code&gt;. This makes the script portable; if you recreate the repo, the ID changes, but the script still finds the correct target.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The Integration Payload&lt;/strong&gt;&lt;br&gt;
We use the &lt;code&gt;integrations/mattermost&lt;/code&gt; endpoint. Notice the configuration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;push_events&lt;/code&gt;: True. Every commit triggers a notification.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;merge_requests_events&lt;/code&gt;: True. Opening or merging an MR alerts the channel.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pipeline_events&lt;/code&gt;: True. GitLab CI status changes are reported (distinct from Jenkins).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;webhook&lt;/code&gt;: This comes directly from &lt;code&gt;MATTERMOST_CODE_REVIEW_WEBHOOK&lt;/code&gt;, ensuring the messages land in &lt;code&gt;#code-reviews&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Idempotency (PUT)&lt;/strong&gt;&lt;br&gt;
We use the HTTP &lt;code&gt;PUT&lt;/code&gt; method. If the integration doesn't exist, GitLab creates it. If it does exist, GitLab updates it to match our JSON payload exactly. This prevents "configuration drift."&lt;/p&gt;


&lt;h2&gt;
  
  
  9.2 The Library: Identity (Manual OAuth Setup)
&lt;/h2&gt;

&lt;p&gt;Webhooks give us &lt;strong&gt;Notifications&lt;/strong&gt;. But to achieve &lt;strong&gt;Interaction&lt;/strong&gt; (viewing your personal To-Do list, subscribing to specific repos, and seeing MR previews in the sidebar), we need &lt;strong&gt;OAuth2&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The Mattermost GitLab Plugin needs permission to act on your behalf. This requires creating a "User-Scoped Application" in GitLab. Because this process generates sensitive secrets that are displayed only once, we perform this step manually in the GitLab UI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Navigate to Applications&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Log in to GitLab (&lt;code&gt;https://gitlab.cicd.local:10300&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt; Click your &lt;strong&gt;Avatar&lt;/strong&gt; (top right) -&amp;gt; &lt;strong&gt;Preferences&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; In the left sidebar, select &lt;strong&gt;Applications&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; Click &lt;strong&gt;Add new application&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Define the Treaty&lt;/strong&gt;&lt;br&gt;
Fill in the form with the following details. Be precise with the Redirect URIs.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;Mattermost Chat Ops&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Redirect URI:&lt;/strong&gt;&lt;br&gt;
You must provide &lt;em&gt;two&lt;/em&gt; URIs: one for the internal DNS name (for desktop/browser users) and one for the IP address (for mobile users/Entry Mode).&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  https://mattermost.cicd.local:8065/plugins/com.github.manland.mattermost-plugin-gitlab/oauth/complete
  https://&amp;lt;YOUR_LAN_IP&amp;gt;:8065/plugins/com.github.manland.mattermost-plugin-gitlab/oauth/complete
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;em&gt;(Replace &lt;code&gt;&amp;lt;YOUR_LAN_IP&amp;gt;&lt;/code&gt; with your host machine's IP, e.g., &lt;code&gt;192.168.0.105&lt;/code&gt;)&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Confidential:&lt;/strong&gt; &lt;code&gt;Yes&lt;/code&gt; (Checked)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Scopes:&lt;/strong&gt; Select the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;api&lt;/code&gt; (Access the API on your behalf)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;read_user&lt;/code&gt; (Read your personal information)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Secure the Secrets&lt;/strong&gt;&lt;br&gt;
Click &lt;strong&gt;Save application&lt;/strong&gt;.&lt;br&gt;
GitLab will present you with an &lt;strong&gt;Application ID&lt;/strong&gt; and a &lt;strong&gt;Secret&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ CRITICAL:&lt;/strong&gt; Keep this page open or copy these values immediately. You cannot see the Secret again.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We will use these two values in the next section to configure the Mattermost server.&lt;/p&gt;
&lt;h2&gt;
  
  
  9.3 The Messenger (&lt;code&gt;07-connect-sonarqube.py&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;We have connected the Factory and the Library. Now we turn to &lt;strong&gt;The Inspector&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;SonarQube analyzes our code quality. In &lt;strong&gt;Article 10&lt;/strong&gt;, we established a "Quality Gate"—a pass/fail threshold for our software. If the Inspector fails a build because of low test coverage or security vulnerabilities, we want that alarm to ring immediately in the dedicated &lt;strong&gt;#alerts&lt;/strong&gt; channel.&lt;/p&gt;

&lt;p&gt;We have already created the destination (the Webhook URL for &lt;code&gt;#alerts&lt;/code&gt;) in script &lt;code&gt;04&lt;/code&gt;. Now we must tell SonarQube to use it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Architectural Note: The Formatting Mismatch&lt;/strong&gt;&lt;br&gt;
It is important to note a limitation here. SonarQube sends webhooks with a complex, nested JSON payload detailing the quality gate status. Mattermost's Incoming Webhooks, however, expect a specific, flat JSON format (e.g., &lt;code&gt;{"text": "..."}&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;If we connect SonarQube directly to Mattermost without a middleware translator, Mattermost will receive the signal but may fail to render it meaningfully (or reject it entirely as malformed). While there are third-party plugins that solve this, in our "First Principles" architecture, we have chosen a more robust pattern: &lt;strong&gt;The Jenkins Relay&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;As we configured in the &lt;code&gt;Jenkinsfile&lt;/code&gt; (Chapter 7), we allow Jenkins—which understands both the build context and the SonarQube result—to format and send the rich, color-coded alert to Mattermost.&lt;/p&gt;

&lt;p&gt;However, we still provide the following script, &lt;strong&gt;The Messenger&lt;/strong&gt;, to establish the direct link. This is useful if you intend to deploy a middleware adapter later or if you want to inspect the raw SonarQube payloads for debugging purposes.&lt;/p&gt;

&lt;p&gt;Create this file at &lt;code&gt;~/Documents/FromFirstPrinciples/articles/0011_cicd_part07_mattermost/07-connect-sonarqube.py&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env python3
&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;urllib.request&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;urllib.parse&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;urllib.error&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;

&lt;span class="c1"&gt;# --- Configuration ---
&lt;/span&gt;&lt;span class="n"&gt;ENV_FILE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;HOME&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cicd_stack&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cicd.env&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="c1"&gt;# SonarQube runs on HTTP internally (Article 10 architecture)
&lt;/span&gt;&lt;span class="n"&gt;SONAR_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://sonarqube.cicd.local:9000&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;WEBHOOK_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Mattermost&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;load_env&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;ENV_FILE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;❌ Error: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ENV_FILE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; not found.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ENV_FILE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'"&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;make_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# SonarQube uses Basic Auth with Token as username, empty password
&lt;/span&gt;    &lt;span class="n"&gt;auth_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;b64_auth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auth_str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Basic &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;b64_auth&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="c1"&gt;# SonarQube requires form-encoded data for these endpoints
&lt;/span&gt;        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Content-Type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;application/x-www-form-urlencoded&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;encoded_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;urlencode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;encoded_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   [DEBUG] Request: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   [DEBUG] Payload: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;encoded_data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   [DEBUG] Response &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;204&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# No Content
&lt;/span&gt;                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HTTPError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   ⛔ HTTP Error &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;err_body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;      [DEBUG] Error Body: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;err_body&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;pass&lt;/span&gt;
        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   ❌ Unexpected Error: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;load_env&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SONAR_ADMIN_TOKEN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# Generated in Art 10
&lt;/span&gt;    &lt;span class="n"&gt;webhook_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SONAR_MATTERMOST_WEBHOOK&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# Generated in Step 04
&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--- Connecting SonarQube to Mattermost ---&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;❌ Error: SONAR_ADMIN_TOKEN not found in cicd.env. (Run Art 10 setup?)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;webhook_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;❌ Error: SONAR_MATTERMOST_WEBHOOK not found in cicd.env. (Run Step 04?)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 1. Check existing webhooks to ensure Idempotency
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   🔎 Checking existing webhooks...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# The 'list' endpoint returns a JSON object with a 'webhooks' array
&lt;/span&gt;    &lt;span class="n"&gt;webhooks_resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;make_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;SONAR_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/api/webhooks/list&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;exists&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;hook&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;webhooks_resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;webhooks&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;hook&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;WEBHOOK_NAME&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;exists&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   ℹ️  Webhook &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;WEBHOOK_NAME&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; already exists.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;

    &lt;span class="c1"&gt;# 2. Create Webhook if missing
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   Creating webhook &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;WEBHOOK_NAME&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;WEBHOOK_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;webhook_url&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nf"&gt;make_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;SONAR_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/api/webhooks/create&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   ✅ Webhook created.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   ✅ Connection verified. Quality Gate alerts will go to #alerts.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[SUCCESS] SonarQube integration complete.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  9.4 The Integrator (&lt;code&gt;08-configure-plugins.py&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;We are now in the final phase of wiring the Library (GitLab).&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Section 9.2&lt;/strong&gt;, we manually created the OAuth Application in GitLab and obtained the &lt;strong&gt;Application ID&lt;/strong&gt; and &lt;strong&gt;Secret&lt;/strong&gt;. However, Mattermost doesn't know these secrets yet.&lt;/p&gt;

&lt;p&gt;Before running this script, you &lt;strong&gt;must&lt;/strong&gt; update your &lt;code&gt;cicd.env&lt;/code&gt; file with the values you copied from the GitLab UI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pre-Requisite: Update Environment&lt;/strong&gt;&lt;br&gt;
Open &lt;code&gt;~/cicd_stack/cicd.env&lt;/code&gt; and append the following lines (replace the placeholders with your actual secrets):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;GITLAB_OAUTH_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;paste_application_id_here&amp;gt;"&lt;/span&gt;
&lt;span class="nv"&gt;GITLAB_OAUTH_SECRET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;paste_secret_here&amp;gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we run &lt;strong&gt;The Integrator&lt;/strong&gt;. This script uses &lt;code&gt;mmctl&lt;/code&gt; to inject these secrets into the Mattermost GitLab Plugin configuration, effectively "logging in" the server to GitLab.&lt;/p&gt;

&lt;p&gt;Create this file at &lt;code&gt;~/Documents/FromFirstPrinciples/articles/0011_cicd_part07_mattermost/08-configure-plugins.py&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env python3
&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;

&lt;span class="c1"&gt;# --- Configuration ---
&lt;/span&gt;&lt;span class="n"&gt;CICD_ROOT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;HOME&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cicd_stack&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;ENV_FILE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CICD_ROOT&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cicd.env&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;CONTAINER_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mattermost&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="c1"&gt;# Base command for mmctl via socket
&lt;/span&gt;&lt;span class="n"&gt;MMCTL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;docker&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;exec&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-i&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CONTAINER_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mmctl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--local&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;load_env&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Reads cicd.env into a dictionary.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;ENV_FILE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;❌ Error: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ENV_FILE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; not found.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;env_vars&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ENV_FILE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;env_vars&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'"&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;env_vars&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;set_config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Sets a config value using mmctl and prints output.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   ⚠️  Skipping &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; (Value is missing/None)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;

    &lt;span class="c1"&gt;# mmctl config set &amp;lt;path&amp;gt; &amp;lt;value&amp;gt;
&lt;/span&gt;    &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MMCTL&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;config&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;set&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Capture stdout and stderr to print them
&lt;/span&gt;        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;capture_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   ✅ Set &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Print actual output from the tool if it exists
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;      &amp;gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;      &amp;gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CalledProcessError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   ❌ Failed to set &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;      Exit Code: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;returncode&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;      [STDOUT]: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;      [STDERR]: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--- Configuring Mattermost Plugins via CLI ---&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;secrets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_env&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;# --- 1. GitLab Plugin Configuration ---
&lt;/span&gt;    &lt;span class="c1"&gt;# Plugin ID: com.github.manland.mattermost-plugin-gitlab
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   🔌 Configuring GitLab Plugin...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# We use the exact keys seen in your config.json
&lt;/span&gt;    &lt;span class="n"&gt;base_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PluginSettings.Plugins.com.github.manland.mattermost-plugin-gitlab&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="c1"&gt;# Required Secrets
&lt;/span&gt;    &lt;span class="n"&gt;gitlab_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://gitlab.cicd.local:10300&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;oauth_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;secrets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GITLAB_OAUTH_ID&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;oauth_secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;secrets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GITLAB_OAUTH_SECRET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;webhook_secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;secrets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MATTERMOST_GITLAB_PLUGIN_SECRET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;enc_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;secrets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MATTERMOST_GITLAB_PLUGIN_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;oauth_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;oauth_secret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;webhook_secret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;enc_key&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   ⚠️  Missing GitLab OAuth/Secret keys in cicd.env. Please check 01-setup script output.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;set_config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;base_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.gitlaburl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gitlab_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;set_config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;base_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.gitlaboauthclientid&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;oauth_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;set_config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;base_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.gitlaboauthclientsecret&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;oauth_secret&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;set_config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;base_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.webhooksecret&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;webhook_secret&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# Note: JSON key is 'encryptionkey', NOT 'atrestencryptionkey' based on the config structure
&lt;/span&gt;        &lt;span class="nf"&gt;set_config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;base_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.encryptionkey&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;enc_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Feature Flags
&lt;/span&gt;        &lt;span class="nf"&gt;set_config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;base_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.enableprivaterepo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;set_config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;base_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.enablecodepreview&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;public_private&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# --- 2. Reload Config ---
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   🔄 Reloading Configuration...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;reload_res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MMCTL&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;config&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reload&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;capture_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   ✅ Config Reloaded.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;reload_res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;      &amp;gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;reload_res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CalledProcessError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   ❌ Failed to reload config: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[SUCCESS] Plugin configuration complete.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deconstructing the Integrator
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Granular Configuration Targeting&lt;/strong&gt;&lt;br&gt;
The Mattermost GitLab plugin is not a "first-party" plugin in the same way &lt;code&gt;focalboard&lt;/code&gt; is. It lives under the long namespace &lt;code&gt;com.github.manland.mattermost-plugin-gitlab&lt;/code&gt;. The script builds the config path dynamically: &lt;code&gt;PluginSettings.Plugins.com.github.manland...&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The Secrets Injection&lt;/strong&gt;&lt;br&gt;
We inject four critical secrets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;gitlaboauthclientid&lt;/code&gt; &amp;amp; &lt;code&gt;secret&lt;/code&gt;: For authenticating users via the Sidebar.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;webhooksecret&lt;/code&gt;: For verifying that incoming webhooks are actually from GitLab (preventing spoofing).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;encryptionkey&lt;/code&gt;: For encrypting the user access tokens stored in the database.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. The Config Reload&lt;/strong&gt;&lt;br&gt;
Unlike the Jenkins plugin installation (which required enabling/disabling), configuration changes via &lt;code&gt;config set&lt;/code&gt; are often cached. We force a &lt;code&gt;config reload&lt;/code&gt; command at the end to ensure the server picks up the new OAuth settings immediately without requiring a full container restart.&lt;/p&gt;
&lt;h2&gt;
  
  
  9.5 Verification: The Sidebar and the Alarm
&lt;/h2&gt;

&lt;p&gt;We have wired the Library (GitLab) and the Inspector (SonarQube). Now we must confirm that the signals are flowing correctly.&lt;/p&gt;
&lt;h3&gt;
  
  
  Part 1: The Library (GitLab Sidebar)
&lt;/h3&gt;

&lt;p&gt;We have configured the server-side OAuth secrets. Now, just like with Jenkins, every user must perform a one-time handshake to link their Mattermost identity to their GitLab identity.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The Handshake:&lt;/strong&gt;&lt;br&gt;
Go to any channel (e.g., &lt;strong&gt;#town-square&lt;/strong&gt;) and type:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;/gitlab connect
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Authorization:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The bot will reply with a private link. Click it.&lt;/p&gt;&lt;/li&gt;

&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;* You will be redirected to `gitlab.cicd.local`.
* If you are already logged in to GitLab, the authorization happens instantly.
* You will be redirected back to Mattermost with a success message: *"You have successfully connected your GitLab account."*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The Sidebar:&lt;/strong&gt;&lt;br&gt;
Look at the &lt;strong&gt;Right Sidebar&lt;/strong&gt; of your Mattermost interface. It is always visible.&lt;br&gt;
You should see a GitLab section that reflects your status:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;GitLab&lt;/strong&gt;&lt;br&gt;
Signed in as: &lt;strong&gt;root&lt;/strong&gt; (or your user)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you see "There are no GitLab subscriptions available in this channel," that is normal for a generic channel like &lt;code&gt;#town-square&lt;/code&gt;. It simply means we haven't linked this specific chat channel to a specific git repository for commit broadcasts (which is what we used the Webhook for in Section 9.1). The critical part is that it recognizes &lt;em&gt;you&lt;/em&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Part 2: The Alarm (Quality Gate Failure)
&lt;/h3&gt;

&lt;p&gt;Now we test the "Red Phone." We want to verify that if the Inspector (SonarQube) detects a violation, the alarm rings specifically in &lt;strong&gt;#alerts&lt;/strong&gt;, not just the general &lt;strong&gt;#builds&lt;/strong&gt; channel.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Verify the Rigging:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;* Log in to **SonarQube** (`http://sonarqube.cicd.local:9000`).
* Navigate to **Quality Gates**.
* Ensure the **"Fail Hard"** gate (created in Article 10) is active.
* Confirm the **Coverage** condition is set to **100%**.
* *Note: Our `0004_std_lib_http_client` currently has \~94% coverage, so this guarantees a failure.*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Trigger the Build:&lt;/strong&gt;&lt;br&gt;
Go to &lt;strong&gt;#builds&lt;/strong&gt; in Mattermost and use your command power:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/jenkins build articles/0004_std_lib_http_client/main
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Observation:&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;* **\#builds channel:** You will see the "Build Queued" confirmation.

* *Wait approx. 2 minutes for the analysis...*

* **\#alerts channel:** Suddenly, a notification appears here.

  &amp;gt; ⛔ **Quality Gate Failed**: ERROR
  &amp;gt; `http://sonarqube.cicd.local:9000/dashboard?id=...`

* **\#builds channel:** Shortly after, the final verdict appears:

  &amp;gt; ❌ **Build Failed**
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This confirms our routing logic is active. The general population in &lt;code&gt;#builds&lt;/code&gt; sees that the build died, but the specific &lt;em&gt;reason&lt;/em&gt; (Quality Gate Failure) is routed to &lt;code&gt;#alerts&lt;/code&gt;, where the QA team or senior engineers would be subscribed.&lt;/p&gt;

&lt;h1&gt;
  
  
  Chapter 10: Conclusion - The Command Center
&lt;/h1&gt;

&lt;h2&gt;
  
  
  10.1 The Echo Test (Verifying the Radio Tower)
&lt;/h2&gt;

&lt;p&gt;We have verified that messages flow from our servers to our devices. Now, we must verify that high-bandwidth media can flow between our devices, traversing the treacherous "Double NAT" landscape we navigated in Chapter 4.&lt;/p&gt;

&lt;p&gt;We built a &lt;strong&gt;Coturn Radio Tower&lt;/strong&gt; to bridge the gap between the Docker internal network and the physical LAN. It is time to test if the tower is broadcasting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Test Protocol:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;The Host:&lt;/strong&gt; On your desktop browser, navigate to the &lt;strong&gt;#town-square&lt;/strong&gt; channel. Click the &lt;strong&gt;Call&lt;/strong&gt; icon (phone handset) in the header to start a call. Grant camera and microphone permissions.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The Client:&lt;/strong&gt; On your Android device (connected to WiFi), open the &lt;strong&gt;Mattermost App&lt;/strong&gt;. Navigate to &lt;strong&gt;#town-square&lt;/strong&gt;. You should see a banner: &lt;em&gt;"Call in progress. Tap to join."&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The Connection:&lt;/strong&gt; Tap &lt;strong&gt;Join&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The Moment of Truth:&lt;/strong&gt;&lt;br&gt;
If the screen remains black or says "Reconnecting...", the UDP packets are being dropped by a firewall or misconfigured NAT.&lt;br&gt;
If you see video from both devices and hear audio (likely with a screeching feedback loop because you are in the same room—&lt;strong&gt;mute your mics quickly!&lt;/strong&gt;), then the Radio Tower is operational.&lt;/p&gt;

&lt;p&gt;This success confirms that our "Host Mode" deployment of Coturn (&lt;code&gt;02-deploy-coturn.sh&lt;/code&gt;) is successfully relaying UDP packets from your phone, through the host's physical interface, into the Mattermost container. We have achieved peer-to-peer style communication in a containerized environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  10.2 The Architecture of ChatOps
&lt;/h2&gt;

&lt;p&gt;With the video link established, take a step back and look at what we have built.&lt;/p&gt;

&lt;p&gt;Before this article, managing our CI/CD pipeline was a game of "Tab Fatigue." You wrote code in an IDE. You pushed it. You switched to a browser tab to check GitLab. You switched to another tab to watch Jenkins. You clicked through to SonarQube to check for code smells. You were constantly &lt;strong&gt;pulling&lt;/strong&gt; information from the system.&lt;/p&gt;

&lt;p&gt;We have inverted this model. We have moved to a &lt;strong&gt;Push-based&lt;/strong&gt; architecture.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Factory (Jenkins)&lt;/strong&gt; pushes status updates to us.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Inspector (SonarQube)&lt;/strong&gt; pushes alarms to us.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Library (GitLab)&lt;/strong&gt; pushes merge requests to us.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We no longer poll the infrastructure; the infrastructure reports to us. The chat window has become the single pane of glass for the entire software lifecycle. We have achieved &lt;strong&gt;ChatOps&lt;/strong&gt;: the practice of connecting people, tools, and processes into a transparent workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  10.3 Sovereign Infrastructure
&lt;/h2&gt;

&lt;p&gt;Perhaps the greater achievement is &lt;em&gt;how&lt;/em&gt; we built it.&lt;/p&gt;

&lt;p&gt;We did not spin up a SaaS instance of Slack or Discord. We did not rely on cloud-hosted relays. We built a fully sovereign communications platform on our own hardware, running on a standard Linux kernel.&lt;/p&gt;

&lt;p&gt;More importantly, we rejected the easy path of "Click-Ops." We did not manually configure ten different integration settings in the web UI. We wrote software to configure our software.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;01-03&lt;/code&gt;&lt;/strong&gt;: Deployed the core infrastructure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;04, 06, 07&lt;/code&gt;&lt;/strong&gt;: Wired the webhooks and integrations programmatically.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;05, 08, 09&lt;/code&gt;&lt;/strong&gt;: Injected configuration and plugins into running containers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If we were to wipe our &lt;code&gt;mattermost&lt;/code&gt; container today, we could restore the entire city—every channel, every bot, every permission scheme—simply by re-running our scripts. This is the discipline of &lt;strong&gt;Infrastructure as Code&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We also conquered the "Mobile Frontier." By manually establishing our own Certificate Authority and installing it on Android, we proved that you do not need Let's Encrypt or a public domain name to have secure, encrypted mobile connectivity.&lt;/p&gt;

&lt;h2&gt;
  
  
  10.4 The Road Ahead: The Noise Problem
&lt;/h2&gt;

&lt;p&gt;Our "Digital City" is now bustling. We have GitLab managing code, Jenkins building binaries, SonarQube inspecting quality, and Mattermost coordinating communications.&lt;/p&gt;

&lt;p&gt;But a bustling city generates noise.&lt;/p&gt;

&lt;p&gt;Currently, if a build fails mysteriously, you have to SSH into the host and run &lt;code&gt;docker logs jenkins&lt;/code&gt;. If Nginx throws a 502 error, you are grepping through &lt;code&gt;/var/log/nginx&lt;/code&gt;. As we add more services, our logs are becoming fragmented, scattered across different containers and file systems. We have built a powerful engine, but we lack a centralized dashboard to monitor its internal health.&lt;/p&gt;

&lt;p&gt;In the next and final article of this series, we will tackle the &lt;strong&gt;Observability&lt;/strong&gt; layer. We will deploy the &lt;strong&gt;ELK Stack&lt;/strong&gt; (Elasticsearch, Logstash, Kibana) to ingest, parse, and visualize the massive stream of data our city is generating, turning raw noise into actionable intelligence.&lt;/p&gt;

&lt;p&gt;The command center is open. Now, let's turn on the radar.&lt;/p&gt;

</description>
      <category>cicd</category>
      <category>devops</category>
      <category>mattermost</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Julia High Performance Crash Course</title>
      <dc:creator>Warren Jitsing</dc:creator>
      <pubDate>Sun, 21 Dec 2025 04:57:03 +0000</pubDate>
      <link>https://dev.to/warren_jitsing_dd1c1d6fc6/julia-high-performance-crash-course-4i7p</link>
      <guid>https://dev.to/warren_jitsing_dd1c1d6fc6/julia-high-performance-crash-course-4i7p</guid>
      <description>&lt;p&gt;I just wanted to post a resource I wrote while learning Julia. Note, this was done in a week and likely contains errors. But it should still be useful on the whole&lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/InfiniteConsult/julia_practice" rel="noopener noreferrer"&gt;https://github.com/InfiniteConsult/julia_practice&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Module 1: Getting Started: Basics
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Repl
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;0001_hello_world.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0001_hello_world.jl&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello, World!"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces the most fundamental function for displaying output in Julia: &lt;code&gt;println()&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;println()&lt;/code&gt;&lt;/strong&gt;: This function takes one or more arguments, prints their string representation to the console, and automatically appends a newline character at the end.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strings&lt;/strong&gt;: Text literals, like &lt;code&gt;"Hello, World!"&lt;/code&gt;, are created using double quotes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Comments&lt;/strong&gt;: Lines beginning with &lt;code&gt;#&lt;/code&gt; are single-line comments and are ignored by the interpreter.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To run this script, save it as &lt;code&gt;0001_hello_world.jl&lt;/code&gt; and execute it from your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0001_hello_world.jl
Hello, World!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;0002_repl_modes.md&lt;/code&gt;
&lt;/h3&gt;

&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;Julia's REPL (Read-Eval-Print Loop) is more than just a command line; it's an interactive environment with several distinct modes, each with its own prompt and purpose. You switch between them using single keystrokes.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. Julian Mode (&lt;code&gt;julia&amp;gt;&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;This is the default mode for writing and executing Julia code.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prompt&lt;/strong&gt;: &lt;code&gt;julia&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Purpose&lt;/strong&gt;: To evaluate Julia expressions. You can define variables, call functions, and test code snippets here.&lt;/li&gt;
&lt;/ul&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="n"&gt;julia&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt;

&lt;span class="n"&gt;julia&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;my_variable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Hello from the REPL"&lt;/span&gt;
&lt;span class="s"&gt;"Hello from the REPL"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  2. Help Mode (&lt;code&gt;help?&amp;gt;&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;This mode is for accessing Julia's built-in documentation.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prompt&lt;/strong&gt;: &lt;code&gt;help?&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How to Enter&lt;/strong&gt;: Type &lt;code&gt;?&lt;/code&gt; in Julian mode.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How to Exit&lt;/strong&gt;: Press &lt;code&gt;Backspace&lt;/code&gt; or &lt;code&gt;Ctrl+C&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="n"&gt;julia&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt;
&lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="o"&gt;?&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;println&lt;/span&gt;

  &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;([&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;IO&lt;/span&gt;&lt;span class="x"&gt;],&lt;/span&gt; &lt;span class="n"&gt;xs&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

  &lt;span class="n"&gt;Print&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;or&lt;/span&gt; &lt;span class="n"&gt;representation&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt; &lt;span class="n"&gt;xs&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;followed&lt;/span&gt; &lt;span class="n"&gt;by&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;newline&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;If&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="n"&gt;not&lt;/span&gt; &lt;span class="n"&gt;supplied&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prints&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="nb"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="o"&gt;?&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  3. Pkg Mode (&lt;code&gt;pkg&amp;gt;&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;This mode provides an interface to Julia's built-in package manager, &lt;code&gt;Pkg&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prompt&lt;/strong&gt;: &lt;code&gt;pkg&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How to Enter&lt;/strong&gt;: Type &lt;code&gt;]&lt;/code&gt; in Julian mode.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How to Exit&lt;/strong&gt;: Press &lt;code&gt;Backspace&lt;/code&gt; or &lt;code&gt;Ctrl+C&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You use this mode to add, remove, and update dependencies for your project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="n"&gt;julia&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="x"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;pkg&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;
  &lt;span class="n"&gt;Project&lt;/span&gt; &lt;span class="n"&gt;MultiLanguageHttpClient&lt;/span&gt; &lt;span class="n"&gt;v0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;
  &lt;span class="n"&gt;Status&lt;/span&gt; &lt;span class="sb"&gt;`~/MultiLanguageHttpClient/Project.toml`&lt;/span&gt; &lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;empty&lt;/span&gt; &lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;pkg&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;add&lt;/span&gt; &lt;span class="n"&gt;Sockets&lt;/span&gt;
  &lt;span class="n"&gt;Updating&lt;/span&gt; &lt;span class="n"&gt;registry&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="sb"&gt;`~/.julia/registries/General`&lt;/span&gt;
  &lt;span class="n"&gt;Resolving&lt;/span&gt; &lt;span class="n"&gt;package&lt;/span&gt; &lt;span class="n"&gt;versions&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;
  &lt;span class="n"&gt;Updating&lt;/span&gt; &lt;span class="sb"&gt;`~/Multi-Language-HTTP-Client/Project.toml`&lt;/span&gt;
  &lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="n"&gt;eb21f48&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;Sockets&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  4. Shell Mode (&lt;code&gt;shell&amp;gt;&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;This mode allows you to run shell commands directly from within Julia.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prompt&lt;/strong&gt;: &lt;code&gt;shell&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How to Enter&lt;/strong&gt;: Type &lt;code&gt;;&lt;/code&gt; in Julian mode.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How to Exit&lt;/strong&gt;: Press &lt;code&gt;Backspace&lt;/code&gt; or &lt;code&gt;Ctrl+C&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is useful for file system operations or running other command-line tools without leaving the Julia REPL.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="n"&gt;julia&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="x"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;shell&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ls&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;
&lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;rw&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="mi"&gt;44&lt;/span&gt; &lt;span class="n"&gt;Oct&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt; &lt;span class="mi"&gt;0001&lt;/span&gt;&lt;span class="n"&gt;_hello_world&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;jl&lt;/span&gt;
&lt;span class="n"&gt;drwxr&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;xr&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;  &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="n"&gt;Oct&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt; &lt;span class="n"&gt;Project&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toml&lt;/span&gt;

&lt;span class="n"&gt;shell&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Variables Assignments
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;0003_variables.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0003_variables.jl&lt;/span&gt;

&lt;span class="c"&gt;# 1. Assign an integer value to a variable named 'x'&lt;/span&gt;
&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The value of x is: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The type of x is: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Print a separator line&lt;/span&gt;

&lt;span class="c"&gt;# 2. Reassign a new value of a different type (a String) to the same variable&lt;/span&gt;
&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Hello, Julia!"&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The value of x is now: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The type of x is now: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script demonstrates fundamental variable assignment and the dynamic nature of Julia's type system.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Assignment&lt;/strong&gt;: The &lt;code&gt;=&lt;/code&gt; operator is used to assign or &lt;em&gt;bind&lt;/em&gt; a value to a variable name.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Dynamic Types&lt;/strong&gt;: Unlike C++ or Rust, you do not need to declare a variable's type before using it. Julia is dynamically typed, which means a variable is simply a name bound to a value, and the type is associated with the value itself, not the variable name. As shown in the example, the variable &lt;code&gt;x&lt;/code&gt; can first hold an integer (&lt;code&gt;Int64&lt;/code&gt; by default on a 64-bit system) and then be reassigned to hold a &lt;code&gt;String&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;typeof()&lt;/code&gt;&lt;/strong&gt;: This built-in function returns the type of the value that its argument currently refers to. It's a useful tool for interactive exploration and debugging.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0003_variables.jl
The value of x is: 100
The &lt;span class="nb"&gt;type &lt;/span&gt;of x is: Int64
&lt;span class="nt"&gt;--------------------&lt;/span&gt;
The value of x is now: Hello, Julia!
The &lt;span class="nb"&gt;type &lt;/span&gt;of x is now: String
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;0004_constants.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0004_constants.jl&lt;/span&gt;

&lt;span class="c"&gt;# A regular (non-constant) global variable. Its type can change.&lt;/span&gt;
&lt;span class="n"&gt;NON_CONST_GLOBAL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;

&lt;span class="c"&gt;# A constant global variable. Its type is now fixed.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;CONST_GLOBAL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; get_non_const&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;NON_CONST_GLOBAL&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; get_const&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;CONST_GLOBAL&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"This script demonstrates the performance difference between constant and non-constant globals."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The real difference is seen by inspecting the compiled code, not just by timing this simple script."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;In the Julia REPL, run the following commands to see the difference:"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  include(&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;0004_constants.jl&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;)"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  @code_warntype get_non_const()"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  @code_warntype get_const()"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# We can call the functions to show they work&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Result from non-constant global: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;get_non_const&lt;/span&gt;&lt;span class="x"&gt;())&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Result from constant global: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;get_const&lt;/span&gt;&lt;span class="x"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces one of the most important concepts for writing high-performance Julia code: &lt;strong&gt;constant global variables&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;const&lt;/code&gt; Keyword&lt;/strong&gt;: When used on a global variable, &lt;code&gt;const&lt;/code&gt; is a promise to the Julia compiler that the &lt;em&gt;type&lt;/em&gt; of this variable will never change. This allows the compiler to generate highly optimized, specialized machine code for any function that uses it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Performance Impact ❗
&lt;/h3&gt;

&lt;p&gt;Accessing &lt;strong&gt;non-constant global variables&lt;/strong&gt; is extremely slow and is one of the most common performance pitfalls for beginners.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Why it's slow&lt;/strong&gt;: Because the type of &lt;code&gt;NON_CONST_GLOBAL&lt;/code&gt; could change at any moment, the compiler can't make any assumptions. Every time &lt;code&gt;get_non_const()&lt;/code&gt; is called, it must generate slow code to dynamically look up the variable, check its current type, and then decide how to perform the &lt;code&gt;* 2&lt;/code&gt; operation.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;How &lt;code&gt;const&lt;/code&gt; fixes it&lt;/strong&gt;: By declaring &lt;code&gt;const CONST_GLOBAL&lt;/code&gt;, the compiler knows its type will always be an integer. It can then generate fast, direct code for &lt;code&gt;get_const()&lt;/code&gt; that performs an efficient integer multiplication, completely avoiding the runtime type-checking overhead.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Diagnosing with &lt;code&gt;@code_warntype&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;@code_warntype&lt;/code&gt; macro is your primary tool for diagnosing this kind of performance issue. After running &lt;code&gt;include("0004_constants.jl")&lt;/code&gt; in the REPL, compare the output of these two commands:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. The Slow Case (Non-Constant)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="n"&gt;julia&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;@code_warntype&lt;/span&gt; &lt;span class="n"&gt;get_non_const&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Any&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;Body::Any&lt;/code&gt; (often highlighted in red) is a warning sign. It means Julia couldn't figure out the function's return type because it depends on a global variable of an unknown type.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The Fast Case (Constant)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="n"&gt;julia&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;@code_warntype&lt;/span&gt; &lt;span class="n"&gt;get_const&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int64&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, Julia correctly infers the return type as &lt;code&gt;Int64&lt;/code&gt;. This indicates type-stable, performant code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rule of Thumb&lt;/strong&gt;: Always declare global variables as &lt;code&gt;const&lt;/code&gt; unless you have a specific reason to change their type.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;0005_unicode_names.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0005_unicode_names.jl&lt;/span&gt;

&lt;span class="c"&gt;# Standard variable names work as expected&lt;/span&gt;
&lt;span class="n"&gt;radius&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;

&lt;span class="c"&gt;# Julia allows many Unicode characters, like Greek letters, in variable names&lt;/span&gt;
&lt;span class="nb"&gt;π&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;3.14159&lt;/span&gt;
&lt;span class="n"&gt;δ&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.01&lt;/span&gt;

&lt;span class="c"&gt;# These variables can be used in calculations just like any other&lt;/span&gt;
&lt;span class="n"&gt;circumference&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nb"&gt;π&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;radius&lt;/span&gt;
&lt;span class="n"&gt;area&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;π&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Radius (r): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Pi (π): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;π&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Delta (δ): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;δ&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Calculated Circumference: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;circumference&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Calculated Area: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;area&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script demonstrates a unique and powerful feature of Julia: its first-class support for Unicode in variable names.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Unicode Identifiers&lt;/strong&gt;: You can use a vast array of Unicode characters, including most mathematical symbols and Greek letters, as valid variable names. This allows your code to more closely resemble the mathematical formulas it represents, which can significantly improve readability in scientific and technical domains.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;How to Type Them&lt;/strong&gt;: In the Julia REPL and many code editors (like VS Code with the Julia extension), you can type these symbols using their LaTeX names followed by the &lt;code&gt;Tab&lt;/code&gt; key.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To get &lt;code&gt;π&lt;/code&gt;, type &lt;code&gt;\pi&lt;/code&gt; and then press &lt;code&gt;Tab&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;To get &lt;code&gt;δ&lt;/code&gt;, type &lt;code&gt;\delta&lt;/code&gt; and then press &lt;code&gt;Tab&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;This feature is not just cosmetic; it's a fundamental part of the language that encourages writing clear, descriptive, and notationally familiar code.&lt;/p&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0005_unicode_names.jl
Radius &lt;span class="o"&gt;(&lt;/span&gt;r&lt;span class="o"&gt;)&lt;/span&gt;: 5
Pi &lt;span class="o"&gt;(&lt;/span&gt;π&lt;span class="o"&gt;)&lt;/span&gt;: 3.14159
Delta &lt;span class="o"&gt;(&lt;/span&gt;δ&lt;span class="o"&gt;)&lt;/span&gt;: 0.01
&lt;span class="nt"&gt;--------------------&lt;/span&gt;
Calculated Circumference: 31.4159
Calculated Area: 78.53975
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Primitive Types
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;0006_integers.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0006_integers.jl (Corrected)&lt;/span&gt;

&lt;span class="c"&gt;# By default, integer literals are of type Int64 on 64-bit systems&lt;/span&gt;
&lt;span class="n"&gt;default_int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Default integer type: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default_int&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;

&lt;span class="c"&gt;# You can specify the exact bit size&lt;/span&gt;
&lt;span class="n"&gt;i8&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int8&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;127&lt;/span&gt;
&lt;span class="n"&gt;i64&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int64&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;9_223_372_036_854_775_807&lt;/span&gt; &lt;span class="c"&gt;# Underscores can be used as separators&lt;/span&gt;
&lt;span class="n"&gt;u8&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;UInt8&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"An 8-bit signed integer: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i8&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"A 64-bit signed integer: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i64&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"An 8-bit unsigned integer: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;u8&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# To demonstrate overflow, all operands must be of the same type.&lt;/span&gt;
&lt;span class="c"&gt;# We explicitly construct an Int8 from the literal '2' before adding.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The maximum value for Int8 is: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typemax&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Int8&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;overflowed_int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i8&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="kt"&gt;Int8&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# This is now Int8(127) + Int8(2)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"127 + 2 as Int8 results in: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;overflowed_int&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The minimum value for Int8 is: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typemin&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Int8&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script covers Julia's primitive integer types and their overflow behavior.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sized Integers&lt;/strong&gt;: Julia provides a full range of standard integer types: &lt;code&gt;Int8&lt;/code&gt;, &lt;code&gt;Int16&lt;/code&gt;, &lt;code&gt;Int32&lt;/code&gt;, &lt;code&gt;Int64&lt;/code&gt;, &lt;code&gt;Int128&lt;/code&gt; and their unsigned (&lt;code&gt;UInt...&lt;/code&gt;) counterparts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Default Type&lt;/strong&gt;: The default type for an integer literal is &lt;code&gt;Int&lt;/code&gt;, which is an alias for the platform's native word size (&lt;code&gt;Int64&lt;/code&gt; on 64-bit systems).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type Construction&lt;/strong&gt;: You can construct a value of a specific type using &lt;code&gt;TypeName(value)&lt;/code&gt;, for example, &lt;code&gt;Int8(2)&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Performance &amp;amp; Behavior Notes
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Memory Usage&lt;/strong&gt;: For large arrays, using the smallest appropriate integer type (e.g., &lt;code&gt;Vector{Int8}&lt;/code&gt;) can significantly reduce memory usage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Overflow Behavior&lt;/strong&gt;: Julia's arithmetic operations &lt;strong&gt;wrap around&lt;/strong&gt; on overflow &lt;em&gt;when all operands are of the same fixed-size integer type&lt;/em&gt;. The expression &lt;code&gt;i8 + Int8(2)&lt;/code&gt; performs &lt;code&gt;Int8&lt;/code&gt; arithmetic, causing the value to wrap from the maximum (&lt;code&gt;127&lt;/code&gt;) to the minimum (&lt;code&gt;-128&lt;/code&gt;) and continue from there. This is a crucial distinction from operations involving mixed types, which promote to a larger type and do not wrap.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To run the corrected script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0006_integers.jl
Default integer &lt;span class="nb"&gt;type&lt;/span&gt;: Int64
An 8-bit signed integer: 127
A 64-bit signed integer: 9223372036854775807
An 8-bit unsigned integer: 255
&lt;span class="nt"&gt;--------------------&lt;/span&gt;
The maximum value &lt;span class="k"&gt;for &lt;/span&gt;Int8 is: 127
127 + 2 as Int8 results &lt;span class="k"&gt;in&lt;/span&gt;: &lt;span class="nt"&gt;-127&lt;/span&gt;
The minimum value &lt;span class="k"&gt;for &lt;/span&gt;Int8 is: &lt;span class="nt"&gt;-128&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;0007_floats.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0007_floats.jl&lt;/span&gt;

&lt;span class="c"&gt;# By default, literals with a decimal point are Float64&lt;/span&gt;
&lt;span class="n"&gt;f64&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Default float type: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f64&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;

&lt;span class="c"&gt;# You can create a Float32 by using an 'f0' suffix&lt;/span&gt;
&lt;span class="n"&gt;f32&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.5f0&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"A 32-bit float: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f32&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;

&lt;span class="c"&gt;# Scientific notation is also supported&lt;/span&gt;
&lt;span class="n"&gt;small_num&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1e-5&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Scientific notation (1e-5): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;small_num&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Floating-point arithmetic follows IEEE 754 standards, including special values&lt;/span&gt;
&lt;span class="n"&gt;positive_infinity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;
&lt;span class="n"&gt;negative_infinity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;
&lt;span class="n"&gt;not_a_number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"1.0 / 0.0 = "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;positive_infinity&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-1.0 / 0.0 = "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;negative_infinity&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0.0 / 0.0 = "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;not_a_number&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# You can check for these special values&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Is positive_infinity infinite? "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isinf&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;positive_infinity&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Is not_a_number a NaN? "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isnan&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;not_a_number&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces Julia's floating-point types and their special values, which will be familiar from C++ and Rust as they follow the IEEE 754 standard.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Floating-Point Types&lt;/strong&gt;: Julia's main floating-point types are &lt;code&gt;Float32&lt;/code&gt; (single precision) and &lt;code&gt;Float64&lt;/code&gt; (double precision). &lt;code&gt;Float64&lt;/code&gt; is the default for any literal containing a decimal point.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Literals&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A literal like &lt;code&gt;3.14&lt;/code&gt; is automatically a &lt;code&gt;Float64&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;To create a &lt;code&gt;Float32&lt;/code&gt; literal, you can use the &lt;code&gt;f0&lt;/code&gt; suffix (e.g., &lt;code&gt;3.14f0&lt;/code&gt;). This is a concise syntax similar to the &lt;code&gt;f&lt;/code&gt; suffix in C/C++.&lt;/li&gt;
&lt;li&gt;Scientific notation can be expressed with &lt;code&gt;e&lt;/code&gt; or &lt;code&gt;E&lt;/code&gt;, as in &lt;code&gt;6.022e23&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Special Values&lt;/strong&gt;: Standard floating-point arithmetic can result in three special values:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Inf&lt;/code&gt;: Infinity, resulting from operations like &lt;code&gt;1.0 / 0.0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-Inf&lt;/code&gt;: Negative infinity.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;NaN&lt;/code&gt;: "Not a Number," resulting from undefined operations like &lt;code&gt;0.0 / 0.0&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Check Functions&lt;/strong&gt;: Julia provides &lt;code&gt;isinf()&lt;/code&gt;, &lt;code&gt;isnan()&lt;/code&gt;, and &lt;code&gt;isfinite()&lt;/code&gt; to test for these special values.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Performance Note
&lt;/h3&gt;

&lt;p&gt;For general-purpose computing, the default &lt;code&gt;Float64&lt;/code&gt; is recommended. However, for applications involving very large arrays of floating-point numbers (like in graphics, machine learning, or scientific simulation), explicitly using &lt;code&gt;Float32&lt;/code&gt; can cut memory usage in half and may offer significant speedups on hardware optimized for single-precision arithmetic, such as GPUs.&lt;/p&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0007_floats.jl
Default float &lt;span class="nb"&gt;type&lt;/span&gt;: Float64
A 32-bit float: Float32
Scientific notation &lt;span class="o"&gt;(&lt;/span&gt;1e-5&lt;span class="o"&gt;)&lt;/span&gt;: 1.0e-5
&lt;span class="nt"&gt;--------------------&lt;/span&gt;
1.0 / 0.0 &lt;span class="o"&gt;=&lt;/span&gt; Inf
&lt;span class="nt"&gt;-1&lt;/span&gt;.0 / 0.0 &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nt"&gt;-Inf&lt;/span&gt;
0.0 / 0.0 &lt;span class="o"&gt;=&lt;/span&gt; NaN
Is positive_infinity infinite? &lt;span class="nb"&gt;true
&lt;/span&gt;Is not_a_number a NaN? &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;0008_booleans_chars.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0008_booleans_chars.jl&lt;/span&gt;

&lt;span class="c"&gt;# Booleans can be 'true' or 'false'&lt;/span&gt;
&lt;span class="n"&gt;is_active&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;span class="n"&gt;is_complete&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Value of is_active: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;is_active&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;", Type: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;is_active&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Value of is_complete: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;is_complete&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;", Type: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;is_complete&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Characters are created with single quotes and represent a single Unicode code point&lt;/span&gt;
&lt;span class="n"&gt;letter_a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'a'&lt;/span&gt;
&lt;span class="n"&gt;unicode_char&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'Ω'&lt;/span&gt; &lt;span class="c"&gt;# Greek letter Omega&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Value of letter_a: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;letter_a&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;", Type: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;letter_a&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Value of unicode_char: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unicode_char&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;", Type: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unicode_char&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;

&lt;span class="c"&gt;# A Julia Char is a 32-bit primitive type, which can be seen by converting it to an integer&lt;/span&gt;
&lt;span class="n"&gt;codepoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;UInt32&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unicode_char&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The Unicode codepoint for 'Ω' is: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;codepoint&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script covers two fundamental primitive types: booleans and characters.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;Bool&lt;/code&gt;&lt;/strong&gt;: The boolean type has two possible instances: &lt;code&gt;true&lt;/code&gt; and &lt;code&gt;false&lt;/code&gt;. It is used for logical operations and control flow.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;Char&lt;/code&gt;&lt;/strong&gt;: A character literal is created using &lt;strong&gt;single quotes&lt;/strong&gt; (e.g., &lt;code&gt;'a'&lt;/code&gt;). This distinguishes it from strings, which use double quotes.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Important Distinction for C/C++ Programmers
&lt;/h3&gt;

&lt;p&gt;A crucial difference from C/C++ is that a Julia &lt;code&gt;Char&lt;/code&gt; is &lt;strong&gt;not&lt;/strong&gt; an 8-bit integer. It is a special 32-bit primitive type that represents a single Unicode code point. This allows any Unicode character, from 'a' to 'Ω' to '😂', to be stored in a &lt;code&gt;Char&lt;/code&gt; variable without ambiguity. You can convert a &lt;code&gt;Char&lt;/code&gt; to its corresponding integer value to see its code point.&lt;/p&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0008_booleans_chars.jl
Value of is_active: &lt;span class="nb"&gt;true&lt;/span&gt;, Type: Bool
Value of is_complete: &lt;span class="nb"&gt;false&lt;/span&gt;, Type: Bool
&lt;span class="nt"&gt;--------------------&lt;/span&gt;
Value of letter_a: a, Type: Char
Value of unicode_char: Ω, Type: Char
The Unicode codepoint &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="s1"&gt;'Ω'&lt;/span&gt; is: 937
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Basic Operators
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;0009_arithmetic_operators.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0009_arithmetic_operators.jl&lt;/span&gt;

&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;

&lt;span class="c"&gt;# Standard arithmetic operators&lt;/span&gt;
&lt;span class="n"&gt;addition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;span class="n"&gt;subtraction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;span class="n"&gt;multiplication&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;span class="n"&gt;exponentiation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="c"&gt;# Note: ^ is for power, not XOR&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"a + b = "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;addition&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"a - b = "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;subtraction&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"a * b = "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;multiplication&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"a ^ b = "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exponentiation&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Julia has two types of division&lt;/span&gt;
&lt;span class="n"&gt;float_division&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;span class="n"&gt;integer_division&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;÷&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="c"&gt;# Type this with \div&amp;lt;tab&amp;gt;&lt;/span&gt;
&lt;span class="n"&gt;remainder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Floating-point division (a / b): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;float_division&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Integer division (a ÷ b): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;integer_division&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Remainder (a % b): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;remainder&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script covers Julia's standard arithmetic operators, highlighting the important distinction between the two division operators.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Standard Operators&lt;/strong&gt;: Julia uses the expected symbols for addition (&lt;code&gt;+&lt;/code&gt;), subtraction (&lt;code&gt;-&lt;/code&gt;), multiplication (&lt;code&gt;*&lt;/code&gt;), exponentiation (&lt;code&gt;^&lt;/code&gt;), and remainder (&lt;code&gt;%&lt;/code&gt;).

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Note&lt;/strong&gt;: Coming from C/C++/Rust, be aware that &lt;code&gt;^&lt;/code&gt; is for exponentiation, not bitwise XOR (which is done with the &lt;code&gt;xor()&lt;/code&gt; function or the &lt;code&gt;⊻&lt;/code&gt; symbol).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Division Operators
&lt;/h3&gt;

&lt;p&gt;Julia provides two distinct division operators to avoid ambiguity, which is a common source of bugs in other languages.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;/&lt;/code&gt; &lt;strong&gt;(Floating-Point Division)&lt;/strong&gt;: This operator &lt;em&gt;always&lt;/em&gt; performs floating-point division and will always return a floating-point number, even if the inputs are integers. This is identical to Python 3's &lt;code&gt;/&lt;/code&gt; operator.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;10 / 2&lt;/code&gt; results in &lt;code&gt;5.0&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;code&gt;÷&lt;/code&gt; &lt;strong&gt;(Integer Division)&lt;/strong&gt;: This operator (typed as &lt;code&gt;\div&lt;/code&gt; followed by &lt;code&gt;Tab&lt;/code&gt;) performs Euclidean division, truncating the result to an integer. This is the equivalent of integer division in C/C++ or the &lt;code&gt;//&lt;/code&gt; operator in Python.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;10 ÷ 3&lt;/code&gt; results in &lt;code&gt;3&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0009_arithmetic_operators.jl
a + b &lt;span class="o"&gt;=&lt;/span&gt; 13
a - b &lt;span class="o"&gt;=&lt;/span&gt; 7
a &lt;span class="k"&gt;*&lt;/span&gt; b &lt;span class="o"&gt;=&lt;/span&gt; 30
a ^ b &lt;span class="o"&gt;=&lt;/span&gt; 1000
&lt;span class="nt"&gt;--------------------&lt;/span&gt;
Floating-point division &lt;span class="o"&gt;(&lt;/span&gt;a / b&lt;span class="o"&gt;)&lt;/span&gt;: 3.3333333333333335
Integer division &lt;span class="o"&gt;(&lt;/span&gt;a ÷ b&lt;span class="o"&gt;)&lt;/span&gt;: 3
Remainder &lt;span class="o"&gt;(&lt;/span&gt;a % b&lt;span class="o"&gt;)&lt;/span&gt;: 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;0010_comparison_operators.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0010_comparison_operators.jl&lt;/span&gt;

&lt;span class="c"&gt;# Standard comparison operators&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"5 &amp;gt; 3 is "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"5 == 5 is "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"5 != 3 is "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 'a' is less than 'b' based on its Unicode value&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"'a' &amp;lt; 'b' is "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="sc"&gt;'a'&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="sc"&gt;'b'&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# The `==` operator compares values after type promotion&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Does 1 (Integer) == 1.0 (Float)? "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# The `===` operator checks for strict equality (same type and value)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Does 1 (Integer) === 1.0 (Float)? "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# `NaN` is a special case for equality&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Does NaN == NaN? "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NaN&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NaN&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# `isequal()` is a function that considers NaN equal to itself&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Does isequal(NaN, NaN)? "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isequal&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;NaN&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NaN&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script demonstrates Julia's comparison operators, highlighting the important differences between the three types of equality checks.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Standard Operators&lt;/strong&gt;: The usual operators &lt;code&gt;==&lt;/code&gt; (equal), &lt;code&gt;!=&lt;/code&gt; (not equal), &lt;code&gt;&amp;lt;&lt;/code&gt;, &lt;code&gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;=&lt;/code&gt;, and &lt;code&gt;&amp;gt;=&lt;/code&gt; work as expected. They compare values, promoting numeric types if necessary. This is why &lt;code&gt;1 == 1.0&lt;/code&gt; evaluates to &lt;code&gt;true&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Three Equalities
&lt;/h3&gt;

&lt;p&gt;For a systems programmer, understanding the distinction between different equality checks is critical.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;==&lt;/code&gt; (Value Equality)&lt;/strong&gt;: This is the most common equality check. It compares values. If the types are different but can be promoted to a common type (like &lt;code&gt;Int&lt;/code&gt; and &lt;code&gt;Float64&lt;/code&gt;), it does so before comparing. The one special case is that &lt;code&gt;NaN == NaN&lt;/code&gt; is always &lt;code&gt;false&lt;/code&gt;, following the IEEE 754 standard.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;isequal()&lt;/code&gt; (Consistent Value Equality)&lt;/strong&gt;: This function is similar to &lt;code&gt;==&lt;/code&gt; but provides more consistent behavior for use in hash tables (like &lt;code&gt;Dict&lt;/code&gt;). The key difference is that &lt;code&gt;isequal(NaN, NaN)&lt;/code&gt; returns &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;===&lt;/code&gt; (Strict Equality / Identity)&lt;/strong&gt;: This operator, pronounced "triple equals," checks if two operands are identical.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For immutable values like numbers or characters, it returns &lt;code&gt;true&lt;/code&gt; only if they are of the &lt;strong&gt;exact same type&lt;/strong&gt; and have the same value. This is why &lt;code&gt;1 === 1.0&lt;/code&gt; is &lt;code&gt;false&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;For mutable objects (which we will cover later), it checks if they are the exact same object in memory, similar to comparing pointers in C/C++.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0010_comparison_operators.jl
5 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 3 is &lt;span class="nb"&gt;true
&lt;/span&gt;5 &lt;span class="o"&gt;==&lt;/span&gt; 5 is &lt;span class="nb"&gt;true
&lt;/span&gt;5 &lt;span class="o"&gt;!=&lt;/span&gt; 3 is &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;span class="s1"&gt;'a'&lt;/span&gt; &amp;lt; &lt;span class="s1"&gt;'b'&lt;/span&gt; is &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;span class="nt"&gt;--------------------&lt;/span&gt;
Does 1 &lt;span class="o"&gt;(&lt;/span&gt;Integer&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; 1.0 &lt;span class="o"&gt;(&lt;/span&gt;Float&lt;span class="o"&gt;)&lt;/span&gt;? &lt;span class="nb"&gt;true
&lt;/span&gt;Does 1 &lt;span class="o"&gt;(&lt;/span&gt;Integer&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; 1.0 &lt;span class="o"&gt;(&lt;/span&gt;Float&lt;span class="o"&gt;)&lt;/span&gt;? &lt;span class="nb"&gt;false
&lt;/span&gt;Does NaN &lt;span class="o"&gt;==&lt;/span&gt; NaN? &lt;span class="nb"&gt;false
&lt;/span&gt;Does isequal&lt;span class="o"&gt;(&lt;/span&gt;NaN, NaN&lt;span class="o"&gt;)&lt;/span&gt;? &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;0011_boolean_operators.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0011_boolean_operators.jl&lt;/span&gt;

&lt;span class="c"&gt;# Define functions that print when they are called&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; is_true&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Function '"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"' was called and returns true."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; is_false&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Function '"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"' was called and returns false."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Demonstrating &amp;amp;&amp;amp; (AND) ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# The right side is NOT evaluated because the left side is false.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Result: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;is_false&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"LHS"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;is_true&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"RHS"&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Demonstrating || (OR) ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# The right side is NOT evaluated because the left side is true.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Result: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;is_true&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"LHS"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;is_false&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"RHS"&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Demonstrating ! (NOT) ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Result: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;is_false&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"NOT test"&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script demonstrates Julia's logical operators and their short-circuiting behavior, which is a critical feature for writing efficient and safe code.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Operators&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;&lt;/strong&gt;: Logical AND. Returns &lt;code&gt;true&lt;/code&gt; only if both the left and right sides are &lt;code&gt;true&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;||&lt;/code&gt;&lt;/strong&gt;: Logical OR. Returns &lt;code&gt;true&lt;/code&gt; if either the left or the right side is &lt;code&gt;true&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;!&lt;/code&gt;&lt;/strong&gt;: Logical NOT. Inverts a boolean value.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Short-Circuit Evaluation
&lt;/h3&gt;

&lt;p&gt;As in C, C++, Rust, and Python, Julia's &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; and &lt;code&gt;||&lt;/code&gt; operators perform &lt;strong&gt;short-circuit evaluation&lt;/strong&gt;. This is a key performance and control-flow feature.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;For &lt;code&gt;a &amp;amp;&amp;amp; b&lt;/code&gt;&lt;/strong&gt;: The expression &lt;code&gt;b&lt;/code&gt; is &lt;strong&gt;only&lt;/strong&gt; evaluated if &lt;code&gt;a&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt;. If &lt;code&gt;a&lt;/code&gt; is &lt;code&gt;false&lt;/code&gt;, the overall result must be &lt;code&gt;false&lt;/code&gt;, so there is no need to evaluate &lt;code&gt;b&lt;/code&gt;. In the first example, only the &lt;code&gt;is_false("LHS")&lt;/code&gt; function is called.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;For &lt;code&gt;a || b&lt;/code&gt;&lt;/strong&gt;: The expression &lt;code&gt;b&lt;/code&gt; is &lt;strong&gt;only&lt;/strong&gt; evaluated if &lt;code&gt;a&lt;/code&gt; is &lt;code&gt;false&lt;/code&gt;. If &lt;code&gt;a&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt;, the overall result must be &lt;code&gt;true&lt;/code&gt;, so there is no need to evaluate &lt;code&gt;b&lt;/code&gt;. In the second example, only the &lt;code&gt;is_true("LHS")&lt;/code&gt; function is called.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This behavior is commonly used to "guard" subsequent operations, for example, checking that an object is not &lt;code&gt;nothing&lt;/code&gt; before trying to access one of its fields.&lt;/p&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0011_boolean_operators.jl
&lt;span class="nt"&gt;---&lt;/span&gt; Demonstrating &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;AND&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
Function &lt;span class="s1"&gt;'LHS'&lt;/span&gt; was called and returns false.
Result: &lt;span class="nb"&gt;false&lt;/span&gt;

&lt;span class="nt"&gt;---&lt;/span&gt; Demonstrating &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;OR&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
Function &lt;span class="s1"&gt;'LHS'&lt;/span&gt; was called and returns true.
Result: &lt;span class="nb"&gt;true&lt;/span&gt;

&lt;span class="nt"&gt;---&lt;/span&gt; Demonstrating &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;NOT&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
Function &lt;span class="s1"&gt;'NOT test'&lt;/span&gt; was called and returns false.
Result: &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;0012_updating_operators.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0012_updating_operators.jl&lt;/span&gt;

&lt;span class="c"&gt;# Initialize a counter&lt;/span&gt;
&lt;span class="n"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Initial counter value: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Increment the counter by 5&lt;/span&gt;
&lt;span class="n"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"After 'counter += 5': "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Decrement the counter by 3&lt;/span&gt;
&lt;span class="n"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"After 'counter -= 3': "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Multiply the counter by 2&lt;/span&gt;
&lt;span class="n"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;*=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"After 'counter *= 2': "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Floating-point divide the counter by 4&lt;/span&gt;
&lt;span class="c"&gt;# Note: The type of 'counter' will change from Int to Float64&lt;/span&gt;
&lt;span class="n"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;/=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"After 'counter /= 4': "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"New type of counter: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script demonstrates Julia's updating operators, which provide a concise syntax for modifying a variable in place. These operators are syntactically and functionally identical to their counterparts in C, C++, Rust, and Python.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Syntax&lt;/strong&gt;: An updating operator is a combination of a binary operator (like &lt;code&gt;+&lt;/code&gt;, &lt;code&gt;-&lt;/code&gt;, &lt;code&gt;*&lt;/code&gt;) and the assignment operator (&lt;code&gt;=&lt;/code&gt;). The expression &lt;code&gt;x += y&lt;/code&gt; is a shorthand for &lt;code&gt;x = x + y&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Common Operators&lt;/strong&gt;: Julia supports a wide range of these operators, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;+=&lt;/code&gt; (add and assign)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-=&lt;/code&gt; (subtract and assign)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;*=&lt;/code&gt; (multiply and assign)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/=&lt;/code&gt; (divide and assign)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;÷=&lt;/code&gt; (integer divide and assign)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;%=&lt;/code&gt; (remainder and assign)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;^=&lt;/code&gt; (exponentiate and assign)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Type Promotion&lt;/strong&gt;: Be aware that the operation can change the type of the variable. As shown in the example, when &lt;code&gt;counter /= 4&lt;/code&gt; is executed, the &lt;code&gt;/&lt;/code&gt; operator performs floating-point division. The result is a &lt;code&gt;Float64&lt;/code&gt;, so the &lt;code&gt;counter&lt;/code&gt; variable is rebound to this new floating-point value.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0012_updating_operators.jl
Initial counter value: 10
After &lt;span class="s1"&gt;'counter += 5'&lt;/span&gt;: 15
After &lt;span class="s1"&gt;'counter -= 3'&lt;/span&gt;: 12
After &lt;span class="s1"&gt;'counter *= 2'&lt;/span&gt;: 24
After &lt;span class="s1"&gt;'counter /= 4'&lt;/span&gt;: 6.0
New &lt;span class="nb"&gt;type &lt;/span&gt;of counter: Float64
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Strings And Interpolation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;0013_string_basics.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0013_string_basics.jl&lt;/span&gt;

&lt;span class="c"&gt;# A standard, single-line string is created with double quotes.&lt;/span&gt;
&lt;span class="n"&gt;single_line&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"This is a standard string."&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;single_line&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Type: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;single_line&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Multi-line strings are created with triple-double quotes.&lt;/span&gt;
&lt;span class="c"&gt;# Indentation and newlines within the quotes are preserved.&lt;/span&gt;
&lt;span class="n"&gt;multi_line&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"""
This is a multi-line string.
  The indentation on this line is preserved.
It can contain any character, like π or 😊.
"""&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;multi_line&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Strings are sequences, and you can access characters by index.&lt;/span&gt;
&lt;span class="c"&gt;# Note: Julia uses 1-based indexing, not 0-based like C++/Python/Rust.&lt;/span&gt;
&lt;span class="n"&gt;first_char&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;single_line&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The first character is: '"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;first_char&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"', and its type is: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;first_char&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;

&lt;span class="c"&gt;# Attempting to modify a character will cause an error because strings are immutable.&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="n"&gt;single_line&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'t'&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error trying to modify string: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script covers the basics of creating and interacting with strings in Julia.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Literals&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Single-line strings are enclosed in double quotes (&lt;code&gt;"&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Multi-line strings are enclosed in triple-double quotes (&lt;code&gt;"""&lt;/code&gt;). This is a convenient feature for embedding blocks of text, similar to Python's triple quotes.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Encoding&lt;/strong&gt;: Julia strings are UTF-8 encoded by default. This means they can natively store any Unicode character without any special handling.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;1-Based Indexing&lt;/strong&gt;: A major difference from C/C++/Python/Rust is that Julia uses &lt;strong&gt;1-based indexing&lt;/strong&gt;. The first element of any sequence is at index &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Immutability&lt;/strong&gt;: Strings in Julia are &lt;strong&gt;immutable&lt;/strong&gt;. You cannot change the characters of an existing string. When you "modify" a string (e.g., through concatenation), you are actually creating a completely new string in memory. This is a critical design feature that ensures safety and predictable performance, as the compiler doesn't need to worry about the string's contents changing unexpectedly.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;String&lt;/code&gt; vs. &lt;code&gt;Char&lt;/code&gt;&lt;/strong&gt;: When you index into a &lt;code&gt;String&lt;/code&gt;, you get a value of type &lt;code&gt;Char&lt;/code&gt;, which represents a single Unicode code point.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0013_string_basics.jl
This is a standard string.
Type: String
&lt;span class="nt"&gt;--------------------&lt;/span&gt;
This is a multi-line string.
  The indentation on this line is preserved.
It can contain any character, like π or 😊.

The first character is: &lt;span class="s1"&gt;'T'&lt;/span&gt;, and its &lt;span class="nb"&gt;type &lt;/span&gt;is: Char
Error trying to modify string: MethodError&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;f&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;setindex!, &lt;span class="nv"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;...&lt;span class="o"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;0014_string_interpolation.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0014_string_interpolation.jl&lt;/span&gt;

&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Julia"&lt;/span&gt;
&lt;span class="n"&gt;year&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2012&lt;/span&gt;
&lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.10&lt;/span&gt;

&lt;span class="c"&gt;# 1. Basic interpolation with the '$' symbol&lt;/span&gt;
&lt;span class="c"&gt;#    The variable's value is inserted directly into the string.&lt;/span&gt;
&lt;span class="n"&gt;intro&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"My name is &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;name. I was released in &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;year."&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;intro&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 2. Expression interpolation with '$(...)'&lt;/span&gt;
&lt;span class="c"&gt;#    Any Julia expression inside the parentheses will be evaluated,&lt;/span&gt;
&lt;span class="c"&gt;#    and its result will be inserted into the string.&lt;/span&gt;
&lt;span class="n"&gt;current_year&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2025&lt;/span&gt;
&lt;span class="n"&gt;age_calculation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"It is now &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;current_year, so I am &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;(current_year - year) years old."&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;age_calculation&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# You can even call functions inside the expression.&lt;/span&gt;
&lt;span class="n"&gt;version_info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"My current version is &lt;/span&gt;&lt;span class="si"&gt;$(version)&lt;/span&gt;&lt;span class="s"&gt;, and uppercase it is &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;(uppercase(string(version)))"&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;version_info&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script demonstrates string interpolation, which is Julia's most efficient and common method for constructing strings from other values.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Syntax&lt;/strong&gt;: Interpolation is performed inside double-quoted strings (&lt;code&gt;"..."&lt;/code&gt;).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;$&lt;/code&gt; for Variables&lt;/strong&gt;: A dollar sign (&lt;code&gt;$&lt;/code&gt;) followed by a variable name inserts the value of that variable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;$(...)&lt;/code&gt; for Expressions&lt;/strong&gt;: A dollar sign followed by parentheses (&lt;code&gt;$(...)&lt;/code&gt;) evaluates any Julia code within the parentheses and inserts the result.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Performance&lt;/strong&gt;: String interpolation is extremely performant. Unlike manual string concatenation (e.g., &lt;code&gt;"a" * "b" * "c"&lt;/code&gt;), which creates multiple intermediate strings, interpolation calculates the final size and builds the new string in a single, optimized operation. This is the preferred method for building strings from parts, especially in performance-sensitive code.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0014_string_interpolation.jl
My name is Julia. I was released &lt;span class="k"&gt;in &lt;/span&gt;2012.
&lt;span class="nt"&gt;--------------------&lt;/span&gt;
It is now 2025, so I am 13 years old.
My current version is 1.1, and uppercase it is 1.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;0015_string_concatenation.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0015_string_concatenation.jl&lt;/span&gt;

&lt;span class="c"&gt;# The '*' operator is used for simple string concatenation.&lt;/span&gt;
&lt;span class="n"&gt;str1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Hello"&lt;/span&gt;
&lt;span class="n"&gt;str2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"World"&lt;/span&gt;
&lt;span class="n"&gt;combined&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;str1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="s"&gt;", "&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;str2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="s"&gt;"!"&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Concatenated with '*': "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;combined&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# --- Performance Demonstration ---&lt;/span&gt;

&lt;span class="c"&gt;# Method 1: Inefficiently building a string in a loop with '*'.&lt;/span&gt;
&lt;span class="c"&gt;# This is slow because it creates a new string in every iteration.&lt;/span&gt;
&lt;span class="n"&gt;parts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"a"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"b"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"c"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"d"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"e"&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;s_slow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;part&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;parts&lt;/span&gt;
    &lt;span class="kd"&gt;global&lt;/span&gt; &lt;span class="n"&gt;s_slow&lt;/span&gt;  &lt;span class="c"&gt;# Super important because for loop is a "soft scope".&lt;/span&gt;
                   &lt;span class="c"&gt;# Without declaring the global Julia tries to create a local.&lt;/span&gt;
    &lt;span class="n"&gt;s_slow&lt;/span&gt; &lt;span class="o"&gt;*=&lt;/span&gt; &lt;span class="n"&gt;part&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Result from slow loop: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s_slow&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;


&lt;span class="c"&gt;# Method 2: The performant and idiomatic way using 'join()'.&lt;/span&gt;
&lt;span class="c"&gt;# This calculates the final size once and builds the string efficiently.&lt;/span&gt;
&lt;span class="n"&gt;s_fast&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Result from fast join: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s_fast&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script demonstrates how to join strings and highlights the critical performance difference between concatenation in a loop and using the &lt;code&gt;join()&lt;/code&gt; function.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;*&lt;/code&gt; Operator&lt;/strong&gt;: For joining a small, fixed number of strings, the &lt;code&gt;*&lt;/code&gt; operator is a perfectly readable and acceptable choice. &lt;code&gt;str1 * str2&lt;/code&gt; creates a new string containing the contents of &lt;code&gt;str1&lt;/code&gt; followed by &lt;code&gt;str2&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Performance in Loops ❗
&lt;/h3&gt;

&lt;p&gt;This is a crucial performance concept that translates directly from languages like Python.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Inefficient Loop (&lt;code&gt;*=&lt;/code&gt;):&lt;/strong&gt; When you use &lt;code&gt;s_slow *= part&lt;/code&gt; inside a loop, you are not modifying the string &lt;code&gt;s_slow&lt;/code&gt;. Because strings are immutable, Julia must allocate a &lt;strong&gt;brand new string&lt;/strong&gt; that is large enough to hold the old &lt;code&gt;s_slow&lt;/code&gt; plus the new &lt;code&gt;part&lt;/code&gt;, copy the contents of both into it, and then reassign the name &lt;code&gt;s_slow&lt;/code&gt; to this new string. In a loop with many iterations, this results in excessive memory allocations and copying, leading to very poor performance.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Performant &lt;code&gt;join()&lt;/code&gt;:&lt;/strong&gt; The &lt;code&gt;join()&lt;/code&gt; function is the correct and idiomatic way to combine a collection of strings. It first iterates through the collection to calculate the total size of the final string. Then, it allocates a single block of memory of the correct size and copies each part into it just once. This "calculate-then-allocate" strategy avoids creating many intermediate strings and is dramatically faster.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Rule of Thumb&lt;/strong&gt;: Always use &lt;code&gt;join()&lt;/code&gt; when combining a variable number of strings, especially from within a loop.&lt;/p&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0015_string_concatenation.jl
Concatenated with &lt;span class="s1"&gt;'*'&lt;/span&gt;: Hello, World!
&lt;span class="nt"&gt;--------------------&lt;/span&gt;
Result from slow loop: abcde
Result from fast &lt;span class="nb"&gt;join&lt;/span&gt;: abcde
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Module 2: Control Flow
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Conditional Logic
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;0016_if_else.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0016_if_else.jl&lt;/span&gt;

&lt;span class="c"&gt;# A simple function to check if a number is even or odd&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; check_parity&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The number "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;" is even."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The number "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;" is odd."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;check_parity&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;check_parity&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script demonstrates the fundamental &lt;code&gt;if&lt;/code&gt;/&lt;code&gt;else&lt;/code&gt; statement, which is the most basic structure for conditional logic.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Syntax&lt;/strong&gt;: The structure is &lt;code&gt;if &amp;lt;condition&amp;gt; ... else ... end&lt;/code&gt;. The code inside the &lt;code&gt;if&lt;/code&gt; block is executed if the &lt;code&gt;&amp;lt;condition&amp;gt;&lt;/code&gt; evaluates to &lt;code&gt;true&lt;/code&gt;. Otherwise, the code inside the &lt;code&gt;else&lt;/code&gt; block is executed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Condition&lt;/strong&gt;: The condition (&lt;code&gt;n % 2 == 0&lt;/code&gt;) must be an expression that results in a &lt;code&gt;Bool&lt;/code&gt; (&lt;code&gt;true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;end&lt;/code&gt; Keyword&lt;/strong&gt;: Unlike Python's indentation or C++/Rust's curly braces, Julia uses the &lt;code&gt;end&lt;/code&gt; keyword to terminate blocks of code, including &lt;code&gt;if&lt;/code&gt; statements and functions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0016_if_else.jl
The number 10 is even.
The number 7 is odd.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;0017_if_elseif_else.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0017_if_elseif_else.jl&lt;/span&gt;

&lt;span class="c"&gt;# A function to check the sign of a number&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; check_sign&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The number "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;" is positive."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elseif&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The number "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;" is negative."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The number "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;" is zero."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;check_sign&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;check_sign&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;check_sign&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces the &lt;code&gt;if&lt;/code&gt;/&lt;code&gt;elseif&lt;/code&gt;/&lt;code&gt;else&lt;/code&gt; structure, which allows you to chain multiple conditions together.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Syntax&lt;/strong&gt;: The structure is &lt;code&gt;if &amp;lt;condition1&amp;gt; ... elseif &amp;lt;condition2&amp;gt; ... else ... end&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Execution Flow&lt;/strong&gt;: Julia evaluates the conditions sequentially from top to bottom.

&lt;ol&gt;
&lt;li&gt; First, it checks &lt;code&gt;if n &amp;gt; 0&lt;/code&gt;. If this is &lt;code&gt;true&lt;/code&gt;, its block is executed, and the entire chain is exited.&lt;/li&gt;
&lt;li&gt; Only if the first condition is &lt;code&gt;false&lt;/code&gt;, it then checks &lt;code&gt;elseif n &amp;lt; 0&lt;/code&gt;. If this is &lt;code&gt;true&lt;/code&gt;, its block is executed, and the chain is exited.&lt;/li&gt;
&lt;li&gt; If all preceding &lt;code&gt;if&lt;/code&gt; and &lt;code&gt;elseif&lt;/code&gt; conditions are &lt;code&gt;false&lt;/code&gt;, the final &lt;code&gt;else&lt;/code&gt; block is executed as a fallback.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This structure is a direct equivalent to &lt;code&gt;if&lt;/code&gt;/&lt;code&gt;else if&lt;/code&gt;/&lt;code&gt;else&lt;/code&gt; in C++/Rust and &lt;code&gt;if&lt;/code&gt;/&lt;code&gt;elif&lt;/code&gt;/&lt;code&gt;else&lt;/code&gt; in Python. It's a clean way to handle a series of mutually exclusive conditions.&lt;/p&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0017_if_elseif_else.jl
The number 10 is positive.
The number &lt;span class="nt"&gt;-5&lt;/span&gt; is negative.
The number 0 is zero.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;0018_ternary_operator.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0018_ternary_operator.jl&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; get_parity_message&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# The ternary operator provides a concise way to write a simple if/else.&lt;/span&gt;
    &lt;span class="c"&gt;# The structure is: &amp;lt;condition&amp;gt; ? &amp;lt;value_if_true&amp;gt; : &amp;lt;value_if_false&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s"&gt;"even"&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"odd"&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"The number &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;n is &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;message."&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_parity_message&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_parity_message&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces the &lt;strong&gt;ternary operator&lt;/strong&gt;, a compact syntax for a simple conditional expression.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Syntax&lt;/strong&gt;: The syntax &lt;code&gt;a ? b : c&lt;/code&gt; is identical to its usage in C, C++, Rust, and Python. The parentheses around the condition, &lt;code&gt;(n % 2 == 0)&lt;/code&gt;, are not strictly required but are often used to improve readability.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Execution&lt;/strong&gt;: The condition &lt;code&gt;a&lt;/code&gt; is evaluated first.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If it's &lt;code&gt;true&lt;/code&gt;, the entire expression evaluates to &lt;code&gt;b&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If it's &lt;code&gt;false&lt;/code&gt;, the entire expression evaluates to &lt;code&gt;c&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Usage&lt;/strong&gt;: It's best used for assigning one of two simple values to a variable based on a single condition. It's an expression that returns a value, not a statement that performs actions. For logic involving multiple lines or &lt;code&gt;elseif&lt;/code&gt; branches, a full &lt;code&gt;if/else&lt;/code&gt; block remains more readable and appropriate.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0018_ternary_operator.jl
The number 10 is even.
The number 7 is odd.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;0019_short_circuit_guard.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0019_short_circuit_guard.jl&lt;/span&gt;

&lt;span class="c"&gt;# A simple data structure to hold a value.&lt;/span&gt;
&lt;span class="k"&gt;mutable struct&lt;/span&gt;&lt;span class="nc"&gt; Container&lt;/span&gt;
    &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# This function safely processes a container.&lt;/span&gt;
&lt;span class="c"&gt;# The variable 'obj' can either be a 'Container' or 'nothing'.&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; process_container&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# This is a "guard clause" using short-circuiting.&lt;/span&gt;
    &lt;span class="c"&gt;# The second part, 'obj.value &amp;gt; 10', is ONLY evaluated if the first part is true.&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nb"&gt;nothing&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
        &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Processing container with high value: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Skipping, object is either nothing or its value is not &amp;gt; 10."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# Create an instance of our container&lt;/span&gt;
&lt;span class="n"&gt;c1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Container&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# Create a variable that holds 'nothing'&lt;/span&gt;
&lt;span class="n"&gt;c2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;nothing&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Processing a valid container ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;process_container&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c1&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Processing 'nothing' ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# Without the short-circuit guard, `c2.value` would cause a crash.&lt;/span&gt;
&lt;span class="n"&gt;process_container&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c2&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script demonstrates a practical and critical use of the &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; operator's short-circuiting behavior: creating a &lt;strong&gt;guard clause&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Problem&lt;/strong&gt;: In many languages, you might have a variable that could be &lt;code&gt;null&lt;/code&gt; (or &lt;code&gt;None&lt;/code&gt; in Python). In Julia, the equivalent is &lt;code&gt;nothing&lt;/code&gt;. If you try to access a member of &lt;code&gt;nothing&lt;/code&gt; (e.g., &lt;code&gt;nothing.value&lt;/code&gt;), your program will crash.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Solution&lt;/strong&gt;: Short-circuiting provides an elegant and performant solution. In the line &lt;code&gt;if obj !== nothing &amp;amp;&amp;amp; obj.value &amp;gt; 10&lt;/code&gt;:&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1.  Julia first evaluates `obj !== nothing`. The `!==` operator is the negation of `===` (strict identity) and is the standard way to check if something is not `nothing`.
2.  If `obj` is `nothing`, this expression is `false`. Because this is an `&amp;amp;&amp;amp;` (AND) operation, the entire condition *must* be false, so Julia **stops evaluating** and does not execute the right side.
3.  The right side, `obj.value &amp;gt; 10`, is only ever reached if the first check passed, guaranteeing that `obj` is a valid `Container` object and that accessing `.value` is safe.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This pattern is fundamental in Julia (and many other languages) for writing robust code that gracefully handles potentially missing values.&lt;/p&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0019_short_circuit_guard.jl
&lt;span class="nt"&gt;---&lt;/span&gt; Processing a valid container &lt;span class="nt"&gt;---&lt;/span&gt;
Processing container with high value: 20

&lt;span class="nt"&gt;---&lt;/span&gt; Processing &lt;span class="s1"&gt;'nothing'&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
Skipping, object is either nothing or its value is not &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 10.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Loops
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;0020_for_loop_range.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0020_for_loop_range.jl&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Iterating from 1 to 5 ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# The expression '1:5' creates a UnitRange object.&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Current value of i is: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Iterating with a step ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# The expression '2:2:10' creates a StepRange object.&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Current value of j is: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces the &lt;code&gt;for&lt;/code&gt; loop, Julia's primary construct for iteration.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Syntax&lt;/strong&gt;: The basic structure is &lt;code&gt;for &amp;lt;variable&amp;gt; in &amp;lt;iterable&amp;gt; ... end&lt;/code&gt;. The code inside the loop is executed for each element in the &lt;code&gt;&amp;lt;iterable&amp;gt;&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Ranges&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;UnitRange&lt;/code&gt; (&lt;code&gt;start:stop&lt;/code&gt;)&lt;/strong&gt;: The expression &lt;code&gt;1:5&lt;/code&gt; creates a &lt;code&gt;UnitRange&lt;/code&gt;, which is a lightweight object that represents the sequence of integers from 1 to 5. It is &lt;strong&gt;performant&lt;/strong&gt; because it doesn't actually allocate memory to store all the numbers; it just tracks the start and end points.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;StepRange&lt;/code&gt; (&lt;code&gt;start:step:stop&lt;/code&gt;)&lt;/strong&gt;: The expression &lt;code&gt;2:2:10&lt;/code&gt; creates a &lt;code&gt;StepRange&lt;/code&gt;, representing the sequence starting at 2, incrementing by 2, up to 10. This is also a very efficient object.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;This is the direct equivalent of &lt;code&gt;for (int i = 1; i &amp;lt;= 5; ++i)&lt;/code&gt; in C/C++/Rust or &lt;code&gt;for i in range(1, 6)&lt;/code&gt; in Python.&lt;/p&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0020_for_loop_range.jl
&lt;span class="nt"&gt;---&lt;/span&gt; Iterating from 1 to 5 &lt;span class="nt"&gt;---&lt;/span&gt;
Current value of i is: 1
Current value of i is: 2
Current value of i is: 3
Current value of i is: 4
Current value of i is: 5

&lt;span class="nt"&gt;---&lt;/span&gt; Iterating with a step &lt;span class="nt"&gt;---&lt;/span&gt;
Current value of j is: 2
Current value of j is: 4
Current value of j is: 6
Current value of j is: 8
Current value of j is: 10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;0021_for_loop_collection.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0021_for_loop_collection.jl&lt;/span&gt;

&lt;span class="c"&gt;# A Vector is Julia's primary resizable array type.&lt;/span&gt;
&lt;span class="n"&gt;fruits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Apple"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Banana"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Cherry"&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Iterating over a Vector of strings ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;fruit&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;fruits&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Processing: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fruit&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Iterating with index and value using enumerate ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fruit&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;enumerate&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fruits&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Item at index "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;" is: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fruit&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script shows how to iterate directly over the elements of a collection, which is one of the most common uses for a &lt;code&gt;for&lt;/code&gt; loop.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Direct Iteration&lt;/strong&gt;: The syntax &lt;code&gt;for fruit in fruits&lt;/code&gt; iterates through each element of the &lt;code&gt;fruits&lt;/code&gt; collection, assigning the element to the &lt;code&gt;fruit&lt;/code&gt; variable for each pass of the loop. This is the direct equivalent of a range-based &lt;code&gt;for&lt;/code&gt; loop in C++/Rust or a standard &lt;code&gt;for item in list&lt;/code&gt; loop in Python. It's the most readable and idiomatic way to process every item in a collection.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;enumerate()&lt;/code&gt;&lt;/strong&gt;: If you need both the index and the value during iteration, the &lt;code&gt;enumerate()&lt;/code&gt; function provides an efficient way to do so. It wraps the collection and, on each iteration, yields a tuple of &lt;code&gt;(index, value)&lt;/code&gt;. This is preferable to manually managing an index counter (e.g., &lt;code&gt;i = 1; for fruit in fruits... i += 1&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0021_for_loop_collection.jl
&lt;span class="nt"&gt;---&lt;/span&gt; Iterating over a Vector of strings &lt;span class="nt"&gt;---&lt;/span&gt;
Processing: Apple
Processing: Banana
Processing: Cherry

&lt;span class="nt"&gt;---&lt;/span&gt; Iterating with index and value using enumerate &lt;span class="nt"&gt;---&lt;/span&gt;
Item at index 1 is: Apple
Item at index 2 is: Banana
Item at index 3 is: Cherry
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;0022_while_loop.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0022_while_loop.jl&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Countdown from 5 using a while loop ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Initialize a counter variable outside the loop&lt;/span&gt;
&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;

&lt;span class="c"&gt;# The loop will continue as long as n is greater than 0&lt;/span&gt;
&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Current value of n is: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# It is crucial to update the condition variable inside the loop&lt;/span&gt;
    &lt;span class="kd"&gt;global&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Blast off!"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script demonstrates the &lt;code&gt;while&lt;/code&gt; loop, which executes a block of code repeatedly as long as a specified condition remains true.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Syntax&lt;/strong&gt;: The structure is &lt;code&gt;while &amp;lt;condition&amp;gt; ... end&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Execution Flow&lt;/strong&gt;: Before each iteration, the &lt;code&gt;&amp;lt;condition&amp;gt;&lt;/code&gt; is evaluated. If it's &lt;code&gt;true&lt;/code&gt;, the body of the loop is executed. If it's &lt;code&gt;false&lt;/code&gt;, the loop terminates, and execution continues after the &lt;code&gt;end&lt;/code&gt; keyword.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Loop Variable&lt;/strong&gt;: It's the programmer's responsibility to ensure the condition eventually becomes false. In this example, &lt;code&gt;n -= 1&lt;/code&gt; decrements the counter in each iteration. Forgetting this line would result in an infinite loop, as &lt;code&gt;n&lt;/code&gt; would always be &lt;code&gt;5&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;global&lt;/code&gt; Keyword&lt;/strong&gt;: Just like in the &lt;code&gt;for&lt;/code&gt; loop example, because we are modifying a global variable &lt;code&gt;n&lt;/code&gt; from within the "soft scope" of the &lt;code&gt;while&lt;/code&gt; loop, we must use &lt;code&gt;global n -= 1&lt;/code&gt; to explicitly state our intent to modify the global variable.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;while&lt;/code&gt; loops are best used when the number of iterations isn't known beforehand and depends on a state that changes within the loop.&lt;/p&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0022_while_loop.jl
&lt;span class="nt"&gt;---&lt;/span&gt; Countdown from 5 using a &lt;span class="k"&gt;while &lt;/span&gt;loop &lt;span class="nt"&gt;---&lt;/span&gt;
Current value of n is: 5
Current value of n is: 4
Current value of n is: 3
Current value of n is: 2
Current value of n is: 1
Blast off!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;0023_loop_control.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0023_loop_control.jl&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Using 'continue' and 'break' in a loop from 1 to 10 ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;
    &lt;span class="c"&gt;# If i is 3, skip the rest of this iteration and start the next one.&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
        &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Skipping 3 with 'continue'..."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;continue&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="c"&gt;# If i is 8, terminate the loop completely.&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;
        &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Exiting loop at 8 with 'break'..."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;break&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Processing number: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Loop finished."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script demonstrates the two essential keywords for controlling the flow of a loop: &lt;code&gt;continue&lt;/code&gt; and &lt;code&gt;break&lt;/code&gt;. Their behavior is identical to their counterparts in C, C++, Rust, and Python.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;continue&lt;/code&gt;&lt;/strong&gt;: This keyword immediately stops the current iteration of the loop. The program "skips" the rest of the code in the loop's body for the current element and moves on to the next one. In the example, when &lt;code&gt;i&lt;/code&gt; is 3, the &lt;code&gt;println("Processing...")&lt;/code&gt; line is never reached.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;break&lt;/code&gt;&lt;/strong&gt;: This keyword immediately terminates the innermost loop it is in. Execution jumps to the first line of code after the loop's &lt;code&gt;end&lt;/code&gt; block. In the example, once &lt;code&gt;i&lt;/code&gt; reaches 8, the loop stops entirely, and numbers 9 and 10 are never processed.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These keywords are fundamental tools for handling special cases or termination conditions within an iterative process.&lt;/p&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0023_loop_control.jl
&lt;span class="nt"&gt;---&lt;/span&gt; Using &lt;span class="s1"&gt;'continue'&lt;/span&gt; and &lt;span class="s1"&gt;'break'&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;a loop from 1 to 10 &lt;span class="nt"&gt;---&lt;/span&gt;
Processing number: 1
Processing number: 2
Skipping 3 with &lt;span class="s1"&gt;'continue'&lt;/span&gt;...
Processing number: 4
Processing number: 5
Processing number: 6
Processing number: 7
Exiting loop at 8 with &lt;span class="s1"&gt;'break'&lt;/span&gt;...
Loop finished.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;0024_nested_loops.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0024_nested_loops.jl&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Demonstrating nested loops to create coordinate pairs ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# The outer loop iterates from 1 to 3&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;
    &lt;span class="c"&gt;# The inner loop iterates from 1 to 2&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
        &lt;span class="c"&gt;# This line is executed for every combination of i and j.&lt;/span&gt;
        &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Coordinate: ("&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;", "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;")"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="c"&gt;# This line is executed after the inner loop completes for a given i.&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Inner loop finished for i = "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;" ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script shows a &lt;strong&gt;nested loop&lt;/strong&gt;, where one loop is placed inside another.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Execution Flow&lt;/strong&gt;: The inner loop (&lt;code&gt;for j in 1:2&lt;/code&gt;) runs to completion for &lt;strong&gt;each single iteration&lt;/strong&gt; of the outer loop (&lt;code&gt;for i in 1:3&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1.  The outer loop starts with `i = 1`.
2.  The inner loop then runs completely for `j = 1` and `j = 2`.
3.  The outer loop moves to `i = 2`.
4.  The inner loop runs completely again for `j = 1` and `j = 2`.
5.  This process repeats until the outer loop is finished.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Compact Syntax&lt;/strong&gt;: Julia also offers a more compact syntax for nested loops, which is often more readable:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Coordinate: ("&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;", "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;")"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;This single loop header is equivalent to the two separate &lt;code&gt;for&lt;/code&gt; blocks.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nested loops are commonly used for tasks like iterating over 2D arrays (matrices), generating combinations, or creating coordinate grids.&lt;/p&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0024_nested_loops.jl
&lt;span class="nt"&gt;---&lt;/span&gt; Demonstrating nested loops to create coordinate pairs &lt;span class="nt"&gt;---&lt;/span&gt;
Coordinate: &lt;span class="o"&gt;(&lt;/span&gt;1, 1&lt;span class="o"&gt;)&lt;/span&gt;
Coordinate: &lt;span class="o"&gt;(&lt;/span&gt;1, 2&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nt"&gt;---&lt;/span&gt; Inner loop finished &lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="o"&gt;=&lt;/span&gt; 1 &lt;span class="nt"&gt;---&lt;/span&gt;
Coordinate: &lt;span class="o"&gt;(&lt;/span&gt;2, 1&lt;span class="o"&gt;)&lt;/span&gt;
Coordinate: &lt;span class="o"&gt;(&lt;/span&gt;2, 2&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nt"&gt;---&lt;/span&gt; Inner loop finished &lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="o"&gt;=&lt;/span&gt; 2 &lt;span class="nt"&gt;---&lt;/span&gt;
Coordinate: &lt;span class="o"&gt;(&lt;/span&gt;3, 1&lt;span class="o"&gt;)&lt;/span&gt;
Coordinate: &lt;span class="o"&gt;(&lt;/span&gt;3, 2&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nt"&gt;---&lt;/span&gt; Inner loop finished &lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="o"&gt;=&lt;/span&gt; 3 &lt;span class="nt"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;0025_loop_performance.md&lt;/code&gt;
&lt;/h3&gt;

&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;As a systems programmer, you know that the performance of a loop is critical. In interpreted languages like Python, loops are famously slow because the interpreter has to re-evaluate every operation in every iteration. Julia solves this problem, achieving C/Rust-level speed for loops.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Julia Performance Model: Functions are Compilation Boundaries
&lt;/h2&gt;

&lt;p&gt;The single most important rule is: &lt;strong&gt;For performance, put your code in functions.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Global Scope is Slow&lt;/strong&gt;: When you run a &lt;code&gt;for&lt;/code&gt; loop in the global scope (like in many of our basic examples), Julia's compiler can't make many assumptions. The types of the variables involved could change at any time, forcing the interpreter to fall back to slow, dynamic lookups in every iteration.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Functions are Fast&lt;/strong&gt;: When you put a loop inside a function, the Julia JIT compiler can perform powerful optimizations. The first time you call a function with arguments of specific types (e.g., &lt;code&gt;my_function(10, 3.0)&lt;/code&gt;), the compiler:&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1.  **Analyzes Types**: It traces the types of all variables throughout the function.
2.  **Checks for Type Stability**: It checks if the types of variables change within the function.
3.  **Generates Specialized Machine Code**: If the function is type-stable, the compiler generates a highly optimized version of that function specifically for those input types.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The result is machine code that is just as fast as what a C++ or Rust compiler would produce. The overhead of the JIT compilation happens only once (the first time), and every subsequent call to the function with the same argument types is extremely fast.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example: The "Why"
&lt;/h3&gt;

&lt;p&gt;Consider this simple loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# Slow if run in global scope&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1_000_000_000&lt;/span&gt;
    &lt;span class="c"&gt;# operation&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# Fast if run like this&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; loop_in_a_function&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1_000_000_000&lt;/span&gt;
        &lt;span class="c"&gt;# operation&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;loop_in_a_function&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt; &lt;span class="c"&gt;# First call compiles, subsequent calls are fast&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside &lt;code&gt;loop_in_a_function&lt;/code&gt;, the compiler knows the type of &lt;code&gt;i&lt;/code&gt; will always be an &lt;code&gt;Int&lt;/code&gt;. It can then unroll the loop, use CPU registers, and apply other low-level optimizations, just as &lt;code&gt;gcc&lt;/code&gt; or &lt;code&gt;clang&lt;/code&gt; would. In the global scope, it cannot make these guarantees.&lt;/p&gt;

&lt;p&gt;This "compilation boundary" at the function level is the core of Julia's performance model and the reason it successfully solves the "two-language problem" (where you prototype in a slow language and rewrite in a fast one). In Julia, the prototype &lt;em&gt;is&lt;/em&gt; the fast code, as long as it's written in functions.&lt;/p&gt;




&lt;h1&gt;
  
  
  Module 3: Collections
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Tuples
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;0026_tuples.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0026_tuples.jl&lt;/span&gt;

&lt;span class="c"&gt;# 1. Tuples are created with parentheses and commas.&lt;/span&gt;
&lt;span class="c"&gt;#    They are immutable and have a fixed size.&lt;/span&gt;
&lt;span class="n"&gt;my_tuple&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"hello"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Tuple value: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;my_tuple&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Tuple type: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_tuple&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 2. Elements are accessed with 1-based indexing.&lt;/span&gt;
&lt;span class="n"&gt;first_element&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;my_tuple&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;second_element&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;my_tuple&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"First element: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;first_element&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Second element: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;second_element&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 3. You can "destructure" a tuple to unpack its values into separate variables.&lt;/span&gt;
&lt;span class="c"&gt;#    This is a common and efficient way to handle multiple return values from a function.&lt;/span&gt;
&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;my_tuple&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Unpacked variable 'a': "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Unpacked variable 'b': "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Unpacked variable 'c': "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 4. Attempting to modify a tuple will result in an error.&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="n"&gt;my_tuple&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Error trying to modify a tuple: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces the &lt;strong&gt;tuple&lt;/strong&gt;, a fixed-size, immutable collection of ordered elements. Its properties make it a highly performant data structure, very similar to a &lt;code&gt;std::tuple&lt;/code&gt; in C++ or a tuple in Python.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Creation&lt;/strong&gt;: Tuples are defined by enclosing comma-separated values in parentheses &lt;code&gt;()&lt;/code&gt;. The type of the tuple, like &lt;code&gt;Tuple{Int64, String, Bool}&lt;/code&gt;, is determined by the types of the elements it contains.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Immutability&lt;/strong&gt;: Once a tuple is created, its contents cannot be changed. This makes it a safe and predictable data structure to pass around, as you can be certain it won't be modified.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Access&lt;/strong&gt;: Elements are accessed using square brackets &lt;code&gt;[]&lt;/code&gt; with &lt;strong&gt;1-based indexing&lt;/strong&gt;, just like strings. &lt;code&gt;my_tuple[1]&lt;/code&gt; retrieves the first element.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Destructuring&lt;/strong&gt;: This is a powerful feature where you can unpack the elements of a tuple directly into variables. The syntax &lt;code&gt;(a, b, c) = my_tuple&lt;/code&gt; assigns &lt;code&gt;my_tuple[1]&lt;/code&gt; to &lt;code&gt;a&lt;/code&gt;, &lt;code&gt;my_tuple[2]&lt;/code&gt; to &lt;code&gt;b&lt;/code&gt;, and so on. This is the idiomatic way to handle functions that return multiple values.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0026_tuples.jl
Tuple value: &lt;span class="o"&gt;(&lt;/span&gt;10, &lt;span class="s2"&gt;"hello"&lt;/span&gt;, &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
Tuple &lt;span class="nb"&gt;type&lt;/span&gt;: Tuple&lt;span class="o"&gt;{&lt;/span&gt;Int64, String, Bool&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;--------------------&lt;/span&gt;
First element: 10
Second element: hello
&lt;span class="nt"&gt;--------------------&lt;/span&gt;
Unpacked variable &lt;span class="s1"&gt;'a'&lt;/span&gt;: 10
Unpacked variable &lt;span class="s1"&gt;'b'&lt;/span&gt;: hello
Unpacked variable &lt;span class="s1"&gt;'c'&lt;/span&gt;: &lt;span class="nb"&gt;true

&lt;/span&gt;Error trying to modify a tuple: MethodError&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;f&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;setindex!, &lt;span class="nv"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;...&lt;span class="o"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;0027_named_tuples.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0027_named_tuples.jl&lt;/span&gt;

&lt;span class="c"&gt;# 1. A NamedTuple is created with a syntax similar to a tuple,&lt;/span&gt;
&lt;span class="c"&gt;#    but each element is given a name.&lt;/span&gt;
&lt;span class="n"&gt;point&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Start"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"NamedTuple value: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"NamedTuple type: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 2. Elements can be accessed like struct fields using dot notation.&lt;/span&gt;
&lt;span class="c"&gt;#    This is the primary and most readable way to access them.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Access via name (point.x): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Access via name (point.label): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 3. It is still a tuple, so you can also access elements by index.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Access via index (point[1]): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Access via index (point[3]): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="x"&gt;])&lt;/span&gt;

&lt;span class="c"&gt;# You can also get its keys and values&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Keys: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Values: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces the &lt;strong&gt;&lt;code&gt;NamedTuple&lt;/code&gt;&lt;/strong&gt;, which combines the performance and immutability of a tuple with the readability of a &lt;code&gt;struct&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Syntax&lt;/strong&gt;: A &lt;code&gt;NamedTuple&lt;/code&gt; is created by assigning names to each element within the parentheses: &lt;code&gt;(name1 = value1, name2 = value2)&lt;/code&gt;. The resulting type includes the names and the types of the values, like &lt;code&gt;NamedTuple{(:x, :y, :label), Tuple{Int64, Int64, String}}&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Access&lt;/strong&gt;: The key advantage of a &lt;code&gt;NamedTuple&lt;/code&gt; is that you can access its elements using dot notation (&lt;code&gt;point.x&lt;/code&gt;), which makes the code self-documenting. You can still access elements by their 1-based index (&lt;code&gt;point[1]&lt;/code&gt;) just like a regular tuple.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use Case&lt;/strong&gt;: &lt;code&gt;NamedTuple&lt;/code&gt;s are extremely useful as lightweight, "anonymous" structs. They are perfect for returning multiple, clearly-labeled values from a function without the need to define a formal &lt;code&gt;struct&lt;/code&gt; type beforehand. Because they are immutable and have a fixed structure known at compile time, they are just as &lt;strong&gt;performant&lt;/strong&gt; as regular tuples.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0027_named_tuples.jl
NamedTuple value: &lt;span class="o"&gt;(&lt;/span&gt;x &lt;span class="o"&gt;=&lt;/span&gt; 10, y &lt;span class="o"&gt;=&lt;/span&gt; 20, label &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Start"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
NamedTuple &lt;span class="nb"&gt;type&lt;/span&gt;: NamedTuple&lt;span class="o"&gt;{(&lt;/span&gt;:x, :y, :label&lt;span class="o"&gt;)&lt;/span&gt;, Tuple&lt;span class="o"&gt;{&lt;/span&gt;Int64, Int64, String&lt;span class="o"&gt;}}&lt;/span&gt;
&lt;span class="nt"&gt;--------------------&lt;/span&gt;
Access via name &lt;span class="o"&gt;(&lt;/span&gt;point.x&lt;span class="o"&gt;)&lt;/span&gt;: 10
Access via name &lt;span class="o"&gt;(&lt;/span&gt;point.label&lt;span class="o"&gt;)&lt;/span&gt;: Start
&lt;span class="nt"&gt;--------------------&lt;/span&gt;
Access via index &lt;span class="o"&gt;(&lt;/span&gt;point[1]&lt;span class="o"&gt;)&lt;/span&gt;: 10
Access via index &lt;span class="o"&gt;(&lt;/span&gt;point[3]&lt;span class="o"&gt;)&lt;/span&gt;: Start
Keys: &lt;span class="o"&gt;(&lt;/span&gt;:x, :y, :label&lt;span class="o"&gt;)&lt;/span&gt;
Values: &lt;span class="o"&gt;(&lt;/span&gt;10, 20, &lt;span class="s2"&gt;"Start"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;0028_tuple_performance.md&lt;/code&gt;
&lt;/h3&gt;

&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;For a systems programmer, understanding &lt;em&gt;why&lt;/em&gt; a data structure is fast is as important as knowing how to use it. Tuples and &lt;code&gt;NamedTuple&lt;/code&gt;s are among the most performant data structures in Julia because of how the compiler treats them.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Tuples are Fast
&lt;/h2&gt;

&lt;p&gt;A tuple in Julia is conceptually very similar to a &lt;code&gt;struct&lt;/code&gt; in C.&lt;/p&gt;

&lt;p&gt;Consider this C &lt;code&gt;struct&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Point&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And this Julia &lt;code&gt;NamedTuple&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="n"&gt;point&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;3.14&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Julia compiler can optimize the &lt;code&gt;NamedTuple&lt;/code&gt; to have a memory layout and performance profile that is virtually identical to the C &lt;code&gt;struct&lt;/code&gt;. Here’s why:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Immutable&lt;/strong&gt;: Because tuples cannot be changed after creation, the compiler has a strong guarantee about their state. It knows the values and types inside a tuple are fixed for its entire lifetime.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Fixed-Size and Type-Stable&lt;/strong&gt;: The size, type, and order of elements in a tuple are known at compile time. This allows the compiler to generate specialized, highly efficient machine code to access its elements. There is no dynamic lookup; accessing &lt;code&gt;point.x&lt;/code&gt; can be compiled down to a simple memory offset from a base pointer, just like accessing a member of a C &lt;code&gt;struct&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Stack Allocation&lt;/strong&gt;: For small, simple tuples (containing primitive types like numbers), the compiler will often allocate them directly on the &lt;strong&gt;stack&lt;/strong&gt; instead of the heap. Stack allocation is significantly faster than heap allocation because it's just a matter of moving the stack pointer. This completely avoids the overhead of the garbage collector (GC), making their use in tight loops extremely cheap.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In summary, you should feel confident using tuples and &lt;code&gt;NamedTuple&lt;/code&gt;s in performance-critical code. They are not like Python tuples, which carry extra overhead. Julia tuples are lightweight, compile-time constructs that map very closely to the efficient memory layouts you are used to in C, C++, and Rust.&lt;/p&gt;




&lt;h2&gt;
  
  
  Vector
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;0029_vector_basics.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0029_vector_basics.jl&lt;/span&gt;

&lt;span class="c"&gt;# 1. A Vector is created with square brackets.&lt;/span&gt;
&lt;span class="c"&gt;#    It is a mutable, resizable, one-dimensional array.&lt;/span&gt;
&lt;span class="n"&gt;my_vector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Vector value: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;my_vector&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Vector type: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_vector&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Initial length: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_vector&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 2. Use `push!` to add elements to the end of the vector.&lt;/span&gt;
&lt;span class="c"&gt;#    The '!' signifies that this function modifies its first argument.&lt;/span&gt;
&lt;span class="n"&gt;push!&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_vector&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;push!&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_vector&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Vector after pushing elements: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;my_vector&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"New length: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_vector&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 3. Access and modify elements using 1-based indexing.&lt;/span&gt;
&lt;span class="c"&gt;#    Because Vectors are mutable, their elements can be changed.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Element at index 2: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;my_vector&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="x"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;my_vector&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Vector after modification: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;my_vector&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces the &lt;strong&gt;&lt;code&gt;Vector&lt;/code&gt;&lt;/strong&gt;, which is Julia's fundamental, resizable, one-dimensional array. It's the direct equivalent of &lt;code&gt;std::vector&lt;/code&gt; in C++, &lt;code&gt;Vec&lt;/code&gt; in Rust, or &lt;code&gt;list&lt;/code&gt; in Python.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Creation&lt;/strong&gt;: Vectors are created using square brackets &lt;code&gt;[...]&lt;/code&gt;. The type of the vector is inferred from the elements it contains. &lt;code&gt;[10, 20, 30]&lt;/code&gt; creates a &lt;code&gt;Vector{Int64}&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Mutability&lt;/strong&gt;: Unlike tuples, vectors are &lt;strong&gt;mutable&lt;/strong&gt;. You can add, remove, and change their elements after they are created.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;push!()&lt;/code&gt;&lt;/strong&gt;: The standard function for appending an element to the end of a vector is &lt;code&gt;push!&lt;/code&gt;. The &lt;code&gt;!&lt;/code&gt; at the end is a Julia convention indicating that the function &lt;strong&gt;modifies&lt;/strong&gt; its first argument (in this case, &lt;code&gt;my_vector&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;length()&lt;/code&gt;&lt;/strong&gt;: This function returns the number of elements currently in the vector.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Access &amp;amp; Modification&lt;/strong&gt;: You can access and reassign elements using 1-based indexing (&lt;code&gt;my_vector[2] = 25&lt;/code&gt;), just like you would with a standard C array or &lt;code&gt;std::vector&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0029_vector_basics.jl
Vector value: &lt;span class="o"&gt;[&lt;/span&gt;10, 20, 30]
Vector &lt;span class="nb"&gt;type&lt;/span&gt;: Vector&lt;span class="o"&gt;{&lt;/span&gt;Int64&lt;span class="o"&gt;}&lt;/span&gt;
Initial length: 3
&lt;span class="nt"&gt;--------------------&lt;/span&gt;
Vector after pushing elements: &lt;span class="o"&gt;[&lt;/span&gt;10, 20, 30, 40, 50]
New length: 5
&lt;span class="nt"&gt;--------------------&lt;/span&gt;
Element at index 2: 20
Vector after modification: &lt;span class="o"&gt;[&lt;/span&gt;10, 25, 30, 40, 50]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;0030_vector_slicing.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0030_vector_slicing.jl&lt;/span&gt;

&lt;span class="n"&gt;original_vector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt;

&lt;span class="c"&gt;# 1. Create a "slice" of the vector from the 2nd to the 4th element.&lt;/span&gt;
&lt;span class="c"&gt;#    In Julia, this operation creates a new Vector, copying the elements.&lt;/span&gt;
&lt;span class="n"&gt;sub_vector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;original_vector&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Original vector: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;original_vector&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Sub-vector (slice): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sub_vector&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Type of sub-vector: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sub_vector&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 2. Modify an element in the original vector.&lt;/span&gt;
&lt;span class="n"&gt;original_vector&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;999&lt;/span&gt;

&lt;span class="c"&gt;# 3. Observe the results. The sub-vector is unaffected because it's a separate copy.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Original vector after modification: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;original_vector&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Sub-vector remains unchanged: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sub_vector&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script demonstrates &lt;strong&gt;slicing&lt;/strong&gt;, a common operation for extracting a sub-section of an array. It also reveals a critical performance behavior in Julia.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Syntax&lt;/strong&gt;: Slicing is done using the range syntax &lt;code&gt;[start:end]&lt;/code&gt; inside the indexing brackets. &lt;code&gt;original_vector[2:4]&lt;/code&gt; creates a new sequence containing the elements from index 2 up to and including index 4.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Performance Note ❗
&lt;/h3&gt;

&lt;p&gt;This is a crucial concept for a systems programmer. By default, &lt;strong&gt;slicing an array in Julia creates a copy&lt;/strong&gt;, not a view or a reference.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;What it means&lt;/strong&gt;: The expression &lt;code&gt;original_vector[2:4]&lt;/code&gt; allocates new memory for a new &lt;code&gt;Vector&lt;/code&gt;, and then copies the values (&lt;code&gt;20&lt;/code&gt;, &lt;code&gt;30&lt;/code&gt;, &lt;code&gt;40&lt;/code&gt;) from the original vector into this new one. The variable &lt;code&gt;sub_vector&lt;/code&gt; points to this completely independent object.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Implications&lt;/strong&gt;: While safe, this behavior can be very &lt;strong&gt;inefficient&lt;/strong&gt; if you are working with large arrays or performing slicing inside a performance-critical loop. It leads to unnecessary memory allocations and data copying, which can hurt performance and increase pressure on the garbage collector.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The next lesson will introduce &lt;strong&gt;views&lt;/strong&gt;, which are Julia's high-performance, zero-copy solution to this problem.&lt;/p&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0030_vector_slicing.jl
Original vector: &lt;span class="o"&gt;[&lt;/span&gt;10, 20, 30, 40, 50]
Sub-vector &lt;span class="o"&gt;(&lt;/span&gt;slice&lt;span class="o"&gt;)&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;20, 30, 40]
Type of sub-vector: Vector&lt;span class="o"&gt;{&lt;/span&gt;Int64&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;--------------------&lt;/span&gt;
Original vector after modification: &lt;span class="o"&gt;[&lt;/span&gt;10, 999, 30, 40, 50]
Sub-vector remains unchanged: &lt;span class="o"&gt;[&lt;/span&gt;20, 30, 40]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;0031_vector_views.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0031_vector_views.jl&lt;/span&gt;

&lt;span class="n"&gt;original_vector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt;

&lt;span class="c"&gt;# 1. Create a "view" of the vector using the @view macro.&lt;/span&gt;
&lt;span class="c"&gt;#    This does NOT copy the data; it creates a lightweight object&lt;/span&gt;
&lt;span class="c"&gt;#    that refers to the original vector's memory.&lt;/span&gt;
&lt;span class="n"&gt;sub_view&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;@view&lt;/span&gt; &lt;span class="n"&gt;original_vector&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Original vector: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;original_vector&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Sub-view: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sub_view&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Type of sub-view: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sub_view&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 2. Modify an element in the original vector.&lt;/span&gt;
&lt;span class="n"&gt;original_vector&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;999&lt;/span&gt;

&lt;span class="c"&gt;# 3. Observe the results. The sub-view is AFFECTED because it shares&lt;/span&gt;
&lt;span class="c"&gt;#    the same underlying data as the original vector.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Original vector after modification: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;original_vector&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Sub-view now reflects the change: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sub_view&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces &lt;strong&gt;views&lt;/strong&gt;, Julia's high-performance, zero-copy solution for array slicing. This concept is the direct equivalent of &lt;code&gt;std::span&lt;/code&gt; in C++, slices (&lt;code&gt;&amp;amp;[T]&lt;/code&gt;) in Rust, or &lt;code&gt;memoryview&lt;/code&gt; in Python.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;@view&lt;/code&gt; Macro&lt;/strong&gt;: To create a view, you prefix the standard slicing operation with the &lt;code&gt;@view&lt;/code&gt; macro. Instead of allocating a new &lt;code&gt;Vector&lt;/code&gt;, this creates a &lt;code&gt;SubArray&lt;/code&gt; object.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;SubArray&lt;/code&gt;&lt;/strong&gt;: A &lt;code&gt;SubArray&lt;/code&gt; is a lightweight wrapper that stores a reference to the original array along with information about the selected indices. It does &lt;strong&gt;not&lt;/strong&gt; own its own data.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Performance and Behavior ❗
&lt;/h3&gt;

&lt;p&gt;This is the idiomatic way to handle slicing in performance-critical code.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero-Copy&lt;/strong&gt;: Creating a view is extremely fast because no data is copied. The operation is allocation-free, which reduces the workload on the garbage collector and avoids memory bandwidth costs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shared Memory&lt;/strong&gt;: As the example shows, since the view and the original vector share the same underlying data, any modification made through one is immediately visible in the other.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Rule of Thumb&lt;/strong&gt;: When you need to pass a slice of an array to a function, always use a view to prevent unnecessary copying. Slicing with &lt;code&gt;my_array[start:end]&lt;/code&gt; is for when you explicitly need an independent copy of the data.&lt;/p&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0031_vector_views.jl
Original vector: &lt;span class="o"&gt;[&lt;/span&gt;10, 20, 30, 40, 50]
Sub-view: &lt;span class="o"&gt;[&lt;/span&gt;20, 30, 40]
Type of sub-view: SubArray&lt;span class="o"&gt;{&lt;/span&gt;Int64, 1, Vector&lt;span class="o"&gt;{&lt;/span&gt;Int64&lt;span class="o"&gt;}&lt;/span&gt;, Tuple&lt;span class="o"&gt;{&lt;/span&gt;UnitRange&lt;span class="o"&gt;{&lt;/span&gt;Int64&lt;span class="o"&gt;}}&lt;/span&gt;, &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;--------------------&lt;/span&gt;
Original vector after modification: &lt;span class="o"&gt;[&lt;/span&gt;10, 999, 30, 40, 50]
Sub-view now reflects the change: &lt;span class="o"&gt;[&lt;/span&gt;999, 30, 40]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;0032_vector_comprehensions.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0032_vector_comprehensions.jl&lt;/span&gt;

&lt;span class="c"&gt;# 1. A comprehension provides a concise way to create a new vector.&lt;/span&gt;
&lt;span class="c"&gt;#    This creates a vector of the squares of numbers from 1 to 5.&lt;/span&gt;
&lt;span class="n"&gt;squares&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Vector of squares: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;squares&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Type: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;squares&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 2. You can add a filter condition with an 'if' clause.&lt;/span&gt;
&lt;span class="c"&gt;#    This creates a vector of only the even numbers from 1 to 10.&lt;/span&gt;
&lt;span class="n"&gt;evens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Vector of even numbers: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;evens&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 3. The comprehension above is a more readable and equally performant&lt;/span&gt;
&lt;span class="c"&gt;#    equivalent of writing the following manual loop:&lt;/span&gt;
&lt;span class="n"&gt;evens_loop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="x"&gt;[]&lt;/span&gt; &lt;span class="c"&gt;# Create an empty vector of Integers&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="n"&gt;push!&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;evens_loop&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Vector from manual loop: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;evens_loop&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces &lt;strong&gt;comprehensions&lt;/strong&gt;, a powerful and concise syntax for creating collections. This feature will be immediately familiar to you from Python's list comprehensions.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Syntax&lt;/strong&gt;: The basic structure is &lt;code&gt;[expression for variable in iterable]&lt;/code&gt;. For each element in the &lt;code&gt;iterable&lt;/code&gt;, the &lt;code&gt;expression&lt;/code&gt; is evaluated, and the results are collected into a new &lt;code&gt;Vector&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Filtering&lt;/strong&gt;: You can add a conditional clause &lt;code&gt;if condition&lt;/code&gt; at the end to filter which elements are processed. The &lt;code&gt;expression&lt;/code&gt; is only evaluated for elements where the &lt;code&gt;condition&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Readability &amp;amp; Performance&lt;/strong&gt;: Comprehensions are often more readable than writing out a full &lt;code&gt;for&lt;/code&gt; loop with &lt;code&gt;push!&lt;/code&gt;. They are also just as &lt;strong&gt;performant&lt;/strong&gt;. The Julia compiler is able to generate highly optimized code for comprehensions, often pre-calculating the size of the final vector and allocating it in a single step. This makes them the idiomatic choice for constructing a new vector based on an existing sequence.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0032_vector_comprehensions.jl
Vector of squares: &lt;span class="o"&gt;[&lt;/span&gt;1, 4, 9, 16, 25]
Type: Vector&lt;span class="o"&gt;{&lt;/span&gt;Int64&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;--------------------&lt;/span&gt;
Vector of even numbers: &lt;span class="o"&gt;[&lt;/span&gt;2, 4, 6, 8, 10]
&lt;span class="nt"&gt;--------------------&lt;/span&gt;
Vector from manual loop: &lt;span class="o"&gt;[&lt;/span&gt;2, 4, 6, 8, 10]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;0033_vector_of_any.md&lt;/code&gt;
&lt;/h3&gt;

&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This is one of the most critical performance concepts in Julia, especially for a systems programmer. Understanding the difference between a &lt;strong&gt;concrete&lt;/strong&gt; vector like &lt;code&gt;Vector{Int64}&lt;/code&gt; and an &lt;strong&gt;abstract&lt;/strong&gt; vector like &lt;code&gt;Vector{Any}&lt;/code&gt; is the key to avoiding massive, unexpected slowdowns.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Performance Pitfall of &lt;code&gt;Vector{Any}&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;When you create a vector with elements of different types, Julia creates a heterogeneous vector of type &lt;code&gt;Vector{Any}&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# This creates a Vector{Any}&lt;/span&gt;
&lt;span class="n"&gt;mixed_vector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"hello"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;3.0&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From a performance perspective, a &lt;code&gt;Vector{Any}&lt;/code&gt; is disastrous. You should think of it as a &lt;code&gt;Vector{void*}&lt;/code&gt; in C/C++.&lt;/p&gt;

&lt;h3&gt;
  
  
  Memory Layout Comparison
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;Vector{Int64}&lt;/code&gt; (Concrete &amp;amp; Fast)&lt;/strong&gt;: This is a single, contiguous block of memory containing 64-bit integers. It's cache-friendly, and accessing an element is a simple memory offset calculation. It's as fast as a C array or &lt;code&gt;std::vector&amp;lt;int64_t&amp;gt;&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;Vector{Any}&lt;/code&gt; (Abstract &amp;amp; Slow)&lt;/strong&gt;: This is a contiguous block of &lt;strong&gt;pointers&lt;/strong&gt;. Each element of the vector is not the value itself, but a pointer to a heap-allocated "box" that contains the value &lt;em&gt;and&lt;/em&gt; its type information.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why &lt;code&gt;Vector{Any}&lt;/code&gt; is Slow
&lt;/h3&gt;

&lt;p&gt;When you iterate over a &lt;code&gt;Vector{Any}&lt;/code&gt;, the following happens for &lt;em&gt;every single element&lt;/em&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Pointer Chasing&lt;/strong&gt;: The CPU must read the pointer from the vector.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Cache Miss&lt;/strong&gt;: It must then follow that pointer to a potentially random location in heap memory to find the boxed value. This frequently results in a CPU cache miss, which is a major performance penalty.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Dynamic Dispatch (Unboxing)&lt;/strong&gt;: Once the box is found, Julia must inspect its type tag at runtime to figure out what the value is (an &lt;code&gt;Int&lt;/code&gt;? a &lt;code&gt;String&lt;/code&gt;?). Only then can it perform the requested operation. This is called "dynamic dispatch," and it's orders of magnitude slower than a direct machine instruction (like adding two integers).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;In short, operating on a &lt;code&gt;Vector{Any}&lt;/code&gt; inside a loop prevents almost all of the compiler's optimizations.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rule of Thumb&lt;/strong&gt;: Always strive for type-stable, homogeneous collections (e.g., &lt;code&gt;Vector{Int64}&lt;/code&gt;, &lt;code&gt;Vector{String}&lt;/code&gt;). If you find yourself with a &lt;code&gt;Vector{Any}&lt;/code&gt;, it's a strong signal that there is a problem in your code design that needs to be fixed for performance.&lt;/p&gt;




&lt;h2&gt;
  
  
  Dict And Pair
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;0034_dict_basics.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0034_dict_basics.jl&lt;/span&gt;

&lt;span class="c"&gt;# 1. A Dictionary (Dict) is created with the Dict() constructor.&lt;/span&gt;
&lt;span class="c"&gt;#    The `key =&amp;gt; value` syntax creates a Pair object.&lt;/span&gt;
&lt;span class="n"&gt;http_codes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Dict&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;
    &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"OK"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;404&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"Not Found"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;500&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"Internal Server Error"&lt;/span&gt;
&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Dictionary value: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;http_codes&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Dictionary type: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http_codes&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 2. Access values using the key in square brackets.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Code 200 means: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;http_codes&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="x"&gt;])&lt;/span&gt;

&lt;span class="c"&gt;# 3. Add a new key-value pair or update an existing one.&lt;/span&gt;
&lt;span class="n"&gt;http_codes&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;302&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Found"&lt;/span&gt;       &lt;span class="c"&gt;# Add a new pair&lt;/span&gt;
&lt;span class="n"&gt;http_codes&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Server Error"&lt;/span&gt;  &lt;span class="c"&gt;# Update an existing value&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Updated dictionary: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;http_codes&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 4. Use `haskey()` to check if a key exists before accessing it.&lt;/span&gt;
&lt;span class="n"&gt;key_to_check&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;haskey&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http_codes&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key_to_check&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Key &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;key_to_check exists with value: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;http_codes&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key_to_check&lt;/span&gt;&lt;span class="x"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Key &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;key_to_check does not exist."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 5. Use `get()` for safe access with a default fallback value.&lt;/span&gt;
&lt;span class="c"&gt;#    This is often more concise than an if/else block.&lt;/span&gt;
&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http_codes&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;999&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Unknown Code"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Value for non-existent key 999: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces the &lt;strong&gt;&lt;code&gt;Dict&lt;/code&gt;&lt;/strong&gt;, Julia's primary hash map or associative array. It's the direct equivalent of &lt;code&gt;std::unordered_map&lt;/code&gt; in C++, &lt;code&gt;HashMap&lt;/code&gt; in Rust, or &lt;code&gt;dict&lt;/code&gt; in Python.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Creation&lt;/strong&gt;: A &lt;code&gt;Dict&lt;/code&gt; is created with the &lt;code&gt;Dict()&lt;/code&gt; constructor, which takes a collection of &lt;code&gt;Pair&lt;/code&gt; objects. The most common way to create these pairs is with the intuitive &lt;code&gt;key =&amp;gt; value&lt;/code&gt; syntax. Julia infers the types, so the example creates a &lt;code&gt;Dict{Int64, String}&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Access and Modification&lt;/strong&gt;: Like vectors, &lt;code&gt;Dict&lt;/code&gt;s are &lt;strong&gt;mutable&lt;/strong&gt;. You use square bracket syntax (&lt;code&gt;my_dict[key]&lt;/code&gt;) to both access and assign values. If the key already exists, the value is updated; otherwise, a new key-value pair is created.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Safe Access&lt;/strong&gt;: Accessing a non-existent key with &lt;code&gt;my_dict[key]&lt;/code&gt; will throw a &lt;code&gt;KeyError&lt;/code&gt;. To avoid this, you have two primary methods for safe access:&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1.  **`haskey(dict, key)`**: This function returns `true` or `false`, allowing you to check for a key's existence inside an `if` statement.
2.  **`get(dict, key, default)`**: This is often the preferred method. It attempts to retrieve the value for the key. If the key doesn't exist, it returns the `default` value you provide instead of throwing an error.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0034_dict_basics.jl
Dictionary value: Dict&lt;span class="o"&gt;(&lt;/span&gt;404 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Not Found"&lt;/span&gt;, 200 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"OK"&lt;/span&gt;, 500 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Internal Server Error"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
Dictionary &lt;span class="nb"&gt;type&lt;/span&gt;: Dict&lt;span class="o"&gt;{&lt;/span&gt;Int64, String&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;--------------------&lt;/span&gt;
Code 200 means: OK
Updated dictionary: Dict&lt;span class="o"&gt;(&lt;/span&gt;404 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Not Found"&lt;/span&gt;, 200 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"OK"&lt;/span&gt;, 500 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Server Error"&lt;/span&gt;, 302 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Found"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nt"&gt;--------------------&lt;/span&gt;
Key 404 exists with value: Not Found
Value &lt;span class="k"&gt;for &lt;/span&gt;non-existent key 999: Unknown Code
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;0035_dict_iteration.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0035_dict_iteration.jl&lt;/span&gt;

&lt;span class="n"&gt;http_codes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Dict&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;
    &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"OK"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;404&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"Not Found"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;301&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"Moved Permanently"&lt;/span&gt;
&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Iterating over keys ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# The `keys()` function returns an iterable collection of the dictionary's keys.&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http_codes&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Key: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Iterating over values ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# The `values()` function returns an iterable collection of the dictionary's values.&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http_codes&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Value: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Iterating over key-value pairs ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# Iterating directly over the dictionary yields key-value pairs.&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;http_codes&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Code &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;key means '&lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;value'"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script demonstrates the common ways to iterate over a &lt;code&gt;Dict&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;keys(dict)&lt;/code&gt;&lt;/strong&gt;: This function returns an efficient iterator over the keys of the dictionary. You can use this when you only need to work with the keys.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;values(dict)&lt;/code&gt;&lt;/strong&gt;: Similarly, this function provides an iterator for the dictionary's values.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Direct Iteration (Key-Value Pairs)&lt;/strong&gt;: The most common iteration pattern is to loop directly over the dictionary itself. When you do this, Julia yields a &lt;code&gt;Pair&lt;/code&gt; object (&lt;code&gt;key =&amp;gt; value&lt;/code&gt;) for each element. You can immediately destructure this pair into separate &lt;code&gt;key&lt;/code&gt; and &lt;code&gt;value&lt;/code&gt; variables, as shown in the line &lt;code&gt;for (key, value) in http_codes&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Important Note&lt;/strong&gt;: The order of iteration over a standard &lt;code&gt;Dict&lt;/code&gt; is not guaranteed. The elements will be returned based on the internal layout of the hash table, not the order in which they were inserted.&lt;/p&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0035_dict_iteration.jl
&lt;span class="nt"&gt;---&lt;/span&gt; Iterating over keys &lt;span class="nt"&gt;---&lt;/span&gt;
Key: 404
Key: 200
Key: 301

&lt;span class="nt"&gt;---&lt;/span&gt; Iterating over values &lt;span class="nt"&gt;---&lt;/span&gt;
Value: Not Found
Value: OK
Value: Moved Permanently

&lt;span class="nt"&gt;---&lt;/span&gt; Iterating over key-value pairs &lt;span class="nt"&gt;---&lt;/span&gt;
Code 404 means &lt;span class="s1"&gt;'Not Found'&lt;/span&gt;
Code 200 means &lt;span class="s1"&gt;'OK'&lt;/span&gt;
Code 301 means &lt;span class="s1"&gt;'Moved Permanently'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;0036_pairs.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0036_pairs.jl&lt;/span&gt;

&lt;span class="c"&gt;# 1. The `=&amp;gt;` syntax is a convenient way to create a `Pair` object.&lt;/span&gt;
&lt;span class="n"&gt;pair_obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"OK"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Value of the pair object: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pair_obj&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Type of the pair object: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pair_obj&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;

&lt;span class="c"&gt;# A Pair is a simple struct with 'first' and 'second' fields.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"First element: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pair_obj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Second element: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pair_obj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;second&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 2. A Dict is fundamentally a collection of these Pair objects.&lt;/span&gt;
&lt;span class="c"&gt;#    The following two definitions are completely equivalent.&lt;/span&gt;
&lt;span class="n"&gt;dict_syntax&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Dict&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"Not Found"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"Internal Server Error"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;pair1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Pair&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Not Found"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;pair2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Pair&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Internal Server Error"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;dict_constructor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Dict&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pair1&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pair2&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Dicts are equivalent: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dict_syntax&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;dict_constructor&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script clarifies the relationship between the &lt;code&gt;=&amp;gt;&lt;/code&gt; syntax, the &lt;code&gt;Pair&lt;/code&gt; object, and the &lt;code&gt;Dict&lt;/code&gt; data structure.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;Pair&lt;/code&gt; Object&lt;/strong&gt;: The &lt;code&gt;=&amp;gt;&lt;/code&gt; operator is just syntactic sugar for creating a &lt;code&gt;Pair&lt;/code&gt; object. A &lt;code&gt;Pair&lt;/code&gt; is a simple, immutable struct that holds two values, accessible via the fields &lt;code&gt;.first&lt;/code&gt; and &lt;code&gt;.second&lt;/code&gt;. &lt;code&gt;key =&amp;gt; value&lt;/code&gt; is equivalent to &lt;code&gt;Pair(key, value)&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;Dict&lt;/code&gt; and &lt;code&gt;Pair&lt;/code&gt;&lt;/strong&gt;: A dictionary is, at its core, a hash table that stores a collection of &lt;code&gt;Pair&lt;/code&gt; objects. When you write &lt;code&gt;Dict(key1 =&amp;gt; val1, key2 =&amp;gt; val2)&lt;/code&gt;, you are simply creating several &lt;code&gt;Pair&lt;/code&gt; objects and passing them to the &lt;code&gt;Dict&lt;/code&gt; constructor to be stored.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Understanding that &lt;code&gt;=&amp;gt;&lt;/code&gt; creates a &lt;code&gt;Pair&lt;/code&gt; helps demystify how dictionaries are constructed and how iteration works. When you iterate over a dictionary, as in &lt;code&gt;for (k, v) in my_dict&lt;/code&gt;, you are iterating over the &lt;code&gt;Pair&lt;/code&gt;s it contains, and Julia's destructuring assignment automatically unpacks each &lt;code&gt;Pair&lt;/code&gt; into the &lt;code&gt;k&lt;/code&gt; and &lt;code&gt;v&lt;/code&gt; variables.&lt;/p&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0036_pairs.jl
Value of the pair object: 200 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"OK"&lt;/span&gt;
Type of the pair object: Pair&lt;span class="o"&gt;{&lt;/span&gt;Int64, String&lt;span class="o"&gt;}&lt;/span&gt;
First element: 200
Second element: OK
&lt;span class="nt"&gt;--------------------&lt;/span&gt;
Dicts are equivalent: &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Symbol
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;0037_symbols.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0037_symbols.jl&lt;/span&gt;

&lt;span class="c"&gt;# --- Symbols (Guaranteed Interning &amp;amp; Fast Identity Check) ---&lt;/span&gt;
&lt;span class="n"&gt;sym1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;http_status&lt;/span&gt;
&lt;span class="n"&gt;sym2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;http_status&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Symbols ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Symbols are guaranteed to be interned (a single object in memory)."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"`sym1 === sym2` is `true` because it's a fast identity check: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sym1&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="n"&gt;sym2&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# --- Strings (Separate Objects &amp;amp; Slower Content Check) ---&lt;/span&gt;
&lt;span class="c"&gt;# This helper function ensures we create new, distinct string objects.&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; build_string&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;str1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;build_string&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"http"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"_"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"status"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;str2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;build_string&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"http"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"_"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"status"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Strings ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Dynamically created strings are separate objects in memory."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Memory address of str1: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pointer_from_objref&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;str1&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Memory address of str2: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pointer_from_objref&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;str2&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;

&lt;span class="c"&gt;# == checks for value equality by comparing content byte-by-byte.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"`str1 == str2` is `true` because contents are the same: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;str1&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;str2&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# For immutable types like String, === ALSO compares content byte-by-byte.&lt;/span&gt;
&lt;span class="c"&gt;# It returns `true` because they are bitwise identical, despite being different objects.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"`str1 === str2` is `true` because immutables are compared by content: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;str1&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="n"&gt;str2&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script demonstrates the critical performance distinction between &lt;code&gt;Symbol&lt;/code&gt;s and &lt;code&gt;String&lt;/code&gt;s, which stems from how they are stored and compared.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;Symbol&lt;/code&gt; (Identity Comparison)&lt;/strong&gt;: A &lt;code&gt;Symbol&lt;/code&gt; is &lt;strong&gt;interned&lt;/strong&gt;, meaning the language &lt;em&gt;guarantees&lt;/em&gt; that only one copy of &lt;code&gt;:http_status&lt;/code&gt; exists in memory. When you compare two symbols with &lt;code&gt;===&lt;/code&gt;, Julia performs a single, fast &lt;strong&gt;identity check&lt;/strong&gt;, which is as cheap as comparing two integer pointers. (NOTE: I am not really sure if this is how it works but seems sensible for an interned string)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;String&lt;/code&gt; (Content Comparison)&lt;/strong&gt;: A &lt;code&gt;String&lt;/code&gt; is an immutable, heap-allocated object. When you create strings at runtime, Julia allocates separate, distinct objects in memory. This is proven by the different memory addresses shown by &lt;code&gt;pointer_from_objref()&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;==&lt;/code&gt;&lt;/strong&gt;: Compares the strings' values, which involves a &lt;strong&gt;byte-by-byte comparison&lt;/strong&gt; of their content.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;===&lt;/code&gt;&lt;/strong&gt;: Because &lt;code&gt;String&lt;/code&gt; is an immutable type, &lt;code&gt;===&lt;/code&gt; also performs a &lt;strong&gt;byte-by-byte content comparison&lt;/strong&gt;. It returns &lt;code&gt;true&lt;/code&gt; because their contents are bitwise identical, even though they are different objects in memory.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Real Performance Takeaway
&lt;/h3&gt;

&lt;p&gt;The crucial difference is not &lt;em&gt;what&lt;/em&gt; &lt;code&gt;===&lt;/code&gt; returns, but &lt;em&gt;how&lt;/em&gt; the comparison is performed.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Symbol&lt;/code&gt; &lt;code&gt;===&lt;/code&gt; &lt;code&gt;Symbol&lt;/code&gt;&lt;/strong&gt;: A single, fast machine instruction (pointer comparison).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;String&lt;/code&gt; &lt;code&gt;===&lt;/code&gt; &lt;code&gt;String&lt;/code&gt;&lt;/strong&gt;: A potentially slow, full-content comparison (like &lt;code&gt;memcmp&lt;/code&gt; in C).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is why &lt;code&gt;Symbol&lt;/code&gt;s are vastly more performant as &lt;code&gt;Dict&lt;/code&gt; keys or in any scenario requiring frequent comparisons.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;0038_symbol_performance.md&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;(NOTE: I am really unsure if any of this is right)&lt;/p&gt;

&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;For performance, the distinction between a &lt;code&gt;Symbol&lt;/code&gt; and a &lt;code&gt;String&lt;/code&gt; is one of the most important in Julia. While both can represent text, their performance characteristics for comparisons are fundamentally different, which directly impacts their use as dictionary keys.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Performance Difference: Identity vs. Value
&lt;/h2&gt;

&lt;p&gt;A &lt;code&gt;Symbol&lt;/code&gt; is an &lt;strong&gt;interned string&lt;/strong&gt;. The language &lt;em&gt;guarantees&lt;/em&gt; that only one copy of a particular symbol exists in memory. This means comparing two symbols for equality is as fast as comparing two integers.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;String&lt;/code&gt; is a &lt;strong&gt;heap-allocated object&lt;/strong&gt;. When you create strings at runtime (e.g., by reading from a file), new, distinct objects are allocated.&lt;/p&gt;

&lt;p&gt;Let's analyze what happens during a comparison, which is a key step in a dictionary lookup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;sym1 === sym2&lt;/code&gt;&lt;/strong&gt;: This is an &lt;strong&gt;identity check&lt;/strong&gt;. Because &lt;code&gt;:http_status&lt;/code&gt; is guaranteed to be a single, unique object in memory, this comparison is a single, fast machine instruction—essentially a pointer comparison.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;str1 == str2&lt;/code&gt;&lt;/strong&gt;: This is a &lt;strong&gt;value check&lt;/strong&gt;. It must compare the content of the two string objects byte-by-byte to ensure they are the same. For long strings, this can be significantly slower than a simple pointer check.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why This Matters for &lt;code&gt;Dict&lt;/code&gt; Keys
&lt;/h3&gt;

&lt;p&gt;When you use an object as a key in a &lt;code&gt;Dict&lt;/code&gt;, Julia needs to find the correct value. This involves two main steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Hashing&lt;/strong&gt;: Calculating a hash value from the key to quickly find the right "bucket" in the hash table. Both &lt;code&gt;Symbol&lt;/code&gt; and &lt;code&gt;String&lt;/code&gt; have fast hash functions.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Equality Checking&lt;/strong&gt;: If multiple keys have the same hash (a "hash collision"), Julia must compare your key with the keys in the bucket to find the exact match.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This second step is where the performance difference becomes critical:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;With &lt;code&gt;Symbol&lt;/code&gt; keys&lt;/strong&gt;: The equality check is a lightning-fast &lt;code&gt;===&lt;/code&gt; identity check.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;With &lt;code&gt;String&lt;/code&gt; keys&lt;/strong&gt;: The equality check is a potentially slow, byte-by-byte &lt;code&gt;==&lt;/code&gt; value check.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Rule of Thumb&lt;/strong&gt;: When you need to use a text-based identifier as a key in a performance-sensitive &lt;code&gt;Dict&lt;/code&gt; or in any situation requiring many comparisons, &lt;strong&gt;always prefer &lt;code&gt;Symbol&lt;/code&gt; over &lt;code&gt;String&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  Module 4: Functions and Dispatch
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Defining Functions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;0039_function_basics.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0039_function_basics.jl&lt;/span&gt;

&lt;span class="c"&gt;# 1. Standard function definition using the 'function' keyword.&lt;/span&gt;
&lt;span class="c"&gt;#    The return type can be annotated, but often Julia's inference is sufficient.&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; add_numbers&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;
    &lt;span class="c"&gt;# The last evaluated expression in a function is implicitly returned.&lt;/span&gt;
    &lt;span class="c"&gt;# No explicit 'return' keyword is needed here.&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 2. Compact, single-line function definition.&lt;/span&gt;
&lt;span class="c"&gt;#    This is suitable for simple functions. It's just syntactic sugar.&lt;/span&gt;
&lt;span class="n"&gt;multiply_numbers&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;

&lt;span class="c"&gt;# Call the functions&lt;/span&gt;
&lt;span class="n"&gt;sum_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;add_numbers&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;product_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;multiply_numbers&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Result of add_numbers(5, 3): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sum_result&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Result of multiply_numbers(5, 3): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;product_result&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Demonstrate implicit return with a slightly more complex example&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; check_positive&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="s"&gt;"Positive"&lt;/span&gt; &lt;span class="c"&gt;# Implicit return if n &amp;gt; 0&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="s"&gt;"Non-positive"&lt;/span&gt; &lt;span class="c"&gt;# Implicit return otherwise&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Check positive for 10: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;check_positive&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Check positive for -2: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;check_positive&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces the two main ways to define functions in Julia and highlights the concept of implicit return.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Standard Syntax (&lt;code&gt;function ... end&lt;/code&gt;)&lt;/strong&gt;: This is the block syntax used for longer or more complex functions.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;function add_numbers(x::Int, y::Int)&lt;/code&gt;: Defines a function named &lt;code&gt;add_numbers&lt;/code&gt; that takes two arguments, &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt;. The &lt;code&gt;::Int&lt;/code&gt; are &lt;strong&gt;type annotations&lt;/strong&gt;, which we'll cover next. They tell the compiler what type these arguments are expected to be.&lt;/li&gt;
&lt;li&gt;The code within the &lt;code&gt;function&lt;/code&gt; and &lt;code&gt;end&lt;/code&gt; keywords is the function body.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Compact Syntax (&lt;code&gt;f(x) = ...&lt;/code&gt;)&lt;/strong&gt;: For simple, single-expression functions, Julia offers a concise assignment form: &lt;code&gt;multiply_numbers(x, y) = x * y&lt;/code&gt;. This defines a function named &lt;code&gt;multiply_numbers&lt;/code&gt; that takes two arguments and immediately returns the result of &lt;code&gt;x * y&lt;/code&gt;. This is purely syntactic sugar for the standard form.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Implicit Return&lt;/strong&gt;: A defining feature of Julia is that the value of the &lt;strong&gt;last evaluated expression&lt;/strong&gt; in a function's body is automatically returned. You do not need to use the &lt;code&gt;return&lt;/code&gt; keyword unless you want to return early from the middle of a function.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In &lt;code&gt;add_numbers&lt;/code&gt;, the last expression is &lt;code&gt;result&lt;/code&gt;, so its value is returned.&lt;/li&gt;
&lt;li&gt;In &lt;code&gt;check_positive&lt;/code&gt;, the last expression evaluated is either &lt;code&gt;"Positive"&lt;/code&gt; or &lt;code&gt;"Non-positive"&lt;/code&gt;, depending on the &lt;code&gt;if&lt;/code&gt; condition, and that string is returned.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0039_function_basics.jl
Result of add_numbers&lt;span class="o"&gt;(&lt;/span&gt;5, 3&lt;span class="o"&gt;)&lt;/span&gt;: 8
Result of multiply_numbers&lt;span class="o"&gt;(&lt;/span&gt;5, 3&lt;span class="o"&gt;)&lt;/span&gt;: 15
Check positive &lt;span class="k"&gt;for &lt;/span&gt;10: Positive
Check positive &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nt"&gt;-2&lt;/span&gt;: Non-positive
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;0040_type_annotations.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0040_type_annotations.jl&lt;/span&gt;

&lt;span class="c"&gt;# 1. Function without type annotations.&lt;/span&gt;
&lt;span class="c"&gt;#    Julia will compile specialized versions based on the types it sees at runtime.&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; process_unannotated&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# This might be fast if `data` is always the same type,&lt;/span&gt;
    &lt;span class="c"&gt;# but the compiler has less information upfront.&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Processing data of type: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="c"&gt;# Return the data unmodified&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 2. Function WITH type annotations for arguments.&lt;/span&gt;
&lt;span class="c"&gt;#    This tells the compiler (and the programmer) that `x` MUST be an Int.&lt;/span&gt;
&lt;span class="c"&gt;#    It enables method dispatch and performance optimizations.&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; calculate_area&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 3. Function WITH annotations for arguments AND return type.&lt;/span&gt;
&lt;span class="c"&gt;#    The `::Int` after the argument list guarantees the function will return an Int.&lt;/span&gt;
&lt;span class="c"&gt;#    If it tries to return something else, an error occurs.&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; get_int_length&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;
    &lt;span class="n"&gt;len&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# If we tried to return a float here, like `len + 0.5`, it would error.&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;


&lt;span class="c"&gt;# Call the functions&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Unannotated ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;process_unannotated&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;process_unannotated&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hello"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Annotated Arguments ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;area&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;calculate_area&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Calculated area: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;area&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# Calling with wrong types will cause a MethodError immediately&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="n"&gt;calculate_area&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;5.0&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error calling with wrong type: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Annotated Return Type ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;str_len&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_int_length&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Julia"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Length of 'Julia': "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;str_len&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Return type is indeed Int: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;str_len&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script demonstrates &lt;strong&gt;type annotations&lt;/strong&gt; in Julia functions, which are crucial for both correctness and performance. 📝&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Syntax&lt;/strong&gt;: Annotations are added using the double colon &lt;code&gt;::&lt;/code&gt; operator.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;function func(arg::Type)&lt;/code&gt;: Annotates the type of an argument.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;function func(arg)::Type&lt;/code&gt;: Annotates the expected return type of the function.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Purpose&lt;/strong&gt;:&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1.  **Method Dispatch**: Annotations allow you to define different **methods** of the same function for different argument types (this is the core of multiple dispatch, coming next). When you call `calculate_area(5, 4)`, Julia knows *exactly* which version of the function to run because the types match the annotation `(width::Int, height::Int)`.
2.  **Performance**: When the compiler knows the types of the arguments and the expected return type, it can generate highly specialized and optimized machine code. It eliminates the need for runtime type checks within the function body. Functions with fully annotated arguments and return types are much more likely to be **type-stable** and fast.
3.  **Correctness &amp;amp; Readability**: Annotations act as documentation and assertions. They make the function's contract clear. If you call a function with the wrong type, you get an immediate `MethodError` instead of a potentially obscure error later on. If a function annotated to return `::Int` accidentally returns a `Float64`, Julia will throw a `TypeError`.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Omitting Annotations&lt;/strong&gt;: You &lt;em&gt;can&lt;/em&gt; omit annotations (like in &lt;code&gt;process_unannotated&lt;/code&gt;). Julia will still compile specialized versions based on the types it observes when the function is first called. However, adding annotations provides stronger guarantees to the compiler and makes the code easier to understand and debug.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0040_type_annotations.jl
&lt;span class="nt"&gt;---&lt;/span&gt; Unannotated &lt;span class="nt"&gt;---&lt;/span&gt;
Processing data of &lt;span class="nb"&gt;type&lt;/span&gt;: Int64
Processing data of &lt;span class="nb"&gt;type&lt;/span&gt;: String

&lt;span class="nt"&gt;---&lt;/span&gt; Annotated Arguments &lt;span class="nt"&gt;---&lt;/span&gt;
Calculated area: 20
Error calling with wrong &lt;span class="nb"&gt;type&lt;/span&gt;: MethodError&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;f&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;calculate_area, &lt;span class="nv"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;5.0, 4&lt;span class="o"&gt;))&lt;/span&gt;

&lt;span class="nt"&gt;---&lt;/span&gt; Annotated Return Type &lt;span class="nt"&gt;---&lt;/span&gt;
Length of &lt;span class="s1"&gt;'Julia'&lt;/span&gt;: 5
Return &lt;span class="nb"&gt;type &lt;/span&gt;is indeed Int: Int64
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Multiple Dispatch
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;0041_multiple_dispatch_basics.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0041_multiple_dispatch_basics.jl&lt;/span&gt;

&lt;span class="c"&gt;# 1. Define a function name 'process'.&lt;/span&gt;
&lt;span class="c"&gt;#    We will define several *methods* for this function name.&lt;/span&gt;

&lt;span class="c"&gt;# Method 1: Specific for Int arguments.&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; process&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Processing an Integer: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# Method 2: Specific for String arguments.&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; process&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Processing a String: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;uppercase&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# Method 3: A generic fallback for any other type (Any).&lt;/span&gt;
&lt;span class="c"&gt;# 'Any' is the top-level abstract type in Julia.&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; process&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Any&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Processing data of generic type '"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="x"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"': "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 2. Call the function with different argument types.&lt;/span&gt;
&lt;span class="c"&gt;#    Julia automatically selects the MOST specific method available at runtime.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Calling process() with different types ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;          &lt;span class="c"&gt;# Calls Method 1&lt;/span&gt;
&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hello"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;     &lt;span class="c"&gt;# Calls Method 2&lt;/span&gt;
&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;3.14&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;        &lt;span class="c"&gt;# Calls Method 3 (Float64 is a subtype of Any)&lt;/span&gt;
&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="x"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="x"&gt;])&lt;/span&gt;   &lt;span class="c"&gt;# Calls Method 3 (Vector{Int64} is a subtype of Any)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces &lt;strong&gt;multiple dispatch&lt;/strong&gt;, the central organizing principle of Julia 🏛️. It's Julia's answer to function overloading (like in C++) and method overriding (like in Python/Java), but it's more general and powerful.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Functions vs. Methods&lt;/strong&gt;: In Julia, you define a &lt;strong&gt;function&lt;/strong&gt; by its name (e.g., &lt;code&gt;process&lt;/code&gt;). You then define one or more &lt;strong&gt;methods&lt;/strong&gt; for that function, where each method specifies the &lt;em&gt;types&lt;/em&gt; of arguments it accepts using type annotations (e.g., &lt;code&gt;process(data::Int)&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Dispatch&lt;/strong&gt;: When you call a function like &lt;code&gt;process(10)&lt;/code&gt;, Julia looks at the &lt;strong&gt;runtime types&lt;/strong&gt; of all the arguments you provided. It then selects and executes the &lt;strong&gt;most specific method&lt;/strong&gt; whose type signature matches those arguments.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;process(10)&lt;/code&gt; matches &lt;code&gt;process(data::Int)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;process("hello")&lt;/code&gt; matches &lt;code&gt;process(data::String)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;process(3.14)&lt;/code&gt; doesn't match &lt;code&gt;Int&lt;/code&gt; or &lt;code&gt;String&lt;/code&gt;, so it falls back to the least specific method that matches, which is &lt;code&gt;process(data::Any)&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Why it's "Multiple"&lt;/strong&gt;: Unlike object-oriented languages where dispatch usually happens only on the first argument (&lt;code&gt;object.method()&lt;/code&gt;), Julia considers the types of &lt;strong&gt;all&lt;/strong&gt; arguments when selecting the method. This is why it's called &lt;em&gt;multiple&lt;/em&gt; dispatch.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Performance&lt;/strong&gt;: Multiple dispatch is not just elegant; it's also &lt;strong&gt;fast&lt;/strong&gt;. Because the method selection happens based on concrete types, the Julia JIT compiler can generate highly optimized, direct calls to the specific machine code for that method, completely avoiding the overhead of dynamic lookups often associated with traditional object-oriented method calls.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Multiple dispatch encourages writing small, reusable functions that operate on different data types, leading to highly composable and performant code.&lt;/p&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0041_multiple_dispatch_basics.jl
&lt;span class="nt"&gt;---&lt;/span&gt; Calling process&lt;span class="o"&gt;()&lt;/span&gt; with different types &lt;span class="nt"&gt;---&lt;/span&gt;
Processing an Integer: 20
Processing a String: HELLO
Processing data of generic &lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="s1"&gt;'Float64'&lt;/span&gt;: 3.14
Processing data of generic &lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="s1"&gt;'Vector{Int64}'&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;1, 2, 3]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;0042_parametric_methods.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0042_parametric_methods.jl&lt;/span&gt;

&lt;span class="c"&gt;# 1. A generic method for any Vector.&lt;/span&gt;
&lt;span class="c"&gt;#    `Vector{T}` means "a Vector where the element type is some T".&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; get_first_element&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Vector&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="x"&gt;})&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="x"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Generic method called for Vector of type: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;isempty&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;nothing&lt;/span&gt; &lt;span class="c"&gt;# Or throw an error, depending on desired behavior&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 2. A more specific method JUST for Vectors containing Strings.&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; get_first_element&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Vector&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="x"&gt;})&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Specific method called for Vector{String}"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;isempty&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;nothing&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="c"&gt;# We can call string-specific functions here because we know the type&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;uppercase&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 3. Call the function with different vector types.&lt;/span&gt;
&lt;span class="n"&gt;int_vector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;string_vector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"apple"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"banana"&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;float_vector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;1.1&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;2.2&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;empty_vector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="x"&gt;[]&lt;/span&gt; &lt;span class="c"&gt;# An empty Vector{Int}&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Calling get_first_element() ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;first_int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_first_element&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;int_vector&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;       &lt;span class="c"&gt;# Calls Method 1 (T=Int64)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"First int: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;first_int&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;first_string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_first_element&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;string_vector&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Calls Method 2 (Specific match)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"First string (uppercase): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;first_string&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;first_float&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_first_element&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;float_vector&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;   &lt;span class="c"&gt;# Calls Method 1 (T=Float64)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"First float: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;first_float&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;first_empty&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_first_element&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;empty_vector&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;   &lt;span class="c"&gt;# Calls Method 1 (T=Int64)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"First empty: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;first_empty&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script demonstrates how &lt;strong&gt;multiple dispatch&lt;/strong&gt; works with &lt;strong&gt;parametric types&lt;/strong&gt; (generics). 🧬&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Parametric Types&lt;/strong&gt;: A type like &lt;code&gt;Vector{T}&lt;/code&gt; is parametric. It represents a &lt;code&gt;Vector&lt;/code&gt; that can hold elements of &lt;em&gt;any&lt;/em&gt; type, represented by the type parameter &lt;code&gt;T&lt;/code&gt;. When you have &lt;code&gt;[10, 20]&lt;/code&gt;, its type is &lt;code&gt;Vector{Int64}&lt;/code&gt;, where &lt;code&gt;T&lt;/code&gt; is &lt;code&gt;Int64&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Generic Method (&lt;code&gt;where {T}&lt;/code&gt; Syntax)&lt;/strong&gt;: The first method, &lt;code&gt;get_first_element(arr::Vector{T}) where {T}&lt;/code&gt;, defines a generic fallback.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;arr::Vector{T}&lt;/code&gt; means the argument &lt;code&gt;arr&lt;/code&gt; must be a &lt;code&gt;Vector&lt;/code&gt; containing elements of some type &lt;code&gt;T&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;where {T}&lt;/code&gt; introduces the type parameter &lt;code&gt;T&lt;/code&gt;. This allows the compiler to know about &lt;code&gt;T&lt;/code&gt; within the function body and potentially use it (though this simple example doesn't need to).&lt;/li&gt;
&lt;li&gt;This method will be called for any &lt;code&gt;Vector&lt;/code&gt; &lt;em&gt;unless&lt;/em&gt; a more specific method exists.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Specific Method&lt;/strong&gt;: The second method, &lt;code&gt;get_first_element(arr::Vector{String})&lt;/code&gt;, is highly specific. It explicitly states it only works for a &lt;code&gt;Vector&lt;/code&gt; where the element type is exactly &lt;code&gt;String&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Dispatch Rules&lt;/strong&gt;: When you call &lt;code&gt;get_first_element&lt;/code&gt;, Julia again picks the &lt;strong&gt;most specific&lt;/strong&gt; method that matches the argument types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;get_first_element([10, 20])&lt;/code&gt; (a &lt;code&gt;Vector{Int64}&lt;/code&gt;) doesn't match &lt;code&gt;Vector{String}&lt;/code&gt;, so it falls back to the generic &lt;code&gt;Vector{T}&lt;/code&gt; method, with &lt;code&gt;T&lt;/code&gt; becoming &lt;code&gt;Int64&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;get_first_element(["apple", "banana"])&lt;/code&gt; (a &lt;code&gt;Vector{String}&lt;/code&gt;) perfectly matches the specific &lt;code&gt;Vector{String}&lt;/code&gt; method, so that one is chosen.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;get_first_element([1.1, 2.2])&lt;/code&gt; (a &lt;code&gt;Vector{Float64}&lt;/code&gt;) falls back to the generic &lt;code&gt;Vector{T}&lt;/code&gt; method, with &lt;code&gt;T&lt;/code&gt; becoming &lt;code&gt;Float64&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;This ability to dispatch based on the &lt;strong&gt;parameter&lt;/strong&gt; of a generic type is a powerful feature of Julia, allowing you to write general algorithms and then provide highly optimized or specialized versions for specific contained types.&lt;/p&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0042_parametric_methods.jl
&lt;span class="nt"&gt;---&lt;/span&gt; Calling get_first_element&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
Generic method called &lt;span class="k"&gt;for &lt;/span&gt;Vector of &lt;span class="nb"&gt;type&lt;/span&gt;: Int64
First int: 10
&lt;span class="nt"&gt;--------------------&lt;/span&gt;
Specific method called &lt;span class="k"&gt;for &lt;/span&gt;Vector&lt;span class="o"&gt;{&lt;/span&gt;String&lt;span class="o"&gt;}&lt;/span&gt;
First string &lt;span class="o"&gt;(&lt;/span&gt;uppercase&lt;span class="o"&gt;)&lt;/span&gt;: APPLE
&lt;span class="nt"&gt;--------------------&lt;/span&gt;
Generic method called &lt;span class="k"&gt;for &lt;/span&gt;Vector of &lt;span class="nb"&gt;type&lt;/span&gt;: Float64
First float: 1.1
&lt;span class="nt"&gt;--------------------&lt;/span&gt;
Generic method called &lt;span class="k"&gt;for &lt;/span&gt;Vector of &lt;span class="nb"&gt;type&lt;/span&gt;: Int64
First empty: nothing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Function Arguments
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;0043_keyword_arguments.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0043_keyword_arguments.jl&lt;/span&gt;

&lt;span class="c"&gt;# 1. Define a function with keyword arguments after a semicolon.&lt;/span&gt;
&lt;span class="c"&gt;#    Keyword arguments must have default values.&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; create_greeting&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="x"&gt;;&lt;/span&gt; &lt;span class="n"&gt;greeting&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Hello"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;punctuation&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"!"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;greeting, &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;punctuation"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 2. Call the function using only positional arguments.&lt;/span&gt;
&lt;span class="c"&gt;#    Keyword arguments will use their default values.&lt;/span&gt;
&lt;span class="n"&gt;default_greeting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;create_greeting&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Julia"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Default greeting: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default_greeting&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 3. Call the function, overriding some keyword arguments by name.&lt;/span&gt;
&lt;span class="c"&gt;#    The order of keyword arguments does not matter.&lt;/span&gt;
&lt;span class="n"&gt;custom_greeting1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;create_greeting&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"World"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;greeting&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Hi"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Custom greeting 1: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;custom_greeting1&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;custom_greeting2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;create_greeting&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Developers"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;punctuation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"!!!"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;greeting&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Welcome"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Custom greeting 2: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;custom_greeting2&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 4. Mixing positional and keyword arguments.&lt;/span&gt;
&lt;span class="c"&gt;#    Positional arguments must always come before keyword arguments.&lt;/span&gt;
&lt;span class="c"&gt;#    This syntax is clear: create_greeting("Positional"); kw1=val1, kw2=val2...&lt;/span&gt;
&lt;span class="n"&gt;formal_greeting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;create_greeting&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Dr. Turing"&lt;/span&gt;&lt;span class="x"&gt;;&lt;/span&gt; &lt;span class="n"&gt;greeting&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Good day"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Formal greeting: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;formal_greeting&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces &lt;strong&gt;keyword arguments&lt;/strong&gt;, which allow you to pass arguments to a function by name, making the call site more readable and allowing for optional parameters with default values. 🏷️&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Syntax&lt;/strong&gt;: Keyword arguments are defined in the function signature &lt;strong&gt;after&lt;/strong&gt; a semicolon (&lt;code&gt;;&lt;/code&gt;). Each keyword argument must be given a &lt;strong&gt;default value&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; func&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;positional_arg&lt;/span&gt;&lt;span class="x"&gt;;&lt;/span&gt; &lt;span class="n"&gt;keyword_arg1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;default1&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;keyword_arg2&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;default2&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# ...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Calling&lt;/strong&gt;: When calling a function with keyword arguments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can omit them entirely, in which case their default values are used (&lt;code&gt;create_greeting("Julia")&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;You can provide values for specific keywords using the &lt;code&gt;keyword=value&lt;/code&gt; syntax (&lt;code&gt;greeting="Hi"&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;The order in which you provide keyword arguments does not matter (&lt;code&gt;punctuation="!!!", greeting="Welcome"&lt;/code&gt; works).&lt;/li&gt;
&lt;li&gt;All &lt;strong&gt;positional arguments&lt;/strong&gt; (if any) must come &lt;strong&gt;before&lt;/strong&gt; any keyword arguments.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Use Cases&lt;/strong&gt;: Keyword arguments are excellent for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Functions with many arguments where specifying them by name improves clarity.&lt;/li&gt;
&lt;li&gt;Optional configuration parameters.&lt;/li&gt;
&lt;li&gt;Providing a more stable API (adding new keyword arguments doesn't break existing calls that don't use them).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;This feature is very similar to keyword arguments in Python.&lt;/p&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0043_keyword_arguments.jl
Default greeting: Hello, Julia!
Custom greeting 1: Hi, World!
Custom greeting 2: Welcome, Developers!!!
Formal greeting: Good day, Dr. Turing!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;0044_splatting_operator.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0044_splatting_operator.jl&lt;/span&gt;

&lt;span class="c"&gt;# 1. A function that takes a variable number of arguments.&lt;/span&gt;
&lt;span class="c"&gt;#    `numbers...` collects all remaining arguments into a tuple named 'numbers'.&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; sum_all&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;
        &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;": "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 2. Call the function with individual arguments.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Calling with individual arguments ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;sum_all&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Individual args"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Calling with splatting ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 3. Use the splatting operator '...' to pass elements from a collection&lt;/span&gt;
&lt;span class="c"&gt;#    as individual arguments.&lt;/span&gt;
&lt;span class="n"&gt;my_numbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt;
&lt;span class="c"&gt;# This is equivalent to calling sum_all("Splatting", 10, 20, 30)&lt;/span&gt;
&lt;span class="n"&gt;sum_all&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Splatting"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;my_numbers&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# It also works with tuples&lt;/span&gt;
&lt;span class="n"&gt;my_tuple&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;sum_all&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Splatting tuple"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;my_tuple&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces the &lt;strong&gt;splatting operator (&lt;code&gt;...&lt;/code&gt;)&lt;/strong&gt;, which unpacks the elements of a collection into individual arguments for a function call. This is a powerful feature for working with functions that accept a variable number of arguments (varargs). ☄️&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Varargs Functions (&lt;code&gt;numbers...&lt;/code&gt;)&lt;/strong&gt;: In the function definition &lt;code&gt;sum_all(label::String, numbers...)&lt;/code&gt;, the &lt;code&gt;...&lt;/code&gt; after &lt;code&gt;numbers&lt;/code&gt; indicates that this parameter will collect any number of subsequent positional arguments into a single &lt;strong&gt;tuple&lt;/strong&gt; named &lt;code&gt;numbers&lt;/code&gt;. This is similar to &lt;code&gt;*args&lt;/code&gt; in Python or variadic templates in C++.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Splatting Operator (&lt;code&gt;...&lt;/code&gt;)&lt;/strong&gt;: When &lt;em&gt;calling&lt;/em&gt; a function, placing &lt;code&gt;...&lt;/code&gt; after a collection (like a &lt;code&gt;Vector&lt;/code&gt; or &lt;code&gt;Tuple&lt;/code&gt;) &lt;strong&gt;unpacks&lt;/strong&gt; its elements and passes them as separate positional arguments.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;sum_all("Splatting", my_numbers...)&lt;/code&gt; takes the elements &lt;code&gt;10, 20, 30&lt;/code&gt; from &lt;code&gt;my_numbers&lt;/code&gt; and effectively calls &lt;code&gt;sum_all("Splatting", 10, 20, 30)&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Use Cases&lt;/strong&gt;: Splatting is commonly used when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You have a list or tuple of values that you need to pass to a function designed to accept them individually (like &lt;code&gt;sum_all&lt;/code&gt; or functions like &lt;code&gt;max()&lt;/code&gt;, &lt;code&gt;min()&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;You are forwarding arguments from one varargs function to another.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0044_splatting_operator.jl
&lt;span class="nt"&gt;---&lt;/span&gt; Calling with individual arguments &lt;span class="nt"&gt;---&lt;/span&gt;
Individual args: 10

&lt;span class="nt"&gt;---&lt;/span&gt; Calling with splatting &lt;span class="nt"&gt;---&lt;/span&gt;
Splatting: 60
Splatting tuple: 300
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Mutating Vs Non Mutating
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;0045_mutating_functions_convention.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0045_mutating_functions_convention.jl&lt;/span&gt;

&lt;span class="c"&gt;# A mutable struct to hold some data&lt;/span&gt;
&lt;span class="k"&gt;mutable struct&lt;/span&gt;&lt;span class="nc"&gt; Point&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;
    &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 1. Non-mutating function: Creates and returns a NEW Point.&lt;/span&gt;
&lt;span class="c"&gt;#    Does not end with '!'&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; move_point&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Point&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dx&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dy&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# Create a new Point object with the modified coordinates&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Point&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;dx&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;dy&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 2. Mutating function: Modifies the original Point object IN-PLACE.&lt;/span&gt;
&lt;span class="c"&gt;#    Ends with '!' by convention.&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; move_point!&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Point&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dx&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dy&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;dx&lt;/span&gt;
    &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;dy&lt;/span&gt;
    &lt;span class="c"&gt;# Typically returns the modified object, or nothing&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;


&lt;span class="c"&gt;# Create an initial point&lt;/span&gt;
&lt;span class="n"&gt;p1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Point&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;10.0&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;20.0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Original point p1: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Calling non-mutating function ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# Call the non-mutating version&lt;/span&gt;
&lt;span class="n"&gt;p2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;move_point&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;5.0&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;5.0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Returned new point p2: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Original p1 remains unchanged: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Calling mutating function ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# Call the mutating version on p1&lt;/span&gt;
&lt;span class="n"&gt;move_point!&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;100.0&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;100.0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Original p1 IS NOW modified: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script explains the crucial Julia &lt;strong&gt;naming convention&lt;/strong&gt; for functions that modify their arguments: appending an exclamation mark (&lt;code&gt;!&lt;/code&gt;).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The &lt;code&gt;!&lt;/code&gt; Convention&lt;/strong&gt;: If a function modifies the state of one or more of its input arguments (especially mutable collections like &lt;code&gt;Vector&lt;/code&gt;s or &lt;code&gt;mutable struct&lt;/code&gt;s), its name &lt;strong&gt;should&lt;/strong&gt; end with &lt;code&gt;!&lt;/code&gt;. This acts as a clear warning sign to the caller that the function has side effects and will change the input object.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Non-Mutating (&lt;code&gt;move_point&lt;/code&gt;)&lt;/strong&gt;: This function takes a &lt;code&gt;Point&lt;/code&gt; and returns a &lt;strong&gt;new&lt;/strong&gt; &lt;code&gt;Point&lt;/code&gt; object with the updated coordinates. The original &lt;code&gt;p1&lt;/code&gt; is completely untouched. This is often safer as it avoids unexpected side effects.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Mutating (&lt;code&gt;move_point!&lt;/code&gt;)&lt;/strong&gt;: This function directly modifies the fields (&lt;code&gt;p.x&lt;/code&gt;, &lt;code&gt;p.y&lt;/code&gt;) of the &lt;code&gt;Point&lt;/code&gt; object passed into it. The original &lt;code&gt;p1&lt;/code&gt; is altered.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Why It Matters&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Clarity&lt;/strong&gt;: The &lt;code&gt;!&lt;/code&gt; immediately tells you if a function might change your data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance&lt;/strong&gt;: Mutating functions (&lt;code&gt;!&lt;/code&gt;) can often be more performant, especially when working with large data structures. Modifying data in-place avoids allocating new memory for a result, which reduces work for the garbage collector. However, this comes at the cost of potential side effects if the original object is used elsewhere.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Not Enforced&lt;/strong&gt;: It's important to remember this is a &lt;strong&gt;convention&lt;/strong&gt;, not a rule enforced by the compiler. You &lt;em&gt;can&lt;/em&gt; write a function that modifies its arguments without a &lt;code&gt;!&lt;/code&gt;, but it's strongly discouraged as it violates user expectations. Conversely, a function ending in &lt;code&gt;!&lt;/code&gt; &lt;em&gt;should&lt;/em&gt; modify at least one argument. Standard library functions strictly adhere to this convention (e.g., &lt;code&gt;sort&lt;/code&gt; returns a sorted copy, &lt;code&gt;sort!&lt;/code&gt; sorts the input vector in-place).&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0045_mutating_functions_convention.jl
Original point p1: Point&lt;span class="o"&gt;(&lt;/span&gt;10.0, 20.0&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nt"&gt;---&lt;/span&gt; Calling non-mutating &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
Returned new point p2: Point&lt;span class="o"&gt;(&lt;/span&gt;15.0, 15.0&lt;span class="o"&gt;)&lt;/span&gt;
Original p1 remains unchanged: Point&lt;span class="o"&gt;(&lt;/span&gt;10.0, 20.0&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nt"&gt;---&lt;/span&gt; Calling mutating &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
Original p1 IS NOW modified: Point&lt;span class="o"&gt;(&lt;/span&gt;110.0, 120.0&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Higher Order And Do
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;0046_anonymous_functions.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0046_anonymous_functions.jl&lt;/span&gt;

&lt;span class="c"&gt;# 1. Standard function for mapping (e.g., doubling numbers)&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; double&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="n"&gt;numbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;doubled_numbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;double&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Doubled with standard function: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;doubled_numbers&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 2. Using an anonymous function directly within the map call.&lt;/span&gt;
&lt;span class="c"&gt;#    The syntax `x -&amp;gt; x * 2` creates a function without a name.&lt;/span&gt;
&lt;span class="n"&gt;doubled_anon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Doubled with anonymous function: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;doubled_anon&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 3. Anonymous functions can take multiple arguments.&lt;/span&gt;
&lt;span class="c"&gt;#    Here, we use `map` to add elements from two lists.&lt;/span&gt;
&lt;span class="n"&gt;list1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;list2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;sums&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="x"&gt;((&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;list1&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;list2&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Sums using multi-arg anonymous function: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sums&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 4. Anonymous functions implicitly capture variables from their surrounding scope.&lt;/span&gt;
&lt;span class="n"&gt;multiplier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="n"&gt;multiplied_capture&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;multiplier&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Using captured variable 'multiplier': "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;multiplied_capture&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces &lt;strong&gt;anonymous functions&lt;/strong&gt;, also known as &lt;strong&gt;lambda functions&lt;/strong&gt;. These are functions defined without being given a specific name. They are essential for functional programming patterns and are frequently used as arguments to higher-order functions like &lt;code&gt;map&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Syntax (&lt;code&gt;-&amp;gt;&lt;/code&gt;)&lt;/strong&gt;: The core syntax for creating an anonymous function is &lt;code&gt;arguments -&amp;gt; expression&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;x -&amp;gt; x * 2&lt;/code&gt;: Defines a function that takes one argument &lt;code&gt;x&lt;/code&gt; and returns &lt;code&gt;x * 2&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;(a, b) -&amp;gt; a + b&lt;/code&gt;: Defines a function that takes two arguments &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt; and returns their sum.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;map()&lt;/code&gt; Function&lt;/strong&gt;: The &lt;code&gt;map(function, collection)&lt;/code&gt; function is a standard higher-order function. It applies the given &lt;code&gt;function&lt;/code&gt; to each element of the &lt;code&gt;collection&lt;/code&gt; and returns a new collection containing the results.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use Case&lt;/strong&gt;: Anonymous functions are ideal when you need a simple function just once, typically as an argument to another function. Instead of defining a separate named function (like &lt;code&gt;double&lt;/code&gt;), you can define the operation inline with &lt;code&gt;x -&amp;gt; x * 2&lt;/code&gt;, making the code more concise.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Closures (Variable Capture)&lt;/strong&gt;: Anonymous functions automatically "capture" variables from the scope in which they are defined. In the last example, the function &lt;code&gt;x -&amp;gt; x * multiplier&lt;/code&gt; uses the &lt;code&gt;multiplier&lt;/code&gt; variable defined outside of it. This behavior, where a function remembers the environment it was created in, is called a &lt;strong&gt;closure&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0046_anonymous_functions.jl
Doubled with standard &lt;span class="k"&gt;function&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;2, 4, 6, 8]
&lt;span class="nt"&gt;--------------------&lt;/span&gt;
Doubled with anonymous &lt;span class="k"&gt;function&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;2, 4, 6, 8]
&lt;span class="nt"&gt;--------------------&lt;/span&gt;
Sums using multi-arg anonymous &lt;span class="k"&gt;function&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;11, 22]
&lt;span class="nt"&gt;--------------------&lt;/span&gt;
Using captured variable &lt;span class="s1"&gt;'multiplier'&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;3, 6, 9, 12]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;0047_do_blocks.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0047_do_blocks.jl&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="n"&gt;Printf&lt;/span&gt;

&lt;span class="c"&gt;# 1. A function that takes another function as its first argument.&lt;/span&gt;
&lt;span class="c"&gt;#    This simulates managing a resource (like opening/closing a file).&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; with_resource&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Function&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resource_name&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Acquiring resource: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resource_name&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;resource_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;9999&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Simulate getting a resource handle&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;
        &lt;span class="c"&gt;# Execute the function passed in, giving it the resource ID&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resource_id&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Function executed, result: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
        &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"An error occurred: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;finally&lt;/span&gt;
        &lt;span class="c"&gt;# Ensure the resource is always released, even if an error occurs.&lt;/span&gt;
        &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Releasing resource: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resource_name&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;" (ID: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resource_id&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;")"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 2. Call `with_resource` using a standard anonymous function argument.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Calling with standard anonymous function ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;with_resource&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;@sprintf&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Processing resource %d"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="x"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"MyData"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 3. Call `with_resource` using the 'do' block syntax.&lt;/span&gt;
&lt;span class="c"&gt;#    This is syntactic sugar for the above, especially useful for multi-line functions.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Calling with 'do' block ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;with_resource&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"MyData"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;
    &lt;span class="c"&gt;# This block of code is automatically turned into an anonymous function&lt;/span&gt;
    &lt;span class="c"&gt;# that takes 'id' as its argument.&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Inside the do block, working with ID: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;processed_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;@sprintf&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Processed resource %d successfully"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# The last expression is implicitly returned from the anonymous function&lt;/span&gt;
    &lt;span class="n"&gt;processed_data&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces the &lt;strong&gt;&lt;code&gt;do&lt;/code&gt; block&lt;/strong&gt; syntax, which is a convenient and readable way to pass a multi-line anonymous function as the &lt;em&gt;first&lt;/em&gt; argument to another function. It's commonly used for managing resources safely, similar to Python's &lt;code&gt;with&lt;/code&gt; statement or RAII in C++. 📝&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Pattern&lt;/strong&gt;: Julia functions that manage resources (like opening files, network connections, or temporary directories) often follow a pattern: they take a function as their first argument. This function represents the code the user wants to execute &lt;em&gt;while&lt;/em&gt; the resource is available. The managing function is responsible for setting up the resource before calling the user's function and guaranteeing cleanup afterwards, even if errors occur.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;with_resource&lt;/code&gt; Function&lt;/strong&gt;: Our example function &lt;code&gt;with_resource(func, resource_name)&lt;/code&gt; simulates this pattern. It acquires a dummy resource (an ID), uses a &lt;code&gt;try...finally&lt;/code&gt; block to ensure cleanup, and calls the provided &lt;code&gt;func&lt;/code&gt;, passing it the resource ID.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Standard Anonymous Function Call&lt;/strong&gt;: The first call shows the standard way to pass an anonymous function: &lt;code&gt;with_resource(id -&amp;gt; ..., "MyData")&lt;/code&gt;. This works fine for simple, one-line functions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;do&lt;/code&gt; Block Syntax&lt;/strong&gt;: The second call demonstrates the &lt;code&gt;do&lt;/code&gt; block:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="n"&gt;with_resource&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"MyData"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;
    &lt;span class="c"&gt;# Code block...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;This is &lt;strong&gt;syntactic sugar&lt;/strong&gt; that Julia automatically rewrites into the standard anonymous function call.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The arguments before &lt;code&gt;do&lt;/code&gt; (&lt;code&gt;"MyData"&lt;/code&gt;) become the arguments &lt;em&gt;after&lt;/em&gt; the function argument in the actual call.&lt;/li&gt;
&lt;li&gt;The variable(s) after &lt;code&gt;do&lt;/code&gt; (&lt;code&gt;id&lt;/code&gt;) become the argument(s) to the anonymous function.&lt;/li&gt;
&lt;li&gt;The code between &lt;code&gt;do&lt;/code&gt; and &lt;code&gt;end&lt;/code&gt; becomes the body of the anonymous function.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Readability&lt;/strong&gt;: The &lt;code&gt;do&lt;/code&gt; block is much more readable for multi-line operations, as it avoids deeply nested parentheses and clearly separates the resource being managed from the code operating on it.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Resource Management&lt;/strong&gt;: This pattern, often used with &lt;code&gt;do&lt;/code&gt;, ensures resources are properly released. The &lt;code&gt;finally&lt;/code&gt; block in &lt;code&gt;with_resource&lt;/code&gt; guarantees the "Releasing resource" message prints, whether the code inside the &lt;code&gt;do&lt;/code&gt; block succeeds or throws an error.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0047_do_blocks.jl
&lt;span class="nt"&gt;---&lt;/span&gt; Calling with standard anonymous &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
Acquiring resource: MyData
Function executed, result: Processing resource &amp;lt;ID&amp;gt;
Releasing resource: MyData &lt;span class="o"&gt;(&lt;/span&gt;ID: &amp;lt;ID&amp;gt;&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nt"&gt;--------------------&lt;/span&gt;

&lt;span class="nt"&gt;---&lt;/span&gt; Calling with &lt;span class="s1"&gt;'do'&lt;/span&gt; block &lt;span class="nt"&gt;---&lt;/span&gt;
Acquiring resource: MyData
Inside the &lt;span class="k"&gt;do &lt;/span&gt;block, working with ID: &amp;lt;ID&amp;gt;
Function executed, result: Processed resource &amp;lt;ID&amp;gt; successfully
Releasing resource: MyData &lt;span class="o"&gt;(&lt;/span&gt;ID: &amp;lt;ID&amp;gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(Note: &lt;code&gt;&amp;lt;ID&amp;gt;&lt;/code&gt; will be a random 4-digit number)&lt;/em&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Module 5: Your Own Types and Code Organization
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Struct
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;0048_struct_basics.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0048_struct_basics.jl&lt;/span&gt;

&lt;span class="c"&gt;# 1. Define a new composite data type using the 'struct' keyword.&lt;/span&gt;
&lt;span class="c"&gt;# By default (without 'mutable'), a 'struct' is immutable.&lt;/span&gt;
&lt;span class="c"&gt;# This creates a new type named 'Point'.&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="nc"&gt; Point&lt;/span&gt;
    &lt;span class="c"&gt;# Fields are defined with their names and type annotations&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;
    &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 2. Instantiate (create an instance of) the struct.&lt;/span&gt;
&lt;span class="c"&gt;# Julia provides a default constructor that takes all fields as arguments.&lt;/span&gt;
&lt;span class="n"&gt;p1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Point&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;10.0&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;20.0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 3. Access fields using dot notation.&lt;/span&gt;
&lt;span class="c"&gt;# Note: println separates arguments with a space by default.&lt;/span&gt;
&lt;span class="c"&gt;# The call: println("Label: ", variable) is the standard, readable form.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Accessing field p1.x: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Accessing field p1.y: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 4. Inspect the instance and its type.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Instance p1: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Type of p1:  "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 5. Constructor Type Conversion&lt;/span&gt;
&lt;span class="c"&gt;# Julia's default outer constructor calls convert() on its arguments.&lt;/span&gt;
&lt;span class="c"&gt;# Point(x, y) is automatically defined as:&lt;/span&gt;
&lt;span class="c"&gt;# Point(x, y) = new(convert(Float64, x), convert(Float64, y))&lt;/span&gt;

&lt;span class="c"&gt;# Therefore, passing integers is valid, as they are convertible to Float64.&lt;/span&gt;
&lt;span class="n"&gt;p2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Point&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Constructed from Ints: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Type of p2: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;p3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Point&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;20.0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Constructed from Int/Float: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p3&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 6. When does construction fail?&lt;/span&gt;
&lt;span class="c"&gt;# It fails when convert() fails.&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="n"&gt;p_fail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Point&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hello"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;20.0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Error (as expected) on non-convertible type: "&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces the &lt;code&gt;struct&lt;/code&gt;, the fundamental tool in Julia for creating your own &lt;strong&gt;composite data types&lt;/strong&gt;. It is the direct equivalent of a C &lt;code&gt;struct&lt;/code&gt;, a &lt;code&gt;std::tuple&lt;/code&gt; in C++, or a "frozen" dataclass in Python.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Core Concept:&lt;/strong&gt; A &lt;code&gt;struct&lt;/code&gt; is a way to bundle multiple, related values (called &lt;strong&gt;fields&lt;/strong&gt;) into a single, named object. You define the "blueprint" for the &lt;code&gt;struct&lt;/code&gt; (its name and its fields' types), and then you can create &lt;strong&gt;instances&lt;/strong&gt; of that blueprint.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Default Immutability:&lt;/strong&gt; By default, a &lt;code&gt;struct&lt;/code&gt; in Julia is &lt;strong&gt;immutable&lt;/strong&gt;. This is a deliberate design choice. Once an instance like &lt;code&gt;p1&lt;/code&gt; is created, its fields (&lt;code&gt;p1.x&lt;/code&gt; and &lt;code&gt;p1.y&lt;/code&gt;) &lt;strong&gt;cannot be changed&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Constructor and Conversion:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;::Float64&lt;/code&gt; annotations are a &lt;strong&gt;strict contract&lt;/strong&gt; defining the &lt;em&gt;physical memory layout&lt;/em&gt; of the &lt;code&gt;struct&lt;/code&gt;. &lt;code&gt;Point&lt;/code&gt; is a contiguous 16-byte block of memory: 8 bytes for &lt;code&gt;x&lt;/code&gt; followed by 8 bytes for &lt;code&gt;y&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;When you define a &lt;code&gt;struct&lt;/code&gt;, Julia &lt;em&gt;also&lt;/em&gt; provides a default &lt;strong&gt;outer constructor&lt;/strong&gt; that makes it easy to use. This constructor's behavior is &lt;code&gt;Point(x, y) = new(convert(Float64, x), convert(Float64, y))&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;This is why &lt;code&gt;Point(10, 20)&lt;/code&gt; and &lt;code&gt;Point(10, 20.0)&lt;/code&gt; &lt;strong&gt;both succeed&lt;/strong&gt;. Julia automatically calls &lt;code&gt;convert(Float64, 10)&lt;/code&gt; and &lt;code&gt;convert(Float64, 20)&lt;/code&gt;, creating the &lt;code&gt;Point(10.0, 20.0)&lt;/code&gt; instance.&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;MethodError&lt;/code&gt; only occurs if you provide a type that &lt;code&gt;convert&lt;/code&gt; cannot handle, such as &lt;code&gt;Point("hello", 20.0)&lt;/code&gt;. This robust, "it-just-works" conversion is a core feature of Julia's constructor system.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Performance Deep-Dive: The &lt;code&gt;isbits&lt;/code&gt; Optimization&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
This is the most critical concept for understanding &lt;code&gt;struct&lt;/code&gt; performance.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1.  **`isbits` Type:** Our `Point` struct is an **`isbits`** type. The Julia documentation defines this as a type that is **immutable** and **contains no references** to other values. `Point` is immutable and contains only `Float64`s (which are `isbits`), so it qualifies.
2.  **Stack Allocation:** Because `Point` is a small, immutable, self-contained block of data, the compiler can treat it as a single, simple value (like a single `Int128`). When created inside a function, it can be allocated on the **stack**, which is dramatically faster than heap allocation and avoids any work for the garbage collector (GC).
3.  **Register Passing:** When you pass a `Point` object to another function, the compiler can pass it *directly* in **CPU registers** (e.g., two 64-bit registers) instead of allocating it and passing a pointer. This is the fastest possible way to pass an argument.
4.  **Array Layout:** This is the key. A `Vector{Point}` is **not** an array of pointers. Because `Point` is `isbits`, Julia stores the values **inlined** in a single, flat, contiguous block of memory. The memory layout is literally `[p1.x, p1.y, p2.x, p2.y, ...]`. This "Array of Structs" (AoS) layout is C-like, cache-friendly, and enables the compiler to use powerful SIMD vector instructions when iterating.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;References:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;isbits&lt;/code&gt; Definition:&lt;/strong&gt; Julia Official Documentation, &lt;code&gt;isbits&lt;/code&gt; function. States &lt;code&gt;isbits(T)&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt; if &lt;code&gt;T&lt;/code&gt; is "immutable and contains no references to other values."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stack/Register Allocation:&lt;/strong&gt; Julia Official Documentation, Manual, Types. States: "...small enough immutable values like integers and floats are typically passed to functions in registers (or stack allocated). Mutable values, on the other hand are heap-allocated..."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Array Layout:&lt;/strong&gt; Confirmed by Julia contributor &lt;code&gt;mbauman&lt;/code&gt; in an authoritative Stack Overflow answer: "Julia's arrays will only store elements of type &lt;code&gt;T&lt;/code&gt; unboxed if &lt;code&gt;isbits(T)&lt;/code&gt; is true. That is, the elements must be both immutable and pointer-free."&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0048_struct_basics.jl
Accessing field p1.x: 10.0
Accessing field p1.y: 20.0

Instance p1: Point&lt;span class="o"&gt;(&lt;/span&gt;10.0, 20.0&lt;span class="o"&gt;)&lt;/span&gt;
Type of p1:  Point
&lt;span class="nt"&gt;--------------------&lt;/span&gt;
Constructed from Ints: Point&lt;span class="o"&gt;(&lt;/span&gt;10.0, 20.0&lt;span class="o"&gt;)&lt;/span&gt;
Type of p2: Point
Constructed from Int/Float: Point&lt;span class="o"&gt;(&lt;/span&gt;10.0, 20.0&lt;span class="o"&gt;)&lt;/span&gt;

Error &lt;span class="o"&gt;(&lt;/span&gt;as expected&lt;span class="o"&gt;)&lt;/span&gt; on non-convertible &lt;span class="nb"&gt;type&lt;/span&gt;: 
MethodError&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;f&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;convert, &lt;span class="nv"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;Float64, &lt;span class="s2"&gt;"hello"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;, &lt;span class="nv"&gt;world&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;...&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;0049_struct_immutability.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0049_struct_immutability.jl&lt;/span&gt;

&lt;span class="c"&gt;# 1. Define the same immutable 'Point' struct&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="nc"&gt; Point&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;
    &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 2. Create an instance&lt;/span&gt;
&lt;span class="n"&gt;p1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Point&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;10.0&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;20.0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Original point p1: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 3. Attempt to modify a field of the immutable struct&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;30.0&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Caught expected error:"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 4. The "correct" way to "modify" an immutable object&lt;/span&gt;
&lt;span class="c"&gt;# is to create a new one based on the old one.&lt;/span&gt;
&lt;span class="n"&gt;p2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Point&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;5.0&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Created new point p2: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Original point p1 is unchanged: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script demonstrates the core concept of &lt;strong&gt;immutability&lt;/strong&gt;, which is the default behavior for Julia &lt;code&gt;struct&lt;/code&gt;s.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Core Concept:&lt;/strong&gt; An immutable object is one whose state &lt;strong&gt;cannot be modified&lt;/strong&gt; after it is created. The &lt;code&gt;struct Point&lt;/code&gt; we defined is immutable. When we create &lt;code&gt;p1&lt;/code&gt;, the values &lt;code&gt;10.0&lt;/code&gt; and &lt;code&gt;20.0&lt;/code&gt; are locked in.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Error:&lt;/strong&gt; The line &lt;code&gt;p1.x = 30.0&lt;/code&gt; attempts to assign a new value to the &lt;code&gt;x&lt;/code&gt; field. This is a fundamental violation of the &lt;code&gt;struct&lt;/code&gt;'s immutable contract. Julia intercepts this and fails, resulting in a &lt;code&gt;Setfield! Error&lt;/code&gt; which explicitly states that &lt;code&gt;Point&lt;/code&gt; is immutable and its fields cannot be changed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Why Immutability is a Feature, Not a Bug:&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1.  **Performance:** Immutability is a powerful signal to the compiler. Because the compiler *knows* the data inside `p1` will never change, it can perform aggressive optimizations. It can store `p1` directly in **CPU registers**, allocate it on the **stack** (which is much faster than the heap), or even eliminate the object entirely and just inline its fields.
2.  **Thread Safety:** Immutable objects are inherently **thread-safe**. You can share `p1` across thousands of threads, and no locks are needed because no thread can *write* to it. This eliminates an entire class of complex concurrency bugs.
3.  **Program Logic:** It makes code easier to reason about. When you pass `p1` to a function, you are 100% guaranteed that the function cannot change it, preventing "action at a distance" bugs.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Idiomatic Pattern:&lt;/strong&gt; The idiomatic way to "modify" an immutable object is to create a &lt;strong&gt;new&lt;/strong&gt; object. The line &lt;code&gt;p2 = Point(p1.x + 5.0, p1.y)&lt;/code&gt; does not change &lt;code&gt;p1&lt;/code&gt;. It reads the values from &lt;code&gt;p1&lt;/code&gt;, creates a &lt;em&gt;brand new&lt;/em&gt; &lt;code&gt;Point&lt;/code&gt; in memory, and assigns it to &lt;code&gt;p2&lt;/code&gt;. The original &lt;code&gt;p1&lt;/code&gt; remains untouched. This is a fundamental pattern in high-performance and functional programming.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;References:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, Types:&lt;/strong&gt; "Code using immutable objects can be easier to reason about... An object with an immutable type may be copied freely by the compiler since its immutability makes it impossible to programmatically distinguish between the original object and a copy."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, Types (on Mutability):&lt;/strong&gt; "It is not permitted to modify the value of an immutable type."&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0049_struct_immutability.jl
Original point p1: Point&lt;span class="o"&gt;(&lt;/span&gt;10.0, 20.0&lt;span class="o"&gt;)&lt;/span&gt;

Caught expected error:
Setfield! Error: &lt;span class="s1"&gt;'Point'&lt;/span&gt; is immutable
&lt;span class="o"&gt;[&lt;/span&gt;...]

Created new point p2: Point&lt;span class="o"&gt;(&lt;/span&gt;15.0, 20.0&lt;span class="o"&gt;)&lt;/span&gt;
Original point p1 is unchanged: Point&lt;span class="o"&gt;(&lt;/span&gt;10.0, 20.0&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Mutable Struct
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;0050_mutable_struct.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0050_mutable_struct.jl&lt;/span&gt;

&lt;span class="c"&gt;# 1. Define a MUTABLE composite type using the 'mutable struct' keywords.&lt;/span&gt;
&lt;span class="k"&gt;mutable struct&lt;/span&gt;&lt;span class="nc"&gt; MutablePoint&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;
    &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 2. Instantiate the mutable struct.&lt;/span&gt;
&lt;span class="c"&gt;# The default constructor works identically.&lt;/span&gt;
&lt;span class="n"&gt;p1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MutablePoint&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;10.0&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;20.0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Original mutable point p1: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 3. Modify a field in-place.&lt;/span&gt;
&lt;span class="c"&gt;# This operation is now legal and succeeds.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Mutating p1.x = 30.0..."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;30.0&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Mutated point p1: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 4. Another in-place modification&lt;/span&gt;
&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mf"&gt;5.0&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Mutated point p1 again: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces the &lt;strong&gt;&lt;code&gt;mutable struct&lt;/code&gt;&lt;/strong&gt;, which creates objects whose fields &lt;strong&gt;can be changed&lt;/strong&gt; after creation.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Core Concept:&lt;/strong&gt; The &lt;code&gt;mutable&lt;/code&gt; keyword changes the fundamental contract of the type. &lt;code&gt;mutable struct&lt;/code&gt; creates a "container" whose contents can be modified in-place, while the default &lt;code&gt;struct&lt;/code&gt; creates a single, unchangeable "value".&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Syntax:&lt;/strong&gt; The only difference in the definition is the addition of the &lt;code&gt;mutable&lt;/code&gt; keyword before &lt;code&gt;struct&lt;/code&gt;. Instantiation and field access (&lt;code&gt;.x&lt;/code&gt;) are syntactically identical.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;In-Place Modification:&lt;/strong&gt; The line &lt;code&gt;p1.x = 30.0&lt;/code&gt; now succeeds. This operation directly modifies the memory of the &lt;code&gt;p1&lt;/code&gt; object itself. Any other variable in the program that holds a reference to &lt;code&gt;p1&lt;/code&gt; will instantly see this change.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Performance Trade-Off: Heap vs. Stack&lt;/strong&gt;&lt;br&gt;
This is one of the most important performance distinctions in Julia.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1.  **Allocation:** Because a `mutable struct` must have a single, stable identity in memory (so all references to it can be updated), it is **heap-allocated**. This is a slower operation than the stack-allocation that is possible for immutable `struct`s.
2.  **`isbits`:** A `mutable struct` is **never** an `isbits` type.
3.  **Array Layout:** A `Vector{MutablePoint}` is an **array of pointers** (or "references") to heap-allocated `MutablePoint` objects. It is *not* a flat, contiguous block of data. This memory layout (an "Array of Pointers") is less cache-friendly and prevents the compiler from using SIMD instructions.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Guideline:&lt;/strong&gt; You pay a significant performance cost for mutability. Therefore, &lt;strong&gt;always default to an immutable &lt;code&gt;struct&lt;/code&gt;&lt;/strong&gt;. Only use &lt;code&gt;mutable struct&lt;/code&gt; when you have a specific, long-lived object that &lt;em&gt;must&lt;/em&gt; have its state changed over time (e.g., a simulation environment, a network connection manager, a buffer). For small, data-carrying objects like coordinates or complex numbers, &lt;code&gt;struct&lt;/code&gt; is almost always the correct, high-performance choice.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;References:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, Types:&lt;/strong&gt; "Composite Types declared with &lt;code&gt;mutable struct&lt;/code&gt; are mutable..."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, Types (on Mutability):&lt;/strong&gt; "Mutable values, on the other hand are heap-allocated and passed to functions as pointers to heap-allocated values..."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, &lt;code&gt;isbits&lt;/code&gt;:&lt;/strong&gt; &lt;code&gt;isbits(MutablePoint)&lt;/code&gt; would return &lt;code&gt;false&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0050_mutable_struct.jl
Original mutable point p1: MutablePoint&lt;span class="o"&gt;(&lt;/span&gt;10.0, 20.0&lt;span class="o"&gt;)&lt;/span&gt;

Mutating p1.x &lt;span class="o"&gt;=&lt;/span&gt; 30.0...
Mutated point p1: MutablePoint&lt;span class="o"&gt;(&lt;/span&gt;30.0, 20.0&lt;span class="o"&gt;)&lt;/span&gt;
Mutated point p1 again: MutablePoint&lt;span class="o"&gt;(&lt;/span&gt;30.0, 25.0&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;0051_mutable_vs_immutable_performance.md&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This is one of the most important performance trade-offs in the Julia language. The choice between an immutable &lt;code&gt;struct&lt;/code&gt; and a &lt;code&gt;mutable struct&lt;/code&gt; is not cosmetic; it fundamentally changes how the compiler handles your data, with massive performance implications.&lt;/p&gt;




&lt;h3&gt;
  
  
  Comparison: &lt;code&gt;struct&lt;/code&gt; (Immutable) vs. &lt;code&gt;mutable struct&lt;/code&gt; (Mutable)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;struct Point&lt;/code&gt; (Immutable)&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;mutable struct MutablePoint&lt;/code&gt; (Mutable)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;isbits&lt;/code&gt; Status&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;&lt;code&gt;true&lt;/code&gt;&lt;/strong&gt; (if fields are &lt;code&gt;isbits&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;&lt;code&gt;false&lt;/code&gt;&lt;/strong&gt; (always)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Allocation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Stack&lt;/strong&gt; (if possible)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Heap&lt;/strong&gt; (always)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Passing to Functions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;By value (in &lt;strong&gt;CPU registers&lt;/strong&gt;)&lt;/td&gt;
&lt;td&gt;By reference (as a &lt;strong&gt;pointer&lt;/strong&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Array Layout (&lt;code&gt;Vector{T}&lt;/code&gt;)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Inlined / Contiguous&lt;/strong&gt; (Array of Structs)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Array of Pointers&lt;/strong&gt; (Array of Pointers)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cache Performance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Excellent&lt;/strong&gt; (cache-friendly)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Poor&lt;/strong&gt; (pointer-chasing, cache misses)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  1. Allocation: Stack vs. Heap
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;struct Point&lt;/code&gt; (Immutable):&lt;/strong&gt; Because an immutable &lt;code&gt;struct&lt;/code&gt; is a self-contained, unchangeable block of bits (it's &lt;code&gt;isbits&lt;/code&gt;), the compiler can treat it as a simple value, just like an &lt;code&gt;Int&lt;/code&gt; or &lt;code&gt;Float64&lt;/code&gt;. When created inside a function, it will typically be &lt;strong&gt;stack-allocated&lt;/strong&gt;. Stack allocation is extremely fast—it's just a single instruction to move the stack pointer. It also means there is &lt;strong&gt;zero work for the garbage collector (GC)&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;mutable struct MutablePoint&lt;/code&gt; (Mutable):&lt;/strong&gt; Because a mutable object's fields can change at any time, it must have a single, stable address in memory so that all variables referencing it see the same changes. This requires it to be &lt;strong&gt;heap-allocated&lt;/strong&gt;. Heap allocation is much slower: it requires a call to the memory manager (&lt;code&gt;malloc&lt;/code&gt;) to find a free block of memory, and the GC must track this object for its entire lifetime.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Conclusion:&lt;/strong&gt; Immutable &lt;code&gt;struct&lt;/code&gt;s are significantly "cheaper" to create and destroy than mutable &lt;code&gt;struct&lt;/code&gt;s.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Array Layout: Inlined vs. Pointers
&lt;/h3&gt;

&lt;p&gt;This is the most critical difference for high-performance computing.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Vector{Point}&lt;/code&gt; (Immutable &lt;code&gt;isbits&lt;/code&gt;):&lt;/strong&gt; Julia stores the &lt;code&gt;Point&lt;/code&gt; objects &lt;strong&gt;inlined&lt;/strong&gt; in the array's memory. The &lt;code&gt;Vector&lt;/code&gt; is one single, contiguous block of &lt;code&gt;Float64&lt;/code&gt; values.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Memory Layout:&lt;/strong&gt; &lt;code&gt;[p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, ...]&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;&lt;code&gt;Vector{MutablePoint}&lt;/code&gt; (Mutable):&lt;/strong&gt; Julia stores an array of &lt;strong&gt;pointers&lt;/strong&gt;. Each pointer references a &lt;em&gt;separate&lt;/em&gt; &lt;code&gt;MutablePoint&lt;/code&gt; object allocated somewhere else on the heap.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Memory Layout:&lt;/strong&gt; &lt;code&gt;[ptr1, ptr2, ptr3, ...]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;...where &lt;code&gt;ptr1&lt;/code&gt; points to &lt;code&gt;MutablePoint(x1, y1)&lt;/code&gt;, &lt;code&gt;ptr2&lt;/code&gt; points to &lt;code&gt;MutablePoint(x2, y2)&lt;/code&gt;, etc.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h3&gt;
  
  
  3. CPU Cache and Iteration Performance
&lt;/h3&gt;

&lt;p&gt;The array layout has a direct and massive impact on iteration speed.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Iterating &lt;code&gt;Vector{Point}&lt;/code&gt;:&lt;/strong&gt; When you loop over this array, you are reading memory sequentially. The CPU's prefetcher can load this data directly into the L1/L2 cache &lt;em&gt;before&lt;/em&gt; it's even needed. This results in an &lt;strong&gt;extremely fast, cache-friendly&lt;/strong&gt; loop with no wasted cycles. The compiler can also &lt;strong&gt;vectorize&lt;/strong&gt; the loop using &lt;strong&gt;SIMD&lt;/strong&gt; instructions, processing multiple &lt;code&gt;Point&lt;/code&gt;s per cycle.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Iterating &lt;code&gt;Vector{MutablePoint}&lt;/code&gt;:&lt;/strong&gt; When you loop over this array, you get &lt;strong&gt;pointer-chasing&lt;/strong&gt;.

&lt;ol&gt;
&lt;li&gt; Read &lt;code&gt;ptr1&lt;/code&gt; from the array (potential cache miss).&lt;/li&gt;
&lt;li&gt; "Jump" (dereference) to the memory address of &lt;code&gt;ptr1&lt;/code&gt; to fetch the &lt;code&gt;MutablePoint&lt;/code&gt; object (another potential cache miss).&lt;/li&gt;
&lt;li&gt; Read &lt;code&gt;ptr2&lt;/code&gt; from the array...&lt;/li&gt;
&lt;li&gt; Jump to the memory address of &lt;code&gt;ptr2&lt;/code&gt;...
This "jumpy" memory access pattern defeats the CPU's prefetcher, causes constant cache misses, and makes SIMD vectorization impossible.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Conclusion:&lt;/strong&gt; Iterating a &lt;code&gt;Vector&lt;/code&gt; of immutable &lt;code&gt;isbits&lt;/code&gt; &lt;code&gt;struct&lt;/code&gt;s is often &lt;strong&gt;orders of magnitude faster&lt;/strong&gt; than iterating a &lt;code&gt;Vector&lt;/code&gt; of &lt;code&gt;mutable struct&lt;/code&gt;s.&lt;/p&gt;




&lt;h3&gt;
  
  
  Guideline
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Always default to immutable &lt;code&gt;struct&lt;/code&gt;&lt;/strong&gt;. You should only use &lt;code&gt;mutable struct&lt;/code&gt; when you have a specific, compelling reason to—such as a long-lived object that &lt;em&gt;must&lt;/em&gt; have its state changed, like a buffer, a simulation environment, or a network connection manager.&lt;/li&gt;
&lt;li&gt;For any small, data-carrying object (coordinates, complex numbers, configuration parameters), immutability (&lt;code&gt;struct&lt;/code&gt;) is the correct, safe, and high-performance choice.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Abstract Type
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;0052_abstract_types.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0052_abstract_types.jl&lt;/span&gt;

&lt;span class="c"&gt;# 1. Define an 'abstract type'.&lt;/span&gt;
&lt;span class="c"&gt;# An abstract type defines a general concept, not a concrete object.&lt;/span&gt;
&lt;span class="c"&gt;# You cannot create an instance of it.&lt;/span&gt;
&lt;span class="k"&gt;abstract type&lt;/span&gt;&lt;span class="nc"&gt; AbstractShape&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 2. Define a 'concrete type' that *subtypes* AbstractShape.&lt;/span&gt;
&lt;span class="c"&gt;# The '&amp;lt;:' operator means "is a subtype of".&lt;/span&gt;
&lt;span class="c"&gt;# This struct is immutable and will be 'isbits'.&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="nc"&gt; Circle&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;:&lt;/span&gt; &lt;span class="n"&gt;AbstractShape&lt;/span&gt;
    &lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 3. Define another concrete 'isbits' subtype.&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="nc"&gt; Rectangle&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;:&lt;/span&gt; &lt;span class="n"&gt;AbstractShape&lt;/span&gt;
    &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;
    &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 4. Define a concrete *mutable* subtype.&lt;/span&gt;
&lt;span class="c"&gt;# Because it is 'mutable', it will *not* be 'isbits'.&lt;/span&gt;
&lt;span class="k"&gt;mutable struct&lt;/span&gt;&lt;span class="nc"&gt; MutableSquare&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;:&lt;/span&gt; &lt;span class="n"&gt;AbstractShape&lt;/span&gt;
    &lt;span class="n"&gt;side&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 5. Attempting to instantiate the abstract type will fail.&lt;/span&gt;
&lt;span class="c"&gt;# Abstract types are just concepts; they have no constructor.&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="n"&gt;shape_fail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AbstractShape&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Caught expected error (cannot instantiate abstract type):"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 6. Instantiating the *concrete* types succeeds.&lt;/span&gt;
&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Circle&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;10.0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Rectangle&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;5.0&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;10.0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MutableSquare&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;7.0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Concrete instances:"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"c = "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"r = "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"s = "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 7. Check the type hierarchy using the subtype operator '&amp;lt;:'.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Type hierarchy checks:"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Circle &amp;lt;: AbstractShape? "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Circle&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;:&lt;/span&gt; &lt;span class="n"&gt;AbstractShape&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Rectangle &amp;lt;: AbstractShape? "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Rectangle&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;:&lt;/span&gt; &lt;span class="n"&gt;AbstractShape&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"MutableSquare &amp;lt;: AbstractShape? "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MutableSquare&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;:&lt;/span&gt; &lt;span class="n"&gt;AbstractShape&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# Check if the *instance's type* is a subtype.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"typeof(c) &amp;lt;: AbstractShape? "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;:&lt;/span&gt; &lt;span class="n"&gt;AbstractShape&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- The Nuance of isbits ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# 8. 'isbits(x)' checks the property of an *instance*.&lt;/span&gt;
&lt;span class="c"&gt;# It's a convenient shorthand for isbitstype(typeof(x)).&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"isbits(c): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isbits&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# true&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"isbits(r): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isbits&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# true&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"isbits(s): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isbits&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# false (it's mutable)&lt;/span&gt;

&lt;span class="c"&gt;# 9. 'isbitstype(T)' checks the property of the *Type* itself.&lt;/span&gt;
&lt;span class="c"&gt;# This is the canonical way to check if a type has a C-like,&lt;/span&gt;
&lt;span class="c"&gt;# plain-data memory layout.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;isbitstype(Circle): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isbitstype&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Circle&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# true&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"isbitstype(Rectangle): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isbitstype&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Rectangle&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# true&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"isbitstype(MutableSquare): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isbitstype&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MutableSquare&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces &lt;strong&gt;&lt;code&gt;abstract type&lt;/code&gt;s&lt;/strong&gt;, which form the foundation of Julia's powerful type hierarchy and are the key to &lt;strong&gt;multiple dispatch&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Core Concept:&lt;/strong&gt; An &lt;code&gt;abstract type&lt;/code&gt; defines a &lt;strong&gt;concept&lt;/strong&gt; or an &lt;strong&gt;interface&lt;/strong&gt;, not a specific "thing." You &lt;strong&gt;cannot&lt;/strong&gt; create an instance of an abstract type.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In our example, &lt;code&gt;AbstractShape&lt;/code&gt; represents the general idea of "a shape." It makes no sense to create a generic "shape" without knowing if it's a circle, a square, etc.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;try...catch&lt;/code&gt; block proves this: &lt;code&gt;AbstractShape()&lt;/code&gt; fails with a &lt;code&gt;MethodError&lt;/code&gt; because no constructor exists for this abstract concept.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Subtyping (&lt;code&gt;&amp;lt;:&lt;/code&gt;):&lt;/strong&gt; The "is a subtype of" operator, &lt;code&gt;&amp;lt;:&lt;/code&gt;, is used to build the hierarchy.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;struct Circle &amp;lt;: AbstractShape&lt;/code&gt; declares that a &lt;code&gt;Circle&lt;/code&gt; &lt;strong&gt;is a kind of&lt;/strong&gt; &lt;code&gt;AbstractShape&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Circle&lt;/code&gt; and &lt;code&gt;Rectangle&lt;/code&gt; are called &lt;strong&gt;concrete types&lt;/strong&gt;. They are "real" types that you &lt;em&gt;can&lt;/em&gt; create instances of.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Purpose:&lt;/strong&gt; Why define this? Abstract types allow you to write &lt;strong&gt;generic functions&lt;/strong&gt;. You can write a function that accepts any &lt;code&gt;AbstractShape&lt;/code&gt;, and Julia's dispatch system will automatically call the correct, specific implementation for a &lt;code&gt;Circle&lt;/code&gt; or a &lt;code&gt;Rectangle&lt;/code&gt;. This is the subject of the very next lesson.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;isbits&lt;/code&gt; vs. &lt;code&gt;isbitstype&lt;/code&gt;:&lt;/strong&gt; This is a crucial, subtle distinction.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;isbitstype(T::Type)&lt;/code&gt;:&lt;/strong&gt; This is the authoritative function to ask: "Does the type &lt;code&gt;T&lt;/code&gt; describe a plain-data, C-like memory layout?" As shown, &lt;code&gt;isbitstype(Circle)&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt; because it's immutable and has &lt;code&gt;isbits&lt;/code&gt; fields. &lt;code&gt;isbitstype(MutableSquare)&lt;/code&gt; is &lt;code&gt;false&lt;/code&gt; because it's mutable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;isbits(x)&lt;/code&gt;:&lt;/strong&gt; This is a function that operates on a &lt;strong&gt;value&lt;/strong&gt;. It's a convenient shorthand for &lt;code&gt;isbitstype(typeof(x))&lt;/code&gt;. This is why &lt;code&gt;isbits(c)&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt;. The &lt;em&gt;instance&lt;/em&gt; &lt;code&gt;c&lt;/code&gt; is of type &lt;code&gt;Circle&lt;/code&gt;, and &lt;code&gt;isbitstype(Circle)&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Container Performance:&lt;/strong&gt; This hierarchy has direct performance implications for arrays.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;Vector{Circle}&lt;/code&gt; is a &lt;strong&gt;homogeneous&lt;/strong&gt; array. Because &lt;code&gt;isbitstype(Circle)&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt;, the &lt;code&gt;Circle&lt;/code&gt; objects will be stored &lt;strong&gt;inlined and contiguously&lt;/strong&gt; in memory (an "Array of Structs"). This is fast.&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;Vector{AbstractShape}&lt;/code&gt; is a &lt;strong&gt;heterogeneous&lt;/strong&gt; array. Since it must be able to hold &lt;em&gt;any&lt;/em&gt; &lt;code&gt;AbstractShape&lt;/code&gt;, including &lt;code&gt;Circle&lt;/code&gt; (16 bytes) and &lt;code&gt;MutableSquare&lt;/code&gt; (8-byte pointer), it &lt;strong&gt;must&lt;/strong&gt; be an "array of pointers" (a "boxed" array). This is much slower to iterate.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;References:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, Types, "Abstract Types":&lt;/strong&gt; "Abstract types cannot be instantiated... Abstract types are a way to organize types into a hierarchy."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, Types, "Subtyping":&lt;/strong&gt; "The &lt;code&gt;&amp;lt;:&lt;/code&gt; operator is declared as &lt;code&gt;(::Type, ::Type) -&amp;gt; Bool&lt;/code&gt;, and returns &lt;code&gt;true&lt;/code&gt; if its left operand is a subtype of its right operand."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, &lt;code&gt;isbits(x)&lt;/code&gt;:&lt;/strong&gt; "Return &lt;code&gt;true&lt;/code&gt; if the value &lt;code&gt;x&lt;/code&gt; is of an &lt;code&gt;isbits&lt;/code&gt; type." &lt;code&gt;isbitstype(T)&lt;/code&gt; is noted as the canonical check for the type itself.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0052_abstract_types.jl
Caught expected error &lt;span class="o"&gt;(&lt;/span&gt;cannot instantiate abstract &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;:
MethodError: no method matching AbstractShape&lt;span class="o"&gt;()&lt;/span&gt;

Concrete instances:
c &lt;span class="o"&gt;=&lt;/span&gt; Circle&lt;span class="o"&gt;(&lt;/span&gt;10.0&lt;span class="o"&gt;)&lt;/span&gt;
r &lt;span class="o"&gt;=&lt;/span&gt; Rectangle&lt;span class="o"&gt;(&lt;/span&gt;5.0, 10.0&lt;span class="o"&gt;)&lt;/span&gt;
s &lt;span class="o"&gt;=&lt;/span&gt; MutableSquare&lt;span class="o"&gt;(&lt;/span&gt;7.0&lt;span class="o"&gt;)&lt;/span&gt;

Type hierarchy checks:
Circle &amp;lt;: AbstractShape? &lt;span class="nb"&gt;true
&lt;/span&gt;Rectangle &amp;lt;: AbstractShape? &lt;span class="nb"&gt;true
&lt;/span&gt;MutableSquare &amp;lt;: AbstractShape? &lt;span class="nb"&gt;true
&lt;/span&gt;typeof&lt;span class="o"&gt;(&lt;/span&gt;c&lt;span class="o"&gt;)&lt;/span&gt; &amp;lt;: AbstractShape? &lt;span class="nb"&gt;true&lt;/span&gt;

&lt;span class="nt"&gt;---&lt;/span&gt; The Nuance of isbits &lt;span class="nt"&gt;---&lt;/span&gt;
isbits&lt;span class="o"&gt;(&lt;/span&gt;c&lt;span class="o"&gt;)&lt;/span&gt;: &lt;span class="nb"&gt;true
&lt;/span&gt;isbits&lt;span class="o"&gt;(&lt;/span&gt;r&lt;span class="o"&gt;)&lt;/span&gt;: &lt;span class="nb"&gt;true
&lt;/span&gt;isbits&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt;: &lt;span class="nb"&gt;false

&lt;/span&gt;isbitstype&lt;span class="o"&gt;(&lt;/span&gt;Circle&lt;span class="o"&gt;)&lt;/span&gt;: &lt;span class="nb"&gt;true
&lt;/span&gt;isbitstype&lt;span class="o"&gt;(&lt;/span&gt;Rectangle&lt;span class="o"&gt;)&lt;/span&gt;: &lt;span class="nb"&gt;true
&lt;/span&gt;isbitstype&lt;span class="o"&gt;(&lt;/span&gt;MutableSquare&lt;span class="o"&gt;)&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;0053_dispatch_on_abstract.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0053_dispatch_on_abstract.jl&lt;/span&gt;

&lt;span class="c"&gt;# 1. Define the type hierarchy from the previous lesson&lt;/span&gt;
&lt;span class="k"&gt;abstract type&lt;/span&gt;&lt;span class="nc"&gt; AbstractShape&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="nc"&gt; Circle&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;:&lt;/span&gt; &lt;span class="n"&gt;AbstractShape&lt;/span&gt;
    &lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="nc"&gt; Rectangle&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;:&lt;/span&gt; &lt;span class="n"&gt;AbstractShape&lt;/span&gt;
    &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;
    &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;mutable struct&lt;/span&gt;&lt;span class="nc"&gt; MutableSquare&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;:&lt;/span&gt; &lt;span class="n"&gt;AbstractShape&lt;/span&gt;
    &lt;span class="n"&gt;side&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 2. Define a "generic" function that operates on the abstract type.&lt;/span&gt;
&lt;span class="c"&gt;# This function defines the "interface" or "contract".&lt;/span&gt;
&lt;span class="c"&gt;# We can provide a fallback method that throws an error.&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; calculate_area&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;AbstractShape&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# This error will be hit by any subtype that doesn't&lt;/span&gt;
    &lt;span class="c"&gt;# provide its own specific method.&lt;/span&gt;
    &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"calculate_area not implemented for type "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 3. Define a specific METHOD for Circle.&lt;/span&gt;
&lt;span class="c"&gt;# Julia will dispatch to this function when it sees a Circle.&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; calculate_area&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Circle&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;π&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 4. Define a specific METHOD for Rectangle.&lt;/span&gt;
&lt;span class="c"&gt;# This is the same function name, 'calculate_area', but with&lt;/span&gt;
&lt;span class="c"&gt;# a different type signature (a different method).&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; calculate_area&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Rectangle&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 5. Create a heterogeneous list of shapes.&lt;/span&gt;
&lt;span class="c"&gt;# This will be a Vector{AbstractShape}, which is an&lt;/span&gt;
&lt;span class="c"&gt;# array of pointers (boxed objects).&lt;/span&gt;
&lt;span class="n"&gt;shapes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Circle&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="x"&gt;),&lt;/span&gt; &lt;span class="n"&gt;Rectangle&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;3.0&lt;/span&gt;&lt;span class="x"&gt;),&lt;/span&gt; &lt;span class="n"&gt;Circle&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;4.0&lt;/span&gt;&lt;span class="x"&gt;)]&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Processing heterogeneous array of shapes ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;shape&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;shapes&lt;/span&gt;
    &lt;span class="c"&gt;# 6. Call the generic function.&lt;/span&gt;
    &lt;span class="c"&gt;# At runtime, Julia inspects the *actual* type of 'shape'&lt;/span&gt;
    &lt;span class="c"&gt;# and calls the *most specific* method available.&lt;/span&gt;
    &lt;span class="n"&gt;area&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;calculate_area&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Shape: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;" | Area: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;area&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Testing unimplemented type ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# 7. Test the fallback error&lt;/span&gt;
&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MutableSquare&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;5.0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="n"&gt;calculate_area&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Caught expected error:"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script demonstrates &lt;strong&gt;multiple dispatch&lt;/strong&gt;, which is the "payoff" for using the &lt;code&gt;abstract type&lt;/code&gt; hierarchy. This is arguably the most important and powerful design pattern in Julia.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Core Concept:&lt;/strong&gt; We have defined one generic function name, &lt;code&gt;calculate_area&lt;/code&gt;, but multiple &lt;strong&gt;methods&lt;/strong&gt; for it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;calculate_area(s::AbstractShape)&lt;/code&gt; is a generic fallback.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;calculate_area(c::Circle)&lt;/code&gt; is a specific method for &lt;code&gt;Circle&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;calculate_area(r::Rectangle)&lt;/code&gt; is a specific method for &lt;code&gt;Rectangle&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Multiple Dispatch:&lt;/strong&gt; When you call &lt;code&gt;calculate_area(shape)&lt;/code&gt;, Julia performs a runtime lookup on the &lt;em&gt;concrete type&lt;/em&gt; of the &lt;code&gt;shape&lt;/code&gt; variable. This is called &lt;strong&gt;dynamic dispatch&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1.  In the first loop iteration, `shape` is a `Circle`. Julia sees this and **dispatches** the call to the `calculate_area(c::Circle)` method.
2.  In the second iteration, `shape` is a `Rectangle`. Julia dispatches to the `calculate_area(r::Rectangle)` method.
    This mechanism allows you to write generic code (the `for` loop) that operates on the abstract concept (`AbstractShape`), while Julia handles executing the correct, specialized code automatically.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Defining an Interface:&lt;/strong&gt; The abstract type &lt;code&gt;AbstractShape&lt;/code&gt; and the generic function &lt;code&gt;calculate_area(s::AbstractShape)&lt;/code&gt; together define a "contract" or "interface." They state: "To be a usable shape in this system, you must provide a concrete method for &lt;code&gt;calculate_area&lt;/code&gt;."&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;MutableSquare&lt;/code&gt; example proves this. We created &lt;code&gt;MutableSquare &amp;lt;: AbstractShape&lt;/code&gt;, but we &lt;em&gt;forgot&lt;/em&gt; to provide a &lt;code&gt;calculate_area(s::MutableSquare)&lt;/code&gt; method.&lt;/li&gt;
&lt;li&gt;When &lt;code&gt;calculate_area(s)&lt;/code&gt; is called, Julia finds no specific method for &lt;code&gt;MutableSquare&lt;/code&gt;. It falls back to the next most general method, &lt;code&gt;calculate_area(s::AbstractShape)&lt;/code&gt;, which correctly throws our "not implemented" error. This is a feature, not a bug; it tells us our &lt;code&gt;MutableSquare&lt;/code&gt; is incomplete.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Performance:&lt;/strong&gt; This is not the same as in many object-oriented languages. This dispatch is extremely fast. Even in this "worst-case" scenario of a heterogeneous, type-unstable array (&lt;code&gt;Vector{AbstractShape}&lt;/code&gt;), Julia's dynamic dispatch is highly optimized. In cases where the compiler &lt;em&gt;can&lt;/em&gt; infer the concrete type (e.g., in a loop over a &lt;code&gt;Vector{Circle}&lt;/code&gt;), this dispatch is resolved at &lt;strong&gt;compile time&lt;/strong&gt; and has &lt;strong&gt;zero runtime cost&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;References:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Methods":&lt;/strong&gt; "In Julia, all named functions are &lt;em&gt;generic functions&lt;/em&gt;. A generic function is conceptually a single function, but consists of many &lt;em&gt;methods&lt;/em&gt;. A method is a definition of a function's behavior for a specific combination of argument types."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Dynamic Dispatch":&lt;/strong&gt; "When a function is called, the most specific method applicable to the given arguments is executed."&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  Parametric Types
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;0054_parametric_struct.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0054_parametric_struct.jl&lt;/span&gt;

&lt;span class="c"&gt;# 1. Define a 'parametric struct'.&lt;/span&gt;
&lt;span class="c"&gt;# The '{T}' is a type parameter. This makes 'Container' a&lt;/span&gt;
&lt;span class="c"&gt;# generic blueprint, not a single concrete type.&lt;/span&gt;
&lt;span class="c"&gt;# 'T' can be any type.&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="nc"&gt; Container&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="x"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 2. Instantiate with an explicit type parameter.&lt;/span&gt;
&lt;span class="c"&gt;# We create a 'Container{Float64}', where T=Float64.&lt;/span&gt;
&lt;span class="n"&gt;c_float&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Container&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;&lt;span class="x"&gt;}(&lt;/span&gt;&lt;span class="mf"&gt;10.0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Container with explicit Float64:"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Value: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c_float&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Type:  "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c_float&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;

&lt;span class="c"&gt;# 3. Instantiate with an implicit type parameter.&lt;/span&gt;
&lt;span class="c"&gt;# We let Julia's constructor *infer* the type 'T'.&lt;/span&gt;
&lt;span class="c"&gt;# By passing an Int, Julia creates a 'Container{Int64}'.&lt;/span&gt;
&lt;span class="n"&gt;c_int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Container&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Equivalent to Container{Int64}(20)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Container with inferred Int64:"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Value: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c_int&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Type:  "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c_int&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;

&lt;span class="c"&gt;# 4. 'T' can be *any* type, including non-isbits types.&lt;/span&gt;
&lt;span class="n"&gt;c_string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Container&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Container with inferred String:"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Value: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c_string&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Type:  "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c_string&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Performance: isbits checks ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 5. The 'isbits' status of the struct depends on its *parameters*.&lt;/span&gt;
&lt;span class="c"&gt;# Container{Float64} is immutable and holds an isbits type (Float64).&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"isbitstype(Container{Float64}): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isbitstype&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Container&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;&lt;span class="x"&gt;}))&lt;/span&gt; &lt;span class="c"&gt;# true&lt;/span&gt;

&lt;span class="c"&gt;# Container{String} is immutable but holds a non-isbits type (String).&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"isbitstype(Container{String}):  "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isbitstype&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Container&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="x"&gt;}))&lt;/span&gt; &lt;span class="c"&gt;# false&lt;/span&gt;

&lt;span class="c"&gt;# 'Container' itself is not a concrete type, so it's not isbits.&lt;/span&gt;
&lt;span class="c"&gt;# It's a "family" of types.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"isbitstype(Container):          "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isbitstype&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Container&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces &lt;strong&gt;parametric types&lt;/strong&gt;, Julia's version of generics (like C++ templates or C# generics). This is a core feature for writing code that is both &lt;strong&gt;reusable&lt;/strong&gt; and &lt;strong&gt;high-performance&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Core Concept:&lt;/strong&gt; A parametric &lt;code&gt;struct&lt;/code&gt; is a "blueprint for a type." The &lt;code&gt;struct Container{T}&lt;/code&gt; definition does not create a single type. Instead, it creates a &lt;em&gt;factory&lt;/em&gt; that can produce an infinite family of types, like &lt;code&gt;Container{Float64}&lt;/code&gt;, &lt;code&gt;Container{Int64}&lt;/code&gt;, and &lt;code&gt;Container{String}&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Type Parameter &lt;code&gt;{T}&lt;/code&gt;:&lt;/strong&gt; The &lt;code&gt;{T}&lt;/code&gt; introduces a "type variable" named &lt;code&gt;T&lt;/code&gt;. This &lt;code&gt;T&lt;/code&gt; can then be used as a type annotation for the fields inside the &lt;code&gt;struct&lt;/code&gt;, as we did with &lt;code&gt;value::T&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Instantiation (Explicit vs. Implicit):&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1.  **Explicit:** `Container{Float64}(10.0)`: We explicitly tell Julia to "use the `Container` blueprint, setting `T = Float64`."
2.  **Implicit:** `Container(20)`: We call the default constructor, passing an `Int64`. Julia's compiler infers that `T` must be `Int64` and automatically creates a `Container{Int64}`.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Zero-Cost Abstraction (Performance):&lt;/strong&gt; This is the crucial takeaway. When you create &lt;code&gt;c_int = Container(20)&lt;/code&gt;, Julia's compiler generates a &lt;strong&gt;new, specialized, concrete type&lt;/strong&gt; &lt;code&gt;Container{Int64}&lt;/code&gt;. This specialized type is &lt;em&gt;just as fast&lt;/em&gt; as if you had manually defined &lt;code&gt;struct IntContainer { value::Int64 }&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This is &lt;strong&gt;not&lt;/strong&gt; like &lt;code&gt;Object&lt;/code&gt; in Java. There is no boxing or dynamic dispatch to access &lt;code&gt;c_int.value&lt;/code&gt;. The compiled code knows &lt;em&gt;exactly&lt;/em&gt; where the &lt;code&gt;Int64&lt;/code&gt; is stored.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;isbits&lt;/code&gt; Status:&lt;/strong&gt; The performance of the &lt;code&gt;Container&lt;/code&gt; depends on what &lt;code&gt;T&lt;/code&gt; is.

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;isbitstype(Container{Float64})&lt;/code&gt; is &lt;strong&gt;&lt;code&gt;true&lt;/code&gt;&lt;/strong&gt;. This type is immutable and its field is &lt;code&gt;isbits&lt;/code&gt;, so it gets all the performance benefits: stack allocation, register passing, and inlined, contiguous array layouts.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;isbitstype(Container{String})&lt;/code&gt; is &lt;strong&gt;&lt;code&gt;false&lt;/code&gt;&lt;/strong&gt;. Because &lt;code&gt;String&lt;/code&gt; is not &lt;code&gt;isbits&lt;/code&gt; (it's a pointer to heap data), the resulting &lt;code&gt;Container{String}&lt;/code&gt; struct is &lt;em&gt;also&lt;/em&gt; not &lt;code&gt;isbits&lt;/code&gt;. A &lt;code&gt;Vector{Container{String}}&lt;/code&gt; would be an "array of pointers."&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;&lt;p&gt;This pattern lets you write one, generic, reusable &lt;code&gt;struct&lt;/code&gt; and trust Julia's compiler to stamp out a specialized, high-performance version for every concrete type you use it with.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;References:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, Types, "Parametric Composite Types":&lt;/strong&gt; "It is a common pattern that a type definition declares a composite type &lt;code&gt;Foo&lt;/code&gt; that can hold values of type &lt;code&gt;T&lt;/code&gt;. This is written in Julia as &lt;code&gt;struct Foo{T} ... end&lt;/code&gt;."&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0054_parametric_struct.jl
Container with explicit Float64:
  Value: 10.0
  Type:  Container&lt;span class="o"&gt;{&lt;/span&gt;Float64&lt;span class="o"&gt;}&lt;/span&gt;

Container with inferred Int64:
  Value: 20
  Type:  Container&lt;span class="o"&gt;{&lt;/span&gt;Int64&lt;span class="o"&gt;}&lt;/span&gt;

Container with inferred String:
  Value: Hello
  Type:  Container&lt;span class="o"&gt;{&lt;/span&gt;String&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;---&lt;/span&gt; Performance: isbits checks &lt;span class="nt"&gt;---&lt;/span&gt;
isbitstype&lt;span class="o"&gt;(&lt;/span&gt;Container&lt;span class="o"&gt;{&lt;/span&gt;Float64&lt;span class="o"&gt;})&lt;/span&gt;: &lt;span class="nb"&gt;true
&lt;/span&gt;isbitstype&lt;span class="o"&gt;(&lt;/span&gt;Container&lt;span class="o"&gt;{&lt;/span&gt;String&lt;span class="o"&gt;})&lt;/span&gt;:  &lt;span class="nb"&gt;false
&lt;/span&gt;isbitstype&lt;span class="o"&gt;(&lt;/span&gt;Container&lt;span class="o"&gt;)&lt;/span&gt;:          &lt;span class="nb"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;0055_parametric_functions.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0055_parametric_functions.jl&lt;/span&gt;

&lt;span class="c"&gt;# 1. Define our parametric struct from the previous lesson&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="nc"&gt; Container&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="x"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 2. A generic function using the 'where {T}' syntax.&lt;/span&gt;
&lt;span class="c"&gt;# This is the standard way to write functions for parametric types.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# Read as: "A function 'get_value' that takes 'c' of type 'Container{T}',&lt;/span&gt;
&lt;span class="c"&gt;#          'where T' is some type. This function returns a value of type T."&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; get_value&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Container&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="x"&gt;})&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="x"&gt;}&lt;/span&gt;
    &lt;span class="c"&gt;# 'T' is available as a type *variable* inside the function.&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Generic 'get_value(c::Container{T})' called, where T = "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 3. A function that returns both the value and the *type*.&lt;/span&gt;
&lt;span class="c"&gt;# This shows that 'T' is a real value (a 'DataType') inside the function.&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; get_value_and_type&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Container&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="x"&gt;})&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="x"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Function 'get_value_and_type' called, where T = "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Return a tuple&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 4. A *specific method* for Container{String}.&lt;/span&gt;
&lt;span class="c"&gt;# This method is *more specific* than the generic 'where {T}' version.&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; get_value&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Container&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="x"&gt;})&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Specific 'get_value(c::Container{String})' called!"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;uppercase&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# --- Script Execution ---&lt;/span&gt;

&lt;span class="c"&gt;# 5. Create instances&lt;/span&gt;
&lt;span class="n"&gt;c_int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Container&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;       &lt;span class="c"&gt;# Container{Int64}&lt;/span&gt;
&lt;span class="n"&gt;c_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Container&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hello"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;   &lt;span class="c"&gt;# Container{String}&lt;/span&gt;
&lt;span class="n"&gt;c_flt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Container&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;3.14&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;      &lt;span class="c"&gt;# Container{Float64}&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Calling generic methods ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;val_int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_value&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c_int&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Got value: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;val_int&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;val_flt&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;type_flt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_value_and_type&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c_flt&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Got value: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;val_flt&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;" | Got type: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;type_flt&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Calling specific method (dispatch) ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# 6. Julia's dispatch system will see that c_str is a Container{String}&lt;/span&gt;
&lt;span class="c"&gt;# and select the *most specific* method available.&lt;/span&gt;
&lt;span class="n"&gt;val_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_value&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c_str&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Got value: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;val_str&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script demonstrates how to write &lt;strong&gt;functions&lt;/strong&gt; that operate on the &lt;strong&gt;parametric types&lt;/strong&gt; we just defined. This is where parametric types and multiple dispatch combine to create Julia's high-performance, generic code.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Core Concept: &lt;code&gt;where {T}&lt;/code&gt;:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;where {T}&lt;/code&gt; syntax is the key. It's how you "get" the type parameter from an argument.&lt;/li&gt;
&lt;li&gt;In the signature &lt;code&gt;function get_value(c::Container{T})::T where {T}&lt;/code&gt;, we are telling Julia:

&lt;ol&gt;
&lt;li&gt; &lt;code&gt;c::Container{T}&lt;/code&gt;: "This function accepts a &lt;code&gt;Container&lt;/code&gt;, and I don't care what type it holds. Let's call that type &lt;code&gt;T&lt;/code&gt;."&lt;/li&gt;
&lt;li&gt; &lt;code&gt;where {T}&lt;/code&gt;: "Bind that unknown type &lt;code&gt;T&lt;/code&gt; to a variable named &lt;code&gt;T&lt;/code&gt; that I can use inside my function."&lt;/li&gt;
&lt;li&gt; &lt;code&gt;::T&lt;/code&gt;: "I promise that this function will return a value of that same type &lt;code&gt;T&lt;/code&gt;."&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;As shown in &lt;code&gt;get_value_and_type&lt;/code&gt;, the variable &lt;code&gt;T&lt;/code&gt; is a real value (a &lt;code&gt;DataType&lt;/code&gt; object) that you can inspect, return, or use.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Performance: Compile-Time Specialization:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This is &lt;strong&gt;not&lt;/strong&gt; like a generic &lt;code&gt;function(c::Container{Object})&lt;/code&gt; in Java. There is no runtime "unboxing."&lt;/li&gt;
&lt;li&gt;When you first call &lt;code&gt;get_value(c_int)&lt;/code&gt;, the compiler &lt;em&gt;sees&lt;/em&gt; that &lt;code&gt;T&lt;/code&gt; is &lt;code&gt;Int64&lt;/code&gt;. It then &lt;strong&gt;generates and compiles a new, specialized method&lt;/strong&gt; just for &lt;code&gt;Int64&lt;/code&gt;:&lt;/li&gt;
&lt;/ul&gt;



&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# This is what Julia effectively compiles:&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; get_value&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Container&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;Int64&lt;/span&gt;&lt;span class="x"&gt;})&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int64&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;



&lt;ul&gt;
&lt;li&gt;This specialized method is just as fast as if you had written it by hand. It knows &lt;code&gt;c.value&lt;/code&gt; is an &lt;code&gt;Int64&lt;/code&gt; and the return type is &lt;code&gt;Int64&lt;/code&gt;. There is &lt;strong&gt;zero abstraction cost&lt;/strong&gt;. A separate, fast version is also compiled for &lt;code&gt;Float64&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Dispatch: Generic vs. Specific:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This script shows how parametric methods interact with multiple dispatch. We have two methods for the &lt;code&gt;get_value&lt;/code&gt; function:

&lt;ol&gt;
&lt;li&gt; &lt;code&gt;get_value(c::Container{T}) where {T}&lt;/code&gt; (The generic "catch-all")&lt;/li&gt;
&lt;li&gt; &lt;code&gt;get_value(c::Container{String})&lt;/code&gt; (The specific "special case")&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;When we call &lt;code&gt;get_value(c_int)&lt;/code&gt;, the &lt;code&gt;Container{Int64}&lt;/code&gt; type does not match &lt;code&gt;Container{String}&lt;/code&gt;. It falls back to the generic &lt;code&gt;where {T}&lt;/code&gt; method, with &lt;code&gt;T&lt;/code&gt; becoming &lt;code&gt;Int64&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;When we call &lt;code&gt;get_value(c_str)&lt;/code&gt;, the &lt;code&gt;Container{String}&lt;/code&gt; type matches &lt;strong&gt;both&lt;/strong&gt; methods. Julia's dispatch system follows the rule: &lt;strong&gt;"always pick the most specific method."&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Since &lt;code&gt;Container{String}&lt;/code&gt; is &lt;em&gt;more specific&lt;/em&gt; than &lt;code&gt;Container{T}&lt;/code&gt;, the specialized string version is called, and we get the &lt;code&gt;uppercase&lt;/code&gt; behavior.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;References:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Methods", "Parametric Methods":&lt;/strong&gt; "Method definitions can be parameterized... When a function is called, the method with the most specific matching signature is invoked."&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0055_parametric_functions.jl
&lt;span class="nt"&gt;---&lt;/span&gt; Calling generic methods &lt;span class="nt"&gt;---&lt;/span&gt;
Generic &lt;span class="s1"&gt;'get_value(c::Container{T})'&lt;/span&gt; called, where T &lt;span class="o"&gt;=&lt;/span&gt; Int64
  Got value: 100
Function &lt;span class="s1"&gt;'get_value_and_type'&lt;/span&gt; called, where T &lt;span class="o"&gt;=&lt;/span&gt; Float64
  Got value: 3.14 | Got &lt;span class="nb"&gt;type&lt;/span&gt;: Float64

&lt;span class="nt"&gt;---&lt;/span&gt; Calling specific method &lt;span class="o"&gt;(&lt;/span&gt;dispatch&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
Specific &lt;span class="s1"&gt;'get_value(c::Container{String})'&lt;/span&gt; called!
  Got value: HELLO
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;0056_parametric_abstract.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0056_parametric_abstract.jl&lt;/span&gt;

&lt;span class="c"&gt;# 1. Define a 'parametric abstract type'.&lt;/span&gt;
&lt;span class="c"&gt;# This defines an interface for a *family* of generic types.&lt;/span&gt;
&lt;span class="c"&gt;# It's a contract: "Any subtype must also be parameterized by a type T."&lt;/span&gt;
&lt;span class="k"&gt;abstract type&lt;/span&gt;&lt;span class="nc"&gt; AbstractContainer&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="x"&gt;}&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 2. Define a concrete parametric struct that subtypes it.&lt;/span&gt;
&lt;span class="c"&gt;# We 'pass through' the type parameter T to the abstract type.&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="nc"&gt; ConcreteContainer&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="x"&gt;}&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;:&lt;/span&gt; &lt;span class="n"&gt;AbstractContainer&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="x"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 3. Define another concrete struct that *fixes* the type parameter.&lt;/span&gt;
&lt;span class="c"&gt;# This struct is *not* parametric itself, but it fulfills the&lt;/span&gt;
&lt;span class="c"&gt;# contract by subtyping a *specific* variant of the abstract type.&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="nc"&gt; StringContainer&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;:&lt;/span&gt; &lt;span class="n"&gt;AbstractContainer&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="x"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 4. Define a generic function that operates on the abstract interface.&lt;/span&gt;
&lt;span class="c"&gt;# This function will work on *any* type 'S' that is a subtype&lt;/span&gt;
&lt;span class="c"&gt;# of AbstractContainer{T}, 'where T' is some type.&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; get_abstract_value&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;S&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;:&lt;/span&gt; &lt;span class="n"&gt;AbstractContainer&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="x"&gt;}}&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Dispatching to generic AbstractContainer{T} method where T="&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# We can't access c.value because we don't know&lt;/span&gt;
    &lt;span class="c"&gt;# if the struct has a 'value' field (e.g., StringContainer)&lt;/span&gt;
    &lt;span class="c"&gt;# We just return the type parameter we found.&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 5. Define a more specific (but still abstract) method.&lt;/span&gt;
&lt;span class="c"&gt;# This will dispatch for *any* AbstractContainer that holds a 'String'.&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; process_text_container&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;AbstractContainer&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="x"&gt;})&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Dispatching to specific AbstractContainer{String} method."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# Here we still can't access c.value, but we know T is String.&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# --- Script Execution ---&lt;/span&gt;
&lt;span class="n"&gt;c_int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ConcreteContainer&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;      &lt;span class="c"&gt;# ConcreteContainer{Int64}&lt;/span&gt;
&lt;span class="n"&gt;c_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ConcreteContainer&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# ConcreteContainer{String}&lt;/span&gt;
&lt;span class="n"&gt;s_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;StringContainer&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ID"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Data"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# StringContainer&lt;/span&gt;

&lt;span class="c"&gt;# 6. Call the generic function&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Calling generic get_abstract_value ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;get_abstract_value&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c_int&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;get_abstract_value&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c_str&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;get_abstract_value&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s_str&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 7. Call the more specific function&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Calling specific process_text_container ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# process_text_container(c_int) # This would fail (MethodError)&lt;/span&gt;
&lt;span class="n"&gt;process_text_container&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c_str&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;process_text_container&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s_str&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 8. Check the type hierarchy&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Type hierarchy checks ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ConcreteContainer{Int64} &amp;lt;: AbstractContainer{Int64}?  "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ConcreteContainer&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;Int64&lt;/span&gt;&lt;span class="x"&gt;}&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;:&lt;/span&gt; &lt;span class="n"&gt;AbstractContainer&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;Int64&lt;/span&gt;&lt;span class="x"&gt;})&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"StringContainer &amp;lt;: AbstractContainer{String}?      "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;StringContainer&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;:&lt;/span&gt; &lt;span class="n"&gt;AbstractContainer&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="x"&gt;})&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"StringContainer &amp;lt;: AbstractContainer{Int64}?      "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;StringContainer&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;:&lt;/span&gt; &lt;span class="n"&gt;AbstractContainer&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;Int64&lt;/span&gt;&lt;span class="x"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script combines the two previous concepts—&lt;code&gt;abstract type&lt;/code&gt;s and &lt;code&gt;struct{T}&lt;/code&gt;s—to create &lt;strong&gt;parametric abstract types&lt;/strong&gt;. This is a powerful pattern for defining a generic "interface" for a whole family of types.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Core Concept:&lt;/strong&gt; An &lt;code&gt;abstract type AbstractContainer{T} end&lt;/code&gt; defines a contract for &lt;em&gt;generic&lt;/em&gt; containers. It says, "Any type that claims to be a subtype of me must also specify what &lt;code&gt;T&lt;/code&gt; it is."&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Fulfilling the Contract:&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1.  **`ConcreteContainer{T} &amp;lt;: AbstractContainer{T}`:** This is the most direct way. We create a new parametric `struct` and "pass through" the type parameter `T`. This says, "A `ConcreteContainer{Int}` **is a kind of** `AbstractContainer{Int}`."
2.  **`StringContainer &amp;lt;: AbstractContainer{String}`:** This is a more specialized way. The `StringContainer` *is not* generic (it only holds `String`s), but it fulfills the contract by declaring that it **is a kind of** `AbstractContainer{String}`.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Dispatching on Parametric Abstract Types:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The function &lt;code&gt;get_abstract_value&lt;/code&gt; shows the most generic form. Its signature &lt;code&gt;where {T, S &amp;lt;: AbstractContainer{T}}&lt;/code&gt; is the full, explicit way of saying: "I accept any type &lt;code&gt;S&lt;/code&gt;, as long as that type &lt;code&gt;S&lt;/code&gt; is a subtype of &lt;code&gt;AbstractContainer{T}&lt;/code&gt; for &lt;em&gt;some&lt;/em&gt; &lt;code&gt;T&lt;/code&gt;."&lt;/li&gt;
&lt;li&gt;The function &lt;code&gt;process_text_container(c::AbstractContainer{String})&lt;/code&gt; is much simpler. It accepts &lt;em&gt;any&lt;/em&gt; object whose type is a subtype of &lt;code&gt;AbstractContainer{String}&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;How Dispatch Works:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When we call &lt;code&gt;process_text_container(c_str)&lt;/code&gt;, Julia checks: Is &lt;code&gt;typeof(c_str)&lt;/code&gt; (which is &lt;code&gt;ConcreteContainer{String}&lt;/code&gt;) a subtype of &lt;code&gt;AbstractContainer{String}&lt;/code&gt;? The check is &lt;code&gt;true&lt;/code&gt;, so the call succeeds.&lt;/li&gt;
&lt;li&gt;When we call &lt;code&gt;process_text_container(s_str)&lt;/code&gt;, Julia checks: Is &lt;code&gt;typeof(s_str)&lt;/code&gt; (which is &lt;code&gt;StringContainer&lt;/code&gt;) a subtype of &lt;code&gt;AbstractContainer{String}&lt;/code&gt;? The check is &lt;code&gt;true&lt;/code&gt;, so the call succeeds.&lt;/li&gt;
&lt;li&gt;A call with &lt;code&gt;c_int&lt;/code&gt; (&lt;code&gt;ConcreteContainer{Int64}&lt;/code&gt;) would fail, because &lt;code&gt;ConcreteContainer{Int64}&lt;/code&gt; is &lt;strong&gt;not&lt;/strong&gt; a subtype of &lt;code&gt;AbstractContainer{String}&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Parametric Invariance:&lt;/strong&gt; This last point is critical. &lt;code&gt;ConcreteContainer{Int64}&lt;/code&gt; is &lt;strong&gt;not&lt;/strong&gt; related to &lt;code&gt;ConcreteContainer{String}&lt;/code&gt;. A generic type &lt;code&gt;Foo{T}&lt;/code&gt; is &lt;strong&gt;invariant&lt;/strong&gt; in its type parameter. This strictness is what allows the compiler to generate highly specialized, fast code, as it never has to guess what &lt;code&gt;T&lt;/code&gt; might be.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;References:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, Types, "Parametric Abstract Types":&lt;/strong&gt; "Parametric abstract types are a useful way to define a hierarchy of types on a common parametric structure."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Types", "Parametric Types" (on Invariance):&lt;/strong&gt; "A &lt;code&gt;Container{Int}&lt;/code&gt; is not a subtype of &lt;code&gt;Container{Number}&lt;/code&gt;, even though &lt;code&gt;Int &amp;lt;: Number&lt;/code&gt;."&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0056_parametric_abstract.jl
&lt;span class="nt"&gt;---&lt;/span&gt; Calling generic get_abstract_value &lt;span class="nt"&gt;---&lt;/span&gt;
Dispatching to generic AbstractContainer&lt;span class="o"&gt;{&lt;/span&gt;T&lt;span class="o"&gt;}&lt;/span&gt; method where &lt;span class="nv"&gt;T&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Int64
Dispatching to generic AbstractContainer&lt;span class="o"&gt;{&lt;/span&gt;T&lt;span class="o"&gt;}&lt;/span&gt; method where &lt;span class="nv"&gt;T&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;String
Dispatching to generic AbstractContainer&lt;span class="o"&gt;{&lt;/span&gt;T&lt;span class="o"&gt;}&lt;/span&gt; method where &lt;span class="nv"&gt;T&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;String

&lt;span class="nt"&gt;---&lt;/span&gt; Calling specific process_text_container &lt;span class="nt"&gt;---&lt;/span&gt;
Dispatching to specific AbstractContainer&lt;span class="o"&gt;{&lt;/span&gt;String&lt;span class="o"&gt;}&lt;/span&gt; method.
Dispatching to specific AbstractContainer&lt;span class="o"&gt;{&lt;/span&gt;String&lt;span class="o"&gt;}&lt;/span&gt; method.

&lt;span class="nt"&gt;---&lt;/span&gt; Type hierarchy checks &lt;span class="nt"&gt;---&lt;/span&gt;
ConcreteContainer&lt;span class="o"&gt;{&lt;/span&gt;Int64&lt;span class="o"&gt;}&lt;/span&gt; &amp;lt;: AbstractContainer&lt;span class="o"&gt;{&lt;/span&gt;Int64&lt;span class="o"&gt;}&lt;/span&gt;?  &lt;span class="nb"&gt;true
&lt;/span&gt;StringContainer &amp;lt;: AbstractContainer&lt;span class="o"&gt;{&lt;/span&gt;String&lt;span class="o"&gt;}&lt;/span&gt;?      &lt;span class="nb"&gt;true
&lt;/span&gt;StringContainer &amp;lt;: AbstractContainer&lt;span class="o"&gt;{&lt;/span&gt;Int64&lt;span class="o"&gt;}&lt;/span&gt;?      &lt;span class="nb"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Modules Code Organisation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;0057_module_basics.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0057_module_basics.jl&lt;/span&gt;

&lt;span class="c"&gt;# 1. Define a 'module' to create a new, separate namespace.&lt;/span&gt;
&lt;span class="c"&gt;# Modules are Julia's primary way to organize code into logical units&lt;/span&gt;
&lt;span class="c"&gt;# and prevent name collisions.&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="n"&gt;MyGeometry&lt;/span&gt;

&lt;span class="c"&gt;# 2. We can define types inside the module.&lt;/span&gt;
&lt;span class="k"&gt;abstract type&lt;/span&gt;&lt;span class="nc"&gt; AbstractShape&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="nc"&gt; Circle&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;:&lt;/span&gt; &lt;span class="n"&gt;AbstractShape&lt;/span&gt;
    &lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="nc"&gt; Rectangle&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;:&lt;/span&gt; &lt;span class="n"&gt;AbstractShape&lt;/span&gt;
    &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;
    &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 3. We can define functions inside the module.&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; calculate_area&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Circle&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;π&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; calculate_area&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Rectangle&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 4. We can define private helper functions.&lt;/span&gt;
&lt;span class="c"&gt;# By default, all names are "private" (not exported).&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; _helper_function&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"This is a private helper."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 5. We can define global constants.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;PI_Approximation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;3.14159&lt;/span&gt;

&lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="c"&gt;# --- End of module MyGeometry ---&lt;/span&gt;

&lt;span class="c"&gt;# 6. The module 'MyGeometry' now exists as a global object.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Accessing the module from 'Main' ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Type of MyGeometry: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MyGeometry&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;

&lt;span class="c"&gt;# 7. To access anything *inside* the module, we MUST use dot-notation.&lt;/span&gt;
&lt;span class="c"&gt;# This is called a "qualified name".&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Accessing constant: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MyGeometry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PI_Approximation&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 8. Create an instance of a type defined in the module.&lt;/span&gt;
&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MyGeometry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Circle&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;10.0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Created instance: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 9. Call a function defined in the module.&lt;/span&gt;
&lt;span class="n"&gt;area&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MyGeometry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;calculate_area&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Calculated area: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;area&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces &lt;strong&gt;&lt;code&gt;module&lt;/code&gt;s&lt;/strong&gt;, which are Julia's system for code organization, encapsulation, and namespace management. They are the direct equivalent of Python modules/packages, C++ namespaces, or Rust modules.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Core Concept: Namespace&lt;/strong&gt;&lt;br&gt;
A &lt;code&gt;module&lt;/code&gt; creates a new, isolated &lt;strong&gt;global scope&lt;/strong&gt;. Names defined inside &lt;code&gt;module MyGeometry ... end&lt;/code&gt; (like &lt;code&gt;Circle&lt;/code&gt; or &lt;code&gt;calculate_area&lt;/code&gt;) are completely separate from names defined outside (in the default &lt;code&gt;Main&lt;/code&gt; scope).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This is the primary tool for building large applications. It prevents you from accidentally overwriting a function from another library that has the same name. For example, &lt;code&gt;MyGeometry.calculate_area&lt;/code&gt; is a different function from &lt;code&gt;SomeOtherLibrary.calculate_area&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Accessing Module Contents: Dot Notation&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Once the &lt;code&gt;MyGeometry&lt;/code&gt; module is defined, it exists as a single object in the &lt;code&gt;Main&lt;/code&gt; (top-level) scope.&lt;/li&gt;
&lt;li&gt;To access any name &lt;em&gt;inside&lt;/em&gt; this module from the &lt;em&gt;outside&lt;/em&gt;, you &lt;strong&gt;must&lt;/strong&gt; use a &lt;strong&gt;qualified name&lt;/strong&gt; with dot notation.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;MyGeometry.Circle&lt;/code&gt; refers to the &lt;code&gt;Circle&lt;/code&gt; &lt;code&gt;struct&lt;/code&gt; defined inside &lt;code&gt;MyGeometry&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;MyGeometry.calculate_area(c)&lt;/code&gt; refers to the &lt;code&gt;calculate_area&lt;/code&gt; function inside &lt;code&gt;MyGeometry&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Encapsulation (Privacy)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;By default, all names defined inside a module are "private" in the sense that they are not exported. You can &lt;em&gt;always&lt;/em&gt; access them with the dot notation (e.g., &lt;code&gt;MyGeometry._helper_function()&lt;/code&gt;), so it's not "true" privacy like in C++.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;export&lt;/code&gt; keyword (covered in a later lesson) is used to &lt;em&gt;publicly&lt;/em&gt; list which names are intended for users, allowing them to be brought into scope with &lt;code&gt;using&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The convention is that names beginning with an underscore (e.g., &lt;code&gt;_helper_function&lt;/code&gt;) are considered internal to the module and should not be used by external code, even though it's technically possible.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Modules and Files&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This example shows a module defined in the &lt;em&gt;same file&lt;/em&gt; it's used in.&lt;/li&gt;
&lt;li&gt;The more common pattern is to put &lt;code&gt;module MyGeometry ... end&lt;/code&gt; in its own file (e.g., &lt;code&gt;MyGeometry.jl&lt;/code&gt;) and then load it into another file using &lt;code&gt;include("MyGeometry.jl")&lt;/code&gt;. This will be the subject of the next lesson.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;References:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Modules":&lt;/strong&gt; "Modules are separate global variable workspaces... This prevents unrelated code from accidentally clobbering one another's global variables."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Modules":&lt;/strong&gt; "A module is a new global scope... code in one module cannot directly access a global variable in another module."&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0057_module_basics.jl
&lt;span class="nt"&gt;---&lt;/span&gt; Accessing the module from &lt;span class="s1"&gt;'Main'&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
Type of MyGeometry: Module

Accessing constant: 3.14159
Created instance: MyGeometry.Circle&lt;span class="o"&gt;(&lt;/span&gt;10.0&lt;span class="o"&gt;)&lt;/span&gt;
Calculated area: 314.1592653589793
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;em&gt;This lesson requires you to first create a new file, &lt;code&gt;MyGeometry.jl&lt;/code&gt;, containing the module from the previous lesson.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  File 1: &lt;code&gt;MyGeometry.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# MyGeometry.jl&lt;/span&gt;
&lt;span class="c"&gt;# This file contains our module definition.&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="n"&gt;MyGeometry&lt;/span&gt;

&lt;span class="c"&gt;# 1. Define types&lt;/span&gt;
&lt;span class="k"&gt;abstract type&lt;/span&gt;&lt;span class="nc"&gt; AbstractShape&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="nc"&gt; Circle&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;:&lt;/span&gt; &lt;span class="n"&gt;AbstractShape&lt;/span&gt;
    &lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="nc"&gt; Rectangle&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;:&lt;/span&gt; &lt;span class="n"&gt;AbstractShape&lt;/span&gt;
    &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;
    &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 2. Define functions&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; calculate_area&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Circle&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;π&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; calculate_area&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Rectangle&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 3. Define a "private" helper&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; _helper_function&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"This is a private helper."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 4. Define a constant&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;PI_Approximation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;3.14159&lt;/span&gt;

&lt;span class="c"&gt;# We will add 'export' in a later lesson.&lt;/span&gt;
&lt;span class="c"&gt;# For now, nothing is exported.&lt;/span&gt;

&lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="c"&gt;# --- End of module MyGeometry ---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  File 2: &lt;code&gt;0058_module_access.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0058_module_access.jl&lt;/span&gt;

&lt;span class="c"&gt;# 1. 'include()' parses and executes the contents of the file.&lt;/span&gt;
&lt;span class="c"&gt;# This is like copy-pasting 'MyGeometry.jl' right here.&lt;/span&gt;
&lt;span class="c"&gt;# This line finds the file, runs it, and the 'MyGeometry'&lt;/span&gt;
&lt;span class="c"&gt;# module becomes defined in our 'Main' global scope.&lt;/span&gt;
&lt;span class="n"&gt;include&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"MyGeometry.jl"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 2. We can now access the module, just as before.&lt;/span&gt;
&lt;span class="c"&gt;# We MUST use the qualified name (dot-notation).&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Accessing module from separate file ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MyGeometry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Circle&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;5.0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;area&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MyGeometry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;calculate_area&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Created instance: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Calculated area: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;area&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 3. The namespace 'Main' is *not* polluted.&lt;/span&gt;
&lt;span class="c"&gt;# The name 'Circle' only exists *inside* MyGeometry.&lt;/span&gt;
&lt;span class="c"&gt;# This line will fail, as 'Circle' is not defined in 'Main'.&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="n"&gt;c_fail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Circle&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Caught expected error:"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script demonstrates the standard way to load a module from a separate file using &lt;code&gt;include()&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Core Concept: &lt;code&gt;include()&lt;/code&gt;:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;include(path)&lt;/code&gt; function is a simple, direct command. It tells Julia to "pause execution of this file, go read the file at &lt;code&gt;path&lt;/code&gt;, execute all the code in it from top to bottom, and then come back and continue."&lt;/li&gt;
&lt;li&gt;It is &lt;strong&gt;equivalent to textual copy-pasting&lt;/strong&gt;. After the &lt;code&gt;include("MyGeometry.jl")&lt;/code&gt; line, our script behaves &lt;em&gt;exactly&lt;/em&gt; as if the entire &lt;code&gt;module MyGeometry ... end&lt;/code&gt; block was written at that spot.&lt;/li&gt;
&lt;li&gt;This is the primary mechanism for splitting a large program into multiple files.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Namespace is Still Separate:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A common mistake is to assume &lt;code&gt;include()&lt;/code&gt; "imports" the names from the module. It does not.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;include()&lt;/code&gt; simply runs the file. The file's code defines a &lt;em&gt;single&lt;/em&gt; new name in our &lt;code&gt;Main&lt;/code&gt; scope: the module object &lt;code&gt;MyGeometry&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;All the other names (&lt;code&gt;Circle&lt;/code&gt;, &lt;code&gt;Rectangle&lt;/code&gt;, &lt;code&gt;calculate_area&lt;/code&gt;) still exist &lt;strong&gt;only inside&lt;/strong&gt; the &lt;code&gt;MyGeometry&lt;/code&gt; namespace.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;try...catch&lt;/code&gt; block proves this. Attempting to access &lt;code&gt;Circle&lt;/code&gt; directly fails with a &lt;code&gt;MethodError&lt;/code&gt; (or &lt;code&gt;UndefVarError&lt;/code&gt;) because the name &lt;code&gt;Circle&lt;/code&gt; does not exist in &lt;code&gt;Main&lt;/code&gt;. You &lt;em&gt;must&lt;/em&gt; still use the fully qualified name: &lt;code&gt;MyGeometry.Circle&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;include&lt;/code&gt; vs. &lt;code&gt;using&lt;/code&gt;/&lt;code&gt;import&lt;/code&gt;:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;include(filename)&lt;/code&gt;: This is how you &lt;strong&gt;load code from a file&lt;/strong&gt;. You do this &lt;em&gt;once&lt;/em&gt; per file.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;using ModuleName&lt;/code&gt; / &lt;code&gt;import ModuleName&lt;/code&gt;: This is how you &lt;strong&gt;bring names from an &lt;em&gt;already-loaded&lt;/em&gt; module&lt;/strong&gt; into your current namespace. This is the subject of the next lesson.&lt;/li&gt;
&lt;li&gt;The standard pattern is:

&lt;ol&gt;
&lt;li&gt; &lt;code&gt;include("MyGeometry.jl")&lt;/code&gt; (to load the code and create the module)&lt;/li&gt;
&lt;li&gt; &lt;code&gt;using .MyGeometry&lt;/code&gt; (to make its exported names available)&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;References:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Modules":&lt;/strong&gt; "Files are included using the &lt;code&gt;include&lt;/code&gt; function... The &lt;code&gt;include&lt;/code&gt; function evaluates the contents of a source file in the context of the &lt;em&gt;calling&lt;/em&gt; module."&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;To run the script:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(You must have &lt;code&gt;MyGeometry.jl&lt;/code&gt; in the same directory)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0058_module_access.jl
&lt;span class="nt"&gt;---&lt;/span&gt; Accessing module from separate file &lt;span class="nt"&gt;---&lt;/span&gt;
Created instance: MyGeometry.Circle&lt;span class="o"&gt;(&lt;/span&gt;5.0&lt;span class="o"&gt;)&lt;/span&gt;
Calculated area: 78.53981633974483

Caught expected error:
UndefVarError: &lt;span class="sb"&gt;`&lt;/span&gt;Circle&lt;span class="sb"&gt;`&lt;/span&gt; not defined
&lt;span class="o"&gt;[&lt;/span&gt;...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Using Vs Import
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;0059_using_vs_import.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0059_using_vs_import.jl&lt;/span&gt;

&lt;span class="c"&gt;# 1. First, we MUST load the code from the file.&lt;/span&gt;
&lt;span class="c"&gt;# 'include' executes the file, defining the 'MyGeometry' module&lt;/span&gt;
&lt;span class="c"&gt;# in our current (Main) scope.&lt;/span&gt;
&lt;span class="n"&gt;include&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"MyGeometry.jl"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# We will now explore the three different ways to access&lt;/span&gt;
&lt;span class="c"&gt;# the contents of the *already-loaded* 'MyGeometry' module.&lt;/span&gt;

&lt;span class="c"&gt;# --- Method 1 (Recommended): Full Qualification ---&lt;/span&gt;
&lt;span class="c"&gt;# We do nothing special, and just use the fully qualified name.&lt;/span&gt;
&lt;span class="c"&gt;# This is what we did in the previous lesson.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Method 1: Full Qualification ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;c1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MyGeometry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Circle&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Created: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c1&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Area:    "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MyGeometry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;calculate_area&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c1&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;


&lt;span class="c"&gt;# --- Method 2 (Safe &amp;amp; Explicit): 'import .MyGeometry: Name, ...' ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Method 2: import .MyGeometry: Circle ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# The '.' is critical. It tells Julia to look for 'MyGeometry'&lt;/span&gt;
&lt;span class="c"&gt;# *relative* to our current module (Main), not in the list&lt;/span&gt;
&lt;span class="c"&gt;# of installed packages.&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MyGeometry&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Circle&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;calculate_area&lt;/span&gt;

&lt;span class="c"&gt;# Now we can call 'Circle' and 'calculate_area' directly.&lt;/span&gt;
&lt;span class="n"&gt;c2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Circle&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# This is MyGeometry.Circle&lt;/span&gt;
&lt;span class="n"&gt;area2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;calculate_area&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c2&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# This is MyGeometry.calculate_area&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Created: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c2&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Area:    "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;area2&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# However, 'Rectangle' was *not* imported. We must still qualify it.&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="n"&gt;r_fail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Rectangle&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Caught expected error: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="c"&gt;# This is the correct, qualified way:&lt;/span&gt;
&lt;span class="n"&gt;r_ok&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MyGeometry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Rectangle&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Created Rectangle via qualified name: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r_ok&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;


&lt;span class="c"&gt;# --- Method 3 (Discouraged): 'using .MyGeometry' ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Method 3: using .MyGeometry ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# NEVER EVER DO THIS. DON'T EVEN TRY.&lt;/span&gt;
&lt;span class="c"&gt;# There are cosmic forces at play here, who sense everying time&lt;/span&gt;
&lt;span class="c"&gt;# you use 'using'. You do not want to incur their wrath.&lt;/span&gt;
&lt;span class="c"&gt;# Stay away from importing the entire namespace into the global scope.&lt;/span&gt;
&lt;span class="c"&gt;# Just don't do it.&lt;/span&gt;
&lt;span class="c"&gt;# It's not worth it.&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MyGeometry&lt;/span&gt;

&lt;span class="c"&gt;# But since we didn't 'export' anything, we aren't bringing anything into&lt;/span&gt;
&lt;span class="c"&gt;# scope&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="c"&gt;# This fails, because 'Rectangle' was not exported.&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Rectangle&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;3.0&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;3.0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Caught expected error: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# We *still* have to use the qualified name.&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MyGeometry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Rectangle&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;3.0&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;3.0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Must still use qualified name: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script demonstrates the critical differences between &lt;code&gt;import&lt;/code&gt; and &lt;code&gt;using&lt;/code&gt; for controlling how names from a module are accessed. A clean, explicit namespace is a key component of robust, maintainable systems.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Step 0: &lt;code&gt;include()&lt;/code&gt; and &lt;code&gt;.&lt;/code&gt; Syntax&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First, we &lt;em&gt;must&lt;/em&gt; call &lt;code&gt;include("MyGeometry.jl")&lt;/code&gt;. This is the &lt;strong&gt;loader&lt;/strong&gt;. It executes the file, which defines the &lt;code&gt;MyGeometry&lt;/code&gt; module object inside our current module (which is &lt;code&gt;Main&lt;/code&gt; by default).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The &lt;code&gt;.&lt;/code&gt; Prefix:&lt;/strong&gt; When we write &lt;code&gt;import MyGeometry&lt;/code&gt;, Julia assumes we mean an &lt;em&gt;installed package&lt;/em&gt; from our environment. This fails. The &lt;code&gt;.&lt;/code&gt; prefix in &lt;code&gt;import .MyGeometry&lt;/code&gt; is critical: it makes the path &lt;strong&gt;relative&lt;/strong&gt;. It tells Julia, "Look for a module named &lt;code&gt;MyGeometry&lt;/code&gt; that is &lt;em&gt;already loaded inside my current module&lt;/em&gt;." This is the correct way to refer to modules you have loaded with &lt;code&gt;include&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Method 1: Full Qualification (Safest)&lt;/strong&gt;&lt;br&gt;
This is the simplest, safest, and most explicit method. You use the full &lt;code&gt;MyGeometry.Circle&lt;/code&gt; and &lt;code&gt;MyGeometry.calculate_area&lt;/code&gt; names.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pro:&lt;/strong&gt; It is 100% clear where &lt;code&gt;Circle&lt;/code&gt; and &lt;code&gt;calculate_area&lt;/code&gt; are defined. There is zero chance of a name collision.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Con:&lt;/strong&gt; It can be verbose.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Method 2: &lt;code&gt;import .MyGeometry: Name&lt;/code&gt; (Recommended)&lt;/strong&gt;&lt;br&gt;
This is the recommended pattern for balancing clarity and convenience.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;import .MyGeometry: Circle, calculate_area&lt;/code&gt; states, "From the &lt;code&gt;MyGeometry&lt;/code&gt; module in my current scope, bring &lt;em&gt;only&lt;/em&gt; the &lt;code&gt;Circle&lt;/code&gt; and &lt;code&gt;calculate_area&lt;/code&gt; names into my namespace."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pro:&lt;/strong&gt; It is still explicit. A developer reading the top of the file sees a precise list of imported names. You can use &lt;code&gt;Circle&lt;/code&gt; directly, but &lt;code&gt;Rectangle&lt;/code&gt; (which we didn't import) still requires &lt;code&gt;MyGeometry.Rectangle&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Con:&lt;/strong&gt; You have to list every name you want to use.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Method 3: &lt;code&gt;using .MyGeometry&lt;/code&gt; (Strongly Discouraged)&lt;/strong&gt;&lt;br&gt;
This command is the most "magical" and the most likely to cause problems in large projects.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;using&lt;/code&gt; vs. &lt;code&gt;export&lt;/code&gt;:&lt;/strong&gt; &lt;code&gt;using .MyGeometry&lt;/code&gt; tells Julia, "Find all names that &lt;code&gt;MyGeometry&lt;/code&gt; has &lt;em&gt;publicly exported&lt;/em&gt; and dump them into my current scope." Our &lt;code&gt;MyGeometry.jl&lt;/code&gt; file does not contain an &lt;code&gt;export&lt;/code&gt; statement yet, so it exports &lt;em&gt;nothing&lt;/em&gt;. This is why &lt;code&gt;using .MyGeometry&lt;/code&gt; does not make &lt;code&gt;Rectangle&lt;/code&gt; available.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The "Namespace Pollution" Problem:&lt;/strong&gt; Even if our module &lt;em&gt;did&lt;/em&gt; export &lt;code&gt;Rectangle&lt;/code&gt;, &lt;code&gt;using .MyGeometry&lt;/code&gt; is discouraged. If you have ten &lt;code&gt;using&lt;/code&gt; statements at the top of your file and you see the name &lt;code&gt;Rectangle()&lt;/code&gt; in your code, you have no way of knowing which of those ten modules it came from. This is "namespace pollution."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Guideline:&lt;/strong&gt; Avoid &lt;code&gt;using&lt;/code&gt;. It makes code harder to read and debug by obscuring the origin of names. The explicit &lt;code&gt;import .MyGeometry: ...&lt;/code&gt; or fully qualified &lt;code&gt;MyGeometry.Rectangle&lt;/code&gt; are strongly preferred for writing clear, maintainable, and unambiguous code.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;References:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Modules":&lt;/strong&gt; "The &lt;code&gt;import ... :&lt;/code&gt; syntax allows importing specific names from a module... The &lt;code&gt;using&lt;/code&gt; keyword... brings &lt;em&gt;all exported&lt;/em&gt; names from a module into the current scope."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Code Loading":&lt;/strong&gt; Explains relative imports: "A &lt;code&gt;using&lt;/code&gt; or &lt;code&gt;import&lt;/code&gt; statement with a leading dot (&lt;code&gt;.&lt;/code&gt;) is a &lt;em&gt;relative&lt;/em&gt; import."&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To run the script:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(You must have &lt;code&gt;MyGeometry.jl&lt;/code&gt; from lesson 0058 in the same directory)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0059_using_vs_import.jl
&lt;span class="nt"&gt;---&lt;/span&gt; Method 1: Full Qualification &lt;span class="nt"&gt;---&lt;/span&gt;
  Created: MyGeometry.Circle&lt;span class="o"&gt;(&lt;/span&gt;1.0&lt;span class="o"&gt;)&lt;/span&gt;
  Area:    3.141592653589793

&lt;span class="nt"&gt;---&lt;/span&gt; Method 2: import .MyGeometry: Circle &lt;span class="nt"&gt;---&lt;/span&gt;
  Created: Circle&lt;span class="o"&gt;(&lt;/span&gt;2.0&lt;span class="o"&gt;)&lt;/span&gt;
  Area:    12.566370614359172
  Caught expected error: UndefVarError: &lt;span class="sb"&gt;`&lt;/span&gt;Rectangle&lt;span class="sb"&gt;`&lt;/span&gt; not defined
  Created Rectangle via qualified name: MyGeometry.Rectangle&lt;span class="o"&gt;(&lt;/span&gt;1.0, 1.0&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nt"&gt;---&lt;/span&gt; Method 3: using .MyGeometry &lt;span class="nt"&gt;---&lt;/span&gt;
  Caught expected error: UndefVarError: &lt;span class="sb"&gt;`&lt;/span&gt;Rectangle&lt;span class="sb"&gt;`&lt;/span&gt; not defined
  Must still use qualified name: MyGeometry.Rectangle&lt;span class="o"&gt;(&lt;/span&gt;3.0, 3.0&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;em&gt;This lesson requires a new module file, &lt;code&gt;MyGeometry2.jl&lt;/code&gt;, to demonstrate the &lt;code&gt;export&lt;/code&gt; keyword.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  File 1: &lt;code&gt;MyGeometry2.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# MyGeometry2.jl&lt;/span&gt;
&lt;span class="c"&gt;# This file defines a module that uses the 'export' keyword.&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="n"&gt;MyGeometry2&lt;/span&gt;

&lt;span class="c"&gt;# 1. 'export' lists the names that are considered the "public API"&lt;/span&gt;
&lt;span class="c"&gt;#    of this module. These are the names that 'using .MyGeometry2'&lt;/span&gt;
&lt;span class="c"&gt;#    will bring into the main namespace.&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="n"&gt;AbstractShape&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Circle&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Rectangle&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;calculate_area&lt;/span&gt;

&lt;span class="c"&gt;# 2. Define types&lt;/span&gt;
&lt;span class="k"&gt;abstract type&lt;/span&gt;&lt;span class="nc"&gt; AbstractShape&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="nc"&gt; Circle&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;:&lt;/span&gt; &lt;span class="n"&gt;AbstractShape&lt;/span&gt;
    &lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="nc"&gt; Rectangle&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;:&lt;/span&gt; &lt;span class="n"&gt;AbstractShape&lt;/span&gt;
    &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;
    &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 3. Define functions&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; calculate_area&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Circle&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;π&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; calculate_area&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Rectangle&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 4. This helper function is *NOT* exported.&lt;/span&gt;
&lt;span class="c"&gt;# It is "private" and can only be accessed via&lt;/span&gt;
&lt;span class="c"&gt;# the qualified name 'MyGeometry2._helper_function()'.&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; _helper_function&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"This is a private helper."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="c"&gt;# --- End of module MyGeometry2 ---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  File 2: &lt;code&gt;0060_export.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0060_export.jl&lt;/span&gt;

&lt;span class="c"&gt;# 1. Load the new module file.&lt;/span&gt;
&lt;span class="n"&gt;include&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"MyGeometry2.jl"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 2. Demonstrate 'using .MyGeometry2'&lt;/span&gt;
&lt;span class="c"&gt;# Because MyGeometry2.jl *uses* 'export', this command&lt;/span&gt;
&lt;span class="c"&gt;# now dumps all exported names into our 'Main' scope.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Demonstrating 'using .MyGeometry2' ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MyGeometry2&lt;/span&gt;

&lt;span class="c"&gt;# 3. We can now access the *exported* names directly.&lt;/span&gt;
&lt;span class="c"&gt;# This is "namespace pollution" - it's unclear where&lt;/span&gt;
&lt;span class="c"&gt;# 'Circle' and 'calculate_area' are coming from.&lt;/span&gt;
&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Circle&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;10.0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;area&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;calculate_area&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Created instance: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Calculated area: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;area&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 4. The *non-exported* name '_helper_function' is not in scope.&lt;/span&gt;
&lt;span class="c"&gt;# This correctly fails.&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="n"&gt;_helper_function&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;  Caught expected error (not exported): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 5. We can still access the non-exported name *with qualification*.&lt;/span&gt;
&lt;span class="c"&gt;# 'export' only controls 'using'; it does not prevent&lt;/span&gt;
&lt;span class="c"&gt;# direct, qualified access.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Calling private function with qualification:"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;MyGeometry2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_helper_function&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script completes our module lessons by introducing the &lt;strong&gt;&lt;code&gt;export&lt;/code&gt;&lt;/strong&gt; keyword, which creates a module's "public API."&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Core Concept: &lt;code&gt;export&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
The &lt;code&gt;export&lt;/code&gt; keyword specifies a list of names that are intended for public use. It works hand-in-hand with &lt;code&gt;using&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;export Circle, calculate_area&lt;/code&gt; says: "If a user writes &lt;code&gt;using .MyGeometry2&lt;/code&gt;, I give them permission to pull &lt;code&gt;Circle&lt;/code&gt; and &lt;code&gt;calculate_area&lt;/code&gt; into their namespace."&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;_helper_function&lt;/code&gt; was &lt;strong&gt;not&lt;/strong&gt; in the &lt;code&gt;export&lt;/code&gt; list, so &lt;code&gt;using .MyGeometry2&lt;/code&gt; does &lt;strong&gt;not&lt;/strong&gt; bring it into the namespace.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;using&lt;/code&gt; Re-examined (The "Polluting" Behavior)&lt;/strong&gt;&lt;br&gt;
As this lesson shows, &lt;code&gt;using .MyGeometry2&lt;/code&gt; now "works." It finds the &lt;code&gt;export&lt;/code&gt; list and defines &lt;code&gt;Circle&lt;/code&gt;, &lt;code&gt;Rectangle&lt;/code&gt;, &lt;code&gt;AbstractShape&lt;/code&gt;, and &lt;code&gt;calculate_area&lt;/code&gt; in our &lt;code&gt;Main&lt;/code&gt; scope.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Problem:&lt;/strong&gt; While this is convenient for small scripts, it is &lt;strong&gt;strongly discouraged&lt;/strong&gt; in any serious project. When you read the line &lt;code&gt;c = Circle(10.0)&lt;/code&gt;, you have no immediate, local information to tell you &lt;em&gt;which&lt;/em&gt; module defined &lt;code&gt;Circle&lt;/code&gt;. If you have ten &lt;code&gt;using&lt;/code&gt; statements, you would have to check all ten modules to find its origin.&lt;/li&gt;
&lt;li&gt;This is known as &lt;strong&gt;namespace pollution&lt;/strong&gt;, and it makes code difficult to read, debug, and maintain.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;export&lt;/code&gt; Does Not Mean "Private"&lt;/strong&gt;&lt;br&gt;
A critical, final point: &lt;code&gt;export&lt;/code&gt; does not enforce privacy. As shown in step 5, you can &lt;em&gt;always&lt;/em&gt; access any name inside a module using the fully qualified &lt;code&gt;MyGeometry2._helper_function()&lt;/code&gt; syntax.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;export&lt;/code&gt; is not a security feature; it is a &lt;strong&gt;namespace management&lt;/strong&gt; feature. It's a "politeness" contract that allows &lt;code&gt;using&lt;/code&gt; to be convenient, but it doesn't (and shouldn't) stop a determined user from accessing internal functions.&lt;/li&gt;
&lt;li&gt;The underscore prefix (e.g., &lt;code&gt;_helper_function&lt;/code&gt;) is the &lt;em&gt;real&lt;/em&gt; "do not touch" signal to other developers.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Final Guideline:&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1.  **Full Qualification:** `MyGeometry2.Circle(10.0)` is the clearest and safest method.
2.  **Explicit Import:** `import .MyGeometry2: Circle` is the best compromise.
3.  **`using` (and `export`):** Avoid this pattern in favor of the first two. It is better to be explicit about where your names come from.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;References:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Modules":&lt;/strong&gt; "&lt;code&gt;export&lt;/code&gt; specifies which names a module provides for other modules to use... When &lt;code&gt;using M&lt;/code&gt;, only the names exported by &lt;code&gt;M&lt;/code&gt; are brought into scope."&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To run the script:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(You must have &lt;code&gt;MyGeometry2.jl&lt;/code&gt; from this lesson in the same directory)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0060_export.jl
&lt;span class="nt"&gt;---&lt;/span&gt; Demonstrating &lt;span class="s1"&gt;'using .MyGeometry2'&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
  Created instance: Circle&lt;span class="o"&gt;(&lt;/span&gt;10.0&lt;span class="o"&gt;)&lt;/span&gt;
  Calculated area: 314.1592653589793

  Caught expected error &lt;span class="o"&gt;(&lt;/span&gt;not exported&lt;span class="o"&gt;)&lt;/span&gt;: UndefVarError: &lt;span class="sb"&gt;`&lt;/span&gt;_helper_function&lt;span class="sb"&gt;`&lt;/span&gt; not defined
  Calling private &lt;span class="k"&gt;function &lt;/span&gt;with qualification:
This is a private helper.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Module 6: High-Performance Techniques
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Type Stability And Diagnosis
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;0061_type_stability_intro.md&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This is the &lt;strong&gt;single most important concept&lt;/strong&gt; for writing high-performance Julia code.&lt;/p&gt;




&lt;h3&gt;
  
  
  What is Type Stability?
&lt;/h3&gt;

&lt;p&gt;A function is &lt;strong&gt;type-stable&lt;/strong&gt; if the &lt;strong&gt;type of its output&lt;/strong&gt; can be inferred &lt;em&gt;by the compiler&lt;/em&gt; purely from the &lt;strong&gt;types of its inputs&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Type-Stable (Fast):&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;function add_one(x::Int64) ... end&lt;/code&gt;&lt;br&gt;
The compiler knows: "If I put an &lt;code&gt;Int64&lt;/code&gt; in, I will &lt;em&gt;always&lt;/em&gt; get an &lt;code&gt;Int64&lt;/code&gt; out." It can generate specialized, fast machine code for this specific case.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Type-Unstable (Slow):&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;function parse_number(s::String) ... end&lt;/code&gt;&lt;br&gt;
The compiler does not know what this function will return. If &lt;code&gt;s&lt;/code&gt; is &lt;code&gt;"1"&lt;/code&gt;, it might return an &lt;code&gt;Int&lt;/code&gt;. If &lt;code&gt;s&lt;/code&gt; is &lt;code&gt;"1.0"&lt;/code&gt;, it might return a &lt;code&gt;Float64&lt;/code&gt;. The output type is &lt;em&gt;unknowable&lt;/em&gt; from the input type.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Why is This the Key to Performance?
&lt;/h3&gt;

&lt;p&gt;Julia's performance comes from its Just-In-Time (JIT) compiler, which specializes and compiles code &lt;em&gt;for the specific types&lt;/em&gt; it sees at runtime. &lt;strong&gt;Type-stability is what allows this specialization to happen.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Consider this function call: &lt;code&gt;my_func(x)&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  1. The Fast Path (Type-Stable)
&lt;/h4&gt;

&lt;p&gt;If &lt;code&gt;my_func&lt;/code&gt; is type-stable, the compiler knows the &lt;em&gt;exact&lt;/em&gt; type of its return value. This allows it to generate hyper-optimized machine code:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Specialization:&lt;/strong&gt; The compiler generates a version of the function &lt;code&gt;my_func_Int64&lt;/code&gt; that &lt;em&gt;only&lt;/em&gt; works on &lt;code&gt;Int&lt;/code&gt;s.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;No Type-Checking:&lt;/strong&gt; Inside this specialized function, it doesn't need to check the type of &lt;code&gt;x&lt;/code&gt;. It &lt;em&gt;knows&lt;/em&gt; &lt;code&gt;x&lt;/code&gt; is an &lt;code&gt;Int64&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Static Dispatch:&lt;/strong&gt; When &lt;code&gt;my_func&lt;/code&gt; calls another function, like &lt;code&gt;x + 1&lt;/code&gt;, the compiler knows this is &lt;code&gt;Int64 + Int64&lt;/code&gt; and can emit the &lt;em&gt;single machine instruction&lt;/em&gt; for integer addition (&lt;code&gt;addq&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Inlining:&lt;/strong&gt; The compiler can "inline" the function, essentially copy-pasting its machine code directly into the code that called it, eliminating all function call overhead.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The result is machine code that is identical in speed to C or Fortran.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. The Slow Path (Type-Unstable)
&lt;/h4&gt;

&lt;p&gt;If &lt;code&gt;my_func&lt;/code&gt; is type-unstable, the compiler &lt;strong&gt;cannot&lt;/strong&gt; know the type of its return value. This forces it to generate slow, generic, "fallback" code:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;No Specialization:&lt;/strong&gt; The compiler cannot create a specialized version because it doesn't know what types to specialize for.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Runtime Type-Checking:&lt;/strong&gt; When &lt;code&gt;my_func&lt;/code&gt; returns, the code that called it must &lt;em&gt;check the type&lt;/em&gt; of the returned value at runtime: "Did I get an &lt;code&gt;Int&lt;/code&gt;? Or a &lt;code&gt;Float64&lt;/code&gt;? Or a &lt;code&gt;String&lt;/code&gt;?"&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Dynamic Dispatch:&lt;/strong&gt; When this unstable value is used (e.g., &lt;code&gt;result + 1&lt;/code&gt;), the program must &lt;em&gt;at runtime&lt;/em&gt; look up the correct method. "I have a &lt;code&gt;result&lt;/code&gt;... what is its type? OK, it's a &lt;code&gt;Float64&lt;/code&gt;. Now, where is the function for &lt;code&gt;Float64 + Int64&lt;/code&gt;? OK, call that." This lookup is called &lt;strong&gt;dynamic dispatch&lt;/strong&gt; and it is orders of magnitude slower than a direct static call.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Boxing:&lt;/strong&gt; The compiler must "box" the value in a generic container that holds both the data and a &lt;em&gt;pointer to its type information&lt;/em&gt;. This creates heap allocations and adds pointer-chasing overhead.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Analogy:&lt;/strong&gt; A type-stable function is like a pre-plumbed pipe. An &lt;code&gt;Int64&lt;/code&gt; flows in one end, and the compiler knows an &lt;code&gt;Int64&lt;/code&gt; will come out the other. A type-unstable function is a pipe that ends in a "magic box," and you have no idea what will come out until it does.&lt;/p&gt;

&lt;p&gt;In the next lessons, we will learn to use the &lt;code&gt;@code_warntype&lt;/code&gt; macro, our primary tool for &lt;em&gt;diagnosing&lt;/em&gt; type instability.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Performance Tips":&lt;/strong&gt; "Write 'type-stable' functions." (This is the #1 performance tip).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Performance Tips":&lt;/strong&gt; "Avoid changing the type of a variable. When the type of a variable changes, the compiler may not be able to specialize... This is known as 'type-instability'."&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;0062_type_stable_function.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0062_type_stable_function.jl&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;InteractiveUtils&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nd"&gt;@code_warntype&lt;/span&gt;

&lt;span class="c"&gt;# 1. A function that is type-stable.&lt;/span&gt;
&lt;span class="c"&gt;# The compiler can infer 100% of the types.&lt;/span&gt;
&lt;span class="c"&gt;# Input 'Int64' -&amp;gt; Output 'Int64'&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; add_one_stable&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int64&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 2. A function that is also type-stable.&lt;/span&gt;
&lt;span class="c"&gt;# Input 'Float64' -&amp;gt; Output 'Float64'&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; add_one_stable_float&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# The '1.0' literal ensures the result is a Float64&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 3. A generic, but still type-stable, function.&lt;/span&gt;
&lt;span class="c"&gt;# The compiler knows: Input 'T' -&amp;gt; Output 'T' (where T is a Number)&lt;/span&gt;
&lt;span class="c"&gt;# It will compile a *specialized* version for each type.&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; add_one_generic&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;:&lt;/span&gt;&lt;span class="kt"&gt;Number&lt;/span&gt;&lt;span class="x"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;one&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# 'one(T)' returns 1 as type T&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 4. Use the @code_warntype macro to inspect the compiler's&lt;/span&gt;
&lt;span class="c"&gt;# type inference. This is our primary diagnostic tool.&lt;/span&gt;
&lt;span class="c"&gt;# We must 'execute' the macro in a function (e.g., in main)&lt;/span&gt;
&lt;span class="c"&gt;# or at the REPL to see the output.&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; analyze_stable&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- @code_warntype for add_one_stable(1) ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@code_warntype&lt;/span&gt; &lt;span class="n"&gt;add_one_stable&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- @code_warntype for add_one_stable_float(1.0) ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@code_warntype&lt;/span&gt; &lt;span class="n"&gt;add_one_stable_float&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- @code_warntype for add_one_generic(1) ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@code_warntype&lt;/span&gt; &lt;span class="n"&gt;add_one_generic&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Will infer T=Int64&lt;/span&gt;

    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- @code_warntype for add_one_generic(1.0) ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@code_warntype&lt;/span&gt; &lt;span class="n"&gt;add_one_generic&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Will infer T=Float64&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# Run the analysis&lt;/span&gt;
&lt;span class="n"&gt;analyze_stable&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script demonstrates what a &lt;strong&gt;type-stable&lt;/strong&gt; function looks like and introduces our primary diagnostic tool: the &lt;strong&gt;&lt;code&gt;@code_warntype&lt;/code&gt;&lt;/strong&gt; macro.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Core Concept: &lt;code&gt;add_one_stable(x::Int64)&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
This function is the definition of type stability. The signature &lt;code&gt;(x::Int64)&lt;/code&gt; and the operation &lt;code&gt;x + 1&lt;/code&gt; (where &lt;code&gt;1&lt;/code&gt; is an &lt;code&gt;Int64&lt;/code&gt;) combine to create a contract: "This function &lt;em&gt;always&lt;/em&gt; returns an &lt;code&gt;Int64&lt;/code&gt;." The compiler can rely on this 100% and generate optimal, C-like machine code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Diagnostic Tool: &lt;code&gt;@code_warntype&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;@code_warntype&lt;/code&gt; macro is your "X-ray vision" into the Julia compiler. It runs Julia's type-inference engine on a function call and reports what it found.&lt;/li&gt;
&lt;li&gt;It prints a detailed breakdown, but we only care about one line: the &lt;code&gt;Body&lt;/code&gt; line.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Body::Int64&lt;/code&gt; (Good):&lt;/strong&gt; When we run &lt;code&gt;@code_warntype add_one_stable(1)&lt;/code&gt;, the output will include &lt;code&gt;Body::Int64&lt;/code&gt;. This is the compiler's "all clear" sign. It is printed in green (in a color-supporting terminal) and means: "I have successfully inferred that the body of this function will always return an &lt;code&gt;Int64&lt;/code&gt;."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Body::Any&lt;/code&gt; or &lt;code&gt;Body::Union{...}&lt;/code&gt; (Bad):&lt;/strong&gt; If you see this (especially in red), it means the compiler &lt;em&gt;gave up&lt;/em&gt;. It could not determine the return type. This signifies type-instability and is the source of performance problems.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Generic Stability: &lt;code&gt;add_one_generic&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This function is also type-stable, but in a more general way. The &lt;code&gt;where {T&amp;lt;:Number}&lt;/code&gt; tells the compiler, "Whatever numeric type &lt;code&gt;T&lt;/code&gt; you put in, I will return that same type &lt;code&gt;T&lt;/code&gt;."&lt;/li&gt;
&lt;li&gt;When you run &lt;code&gt;@code_warntype add_one_generic(1)&lt;/code&gt;, the compiler &lt;em&gt;specializes&lt;/em&gt; the function for &lt;code&gt;T=Int64&lt;/code&gt; and infers a return type of &lt;code&gt;Body::Int64&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;When you run &lt;code&gt;@code_warntype add_one_generic(1.0)&lt;/code&gt;, it &lt;em&gt;specializes again&lt;/em&gt; for &lt;code&gt;T=Float64&lt;/code&gt; and infers &lt;code&gt;Body::Float64&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;This specialization is the core of Julia's performance: it allows you to write one generic, readable function, and the compiler automatically creates multiple, hyper-specialized, fast versions for you.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;References:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Performance Tips":&lt;/strong&gt; Explains the use of &lt;code&gt;@code_warntype&lt;/code&gt; to "find problems in your code."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "@code_warntype":&lt;/strong&gt; "Prints the inferred return types of a function call to &lt;code&gt;stdout&lt;/code&gt;... highlighting any values that are not inferred to be of a concrete type."&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;To run the script:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Note: The exact output of &lt;code&gt;@code_warntype&lt;/code&gt; is verbose and can change between Julia versions. We are only interested in the &lt;code&gt;Body::&lt;/code&gt; line at the top.)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0062_type_stable_function.jl
&lt;span class="nt"&gt;---&lt;/span&gt; @code_warntype &lt;span class="k"&gt;for &lt;/span&gt;add_one_stable&lt;span class="o"&gt;(&lt;/span&gt;1&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
Variables
  &lt;span class="c"&gt;#self#::Core.Const(add_one_stable)&lt;/span&gt;
  x::Int64
Body::Int64
&lt;span class="o"&gt;[&lt;/span&gt;...]

&lt;span class="nt"&gt;---&lt;/span&gt; @code_warntype &lt;span class="k"&gt;for &lt;/span&gt;add_one_stable_float&lt;span class="o"&gt;(&lt;/span&gt;1.0&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
Variables
  &lt;span class="c"&gt;#self#::Core.Const(add_one_stable_float)&lt;/span&gt;
  x::Float64
Body::Float64
&lt;span class="o"&gt;[&lt;/span&gt;...]

&lt;span class="nt"&gt;---&lt;/span&gt; @code_warntype &lt;span class="k"&gt;for &lt;/span&gt;add_one_generic&lt;span class="o"&gt;(&lt;/span&gt;1&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
Variables
  &lt;span class="c"&gt;#self#::Core.Const(add_one_generic)&lt;/span&gt;
  x::Int64
Body::Int64
&lt;span class="o"&gt;[&lt;/span&gt;...]

&lt;span class="nt"&gt;---&lt;/span&gt; @code_warntype &lt;span class="k"&gt;for &lt;/span&gt;add_one_generic&lt;span class="o"&gt;(&lt;/span&gt;1.0&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
Variables
  &lt;span class="c"&gt;#self#::Core.Const(add_one_generic)&lt;/span&gt;
  x::Float64
Body::Float64
&lt;span class="o"&gt;[&lt;/span&gt;...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;0063_type_instability.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0063_type_instability.jl&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;InteractiveUtils&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nd"&gt;@code_warntype&lt;/span&gt;

&lt;span class="c"&gt;# 1. A function that is type-UNSTABLE.&lt;/span&gt;
&lt;span class="c"&gt;# The return type depends on the *value* of 'x', not just its type.&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; unstable_type_based_on_value&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="c"&gt;# Returns Int&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;float&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Returns Float64&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 2. Another type-unstable function.&lt;/span&gt;
&lt;span class="c"&gt;# Here, the type changes within the function body.&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; unstable_variable_type&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
    &lt;span class="c"&gt;# 'y' starts as an Int&lt;/span&gt;
    &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="c"&gt;# 'y' might become a Float64&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;
        &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="c"&gt;# The return type depends on runtime randomness.&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 3. Use @code_warntype to diagnose the instability.&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; analyze_unstable&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- @code_warntype for unstable_type_based_on_value(1) ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# Even though we *know* 1 &amp;gt; 0, the compiler analyzes the function&lt;/span&gt;
    &lt;span class="c"&gt;# based on the *type* Int, and sees it *could* return Float64.&lt;/span&gt;
    &lt;span class="nd"&gt;@code_warntype&lt;/span&gt; &lt;span class="n"&gt;unstable_type_based_on_value&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- @code_warntype for unstable_variable_type() ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@code_warntype&lt;/span&gt; &lt;span class="n"&gt;unstable_variable_type&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# Run the analysis&lt;/span&gt;
&lt;span class="n"&gt;analyze_unstable&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script demonstrates &lt;strong&gt;type-instability&lt;/strong&gt; and how to use &lt;code&gt;@code_warntype&lt;/code&gt; to detect it. Type instability is one of the most common causes of poor performance in Julia.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Core Concept: Unstable Return Type&lt;/strong&gt;&lt;br&gt;
The function &lt;code&gt;unstable_type_based_on_value&lt;/code&gt; is type-unstable because its return type &lt;strong&gt;cannot be predicted&lt;/strong&gt; solely from the input type (&lt;code&gt;Int&lt;/code&gt;). If the input &lt;code&gt;x&lt;/code&gt; is positive, it returns an &lt;code&gt;Int&lt;/code&gt;; otherwise, it returns a &lt;code&gt;Float64&lt;/code&gt;. The compiler sees both possibilities and cannot guarantee a single, concrete return type.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Diagnostic Tool: &lt;code&gt;@code_warntype&lt;/code&gt; (Red Flags)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When we run &lt;code&gt;@code_warntype unstable_type_based_on_value(1)&lt;/code&gt;, the output will show something like &lt;code&gt;Body::Union{Int64, Float64}&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Body::Union{Int64, Float64}&lt;/code&gt; (Bad):&lt;/strong&gt; This is a &lt;strong&gt;warning sign&lt;/strong&gt;. It is often printed in &lt;strong&gt;red&lt;/strong&gt; in the terminal. The compiler is telling you: "I cannot guarantee the return type. It might be an &lt;code&gt;Int64&lt;/code&gt;, or it might be a &lt;code&gt;Float64&lt;/code&gt;."&lt;/li&gt;
&lt;li&gt;This &lt;strong&gt;forces&lt;/strong&gt; Julia to use slow, &lt;strong&gt;dynamic dispatch&lt;/strong&gt; whenever the result of this function is used later. The program has to check &lt;em&gt;at runtime&lt;/em&gt; which type was actually returned before it can perform any operation (like addition). It also likely involves &lt;strong&gt;boxing&lt;/strong&gt; the return value on the heap.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Core Concept: Unstable Variable Type&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The function &lt;code&gt;unstable_variable_type&lt;/code&gt; demonstrates another common source of instability. The variable &lt;code&gt;y&lt;/code&gt; starts as an &lt;code&gt;Int&lt;/code&gt; but might be reassigned to a &lt;code&gt;Float64&lt;/code&gt;. The compiler cannot predict the final type of &lt;code&gt;y&lt;/code&gt;, so the function's return type is also unpredictable. &lt;code&gt;@code_warntype&lt;/code&gt; will again report &lt;code&gt;Body::Union{Int64, Float64}&lt;/code&gt; or potentially even &lt;code&gt;Body::Any&lt;/code&gt; if the type changes were more complex.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Performance Impact:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Type instability acts like a "poison" that spreads through your code. If a function is unstable, any other function that calls it might &lt;em&gt;also&lt;/em&gt; become unstable, leading to cascading performance degradation. Identifying and fixing type instabilities using &lt;code&gt;@code_warntype&lt;/code&gt; is therefore a critical skill for writing fast Julia code.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;References:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Performance Tips":&lt;/strong&gt; "Avoid changing the type of a variable... When the type of a variable changes... this is known as 'type-instability'."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "@code_warntype":&lt;/strong&gt; "...highlighting any values that are not inferred to be of a concrete type." (&lt;code&gt;Union&lt;/code&gt; types are generally not concrete).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;To run the script:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Note: The exact output is verbose. Look for the &lt;code&gt;Body::&lt;/code&gt; line, often highlighted in red.)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0063_type_instability.jl
&lt;span class="nt"&gt;---&lt;/span&gt; @code_warntype &lt;span class="k"&gt;for &lt;/span&gt;unstable_type_based_on_value&lt;span class="o"&gt;(&lt;/span&gt;1&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
Variables
  &lt;span class="c"&gt;#self#::Core.Const(unstable_type_based_on_value)&lt;/span&gt;
  x::Int64
Body::Union&lt;span class="o"&gt;{&lt;/span&gt;Float64, Int64&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="c"&gt;# &amp;lt;--- Warning! (Often Red)&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;...]

&lt;span class="nt"&gt;---&lt;/span&gt; @code_warntype &lt;span class="k"&gt;for &lt;/span&gt;unstable_variable_type&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
Variables
  &lt;span class="c"&gt;#self#::Core.Const(unstable_variable_type)&lt;/span&gt;
  y::Union&lt;span class="o"&gt;{&lt;/span&gt;Float64, Int64&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="c"&gt;# &amp;lt;--- Variable 'y' is unstable&lt;/span&gt;
Body::Union&lt;span class="o"&gt;{&lt;/span&gt;Float64, Int64&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="c"&gt;# &amp;lt;--- Warning! (Often Red)&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;0064_global_variable_pitfall.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0064_global_variable_pitfall.jl&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;InteractiveUtils&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nd"&gt;@code_warntype&lt;/span&gt;

&lt;span class="c"&gt;# --- Case 1: Non-Constant Global ---&lt;/span&gt;

&lt;span class="c"&gt;# 1. Define a global variable WITHOUT 'const'.&lt;/span&gt;
&lt;span class="c"&gt;# Its type can change at any time.&lt;/span&gt;
&lt;span class="n"&gt;non_const_global&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;

&lt;span class="c"&gt;# 2. Define a function that uses the non-constant global.&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; use_non_const_global&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
    &lt;span class="c"&gt;# The compiler cannot know the type of 'non_const_global'.&lt;/span&gt;
    &lt;span class="c"&gt;# It might be an Int, or it might change to a String later.&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;non_const_global&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# --- Case 2: Constant Global ---&lt;/span&gt;

&lt;span class="c"&gt;# 3. Define a global variable WITH 'const'.&lt;/span&gt;
&lt;span class="c"&gt;# This is a promise to the compiler: the *type* of this&lt;/span&gt;
&lt;span class="c"&gt;# variable will NEVER change (though its value can if mutable).&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;const_global&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;

&lt;span class="c"&gt;# 4. Define a function that uses the constant global.&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; use_const_global&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
    &lt;span class="c"&gt;# The compiler knows 'const_global' will always be an Int.&lt;/span&gt;
    &lt;span class="c"&gt;# It can generate specialized, fast code.&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;const_global&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# --- Analysis ---&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; analyze_globals&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- @code_warntype for use_non_const_global() ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# This will show type instability (Body::Any or similar).&lt;/span&gt;
    &lt;span class="nd"&gt;@code_warntype&lt;/span&gt; &lt;span class="n"&gt;use_non_const_global&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- @code_warntype for use_const_global() ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# This will show type stability (Body::Int64).&lt;/span&gt;
    &lt;span class="nd"&gt;@code_warntype&lt;/span&gt; &lt;span class="n"&gt;use_const_global&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;

    &lt;span class="c"&gt;# Demonstrate that the functions work at runtime&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Runtime Results ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;res_non_const&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;use_non_const_global&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Result (non-const global): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res_non_const&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# We can even change the non-const global's type (bad practice!)&lt;/span&gt;
    &lt;span class="kd"&gt;global&lt;/span&gt; &lt;span class="n"&gt;non_const_global&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Changed!"&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Non-const global changed to: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;non_const_global&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# Calling the function again would now error at runtime&lt;/span&gt;

    &lt;span class="n"&gt;res_const&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;use_const_global&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Result (const global): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res_const&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# Attempting to change the type of a const global errors&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;
        &lt;span class="kd"&gt;global&lt;/span&gt; &lt;span class="n"&gt;const_global&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Cannot do this"&lt;/span&gt;
    &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
        &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Caught expected error trying to change const global type: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;analyze_globals&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script revisits a critical performance pitfall: accessing &lt;strong&gt;non-constant global variables&lt;/strong&gt; from within functions. It demonstrates why this leads to type instability and how the &lt;code&gt;const&lt;/code&gt; keyword solves the problem.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The Problem: Non-&lt;code&gt;const&lt;/code&gt; Globals&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When you define a global variable like &lt;code&gt;non_const_global = 100&lt;/code&gt;, you are telling the compiler very little. The type of this variable could change at &lt;em&gt;any moment&lt;/em&gt; during the program's execution (as shown when we reassign it to a &lt;code&gt;String&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Inside the function &lt;code&gt;use_non_const_global()&lt;/code&gt;, when the compiler sees &lt;code&gt;non_const_global * 2&lt;/code&gt;, it has &lt;strong&gt;no way to know&lt;/strong&gt; what type &lt;code&gt;non_const_global&lt;/code&gt; will have &lt;em&gt;at runtime&lt;/em&gt;. It cannot specialize the code. It must generate slow, generic code that:

&lt;ol&gt;
&lt;li&gt; Looks up the current value and type of &lt;code&gt;non_const_global&lt;/code&gt; &lt;strong&gt;at runtime&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; Performs &lt;strong&gt;dynamic dispatch&lt;/strong&gt; to find the correct &lt;code&gt;*&lt;/code&gt; method for whatever type it found.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Diagnosis with &lt;code&gt;@code_warntype&lt;/code&gt;:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Running &lt;code&gt;@code_warntype use_non_const_global()&lt;/code&gt; confirms this instability. The output will show &lt;code&gt;Body::Any&lt;/code&gt; (or some other non-concrete type, often in red). This is the compiler telling you it cannot predict the return type because it depends on the unpredictable type of the global variable.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;The Solution: &lt;code&gt;const&lt;/code&gt; Globals&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;const const_global = 200&lt;/code&gt; declaration is a &lt;strong&gt;promise&lt;/strong&gt; to the compiler: "The &lt;em&gt;type&lt;/em&gt; of &lt;code&gt;const_global&lt;/code&gt; will &lt;em&gt;always&lt;/em&gt; be &lt;code&gt;Int64&lt;/code&gt;." (Note: If &lt;code&gt;const_global&lt;/code&gt; was a mutable object like a &lt;code&gt;Vector&lt;/code&gt;, its &lt;em&gt;contents&lt;/em&gt; could still change, but it would always &lt;em&gt;refer&lt;/em&gt; to that same &lt;code&gt;Vector&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Inside &lt;code&gt;use_const_global()&lt;/code&gt;, the compiler now &lt;strong&gt;knows for certain&lt;/strong&gt; that &lt;code&gt;const_global&lt;/code&gt; is an &lt;code&gt;Int64&lt;/code&gt;. It can generate fast, specialized machine code that directly multiplies two integers.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Diagnosis with &lt;code&gt;@code_warntype&lt;/code&gt;:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Running &lt;code&gt;@code_warntype use_const_global()&lt;/code&gt; shows the fix. The output will be &lt;code&gt;Body::Int64&lt;/code&gt; (green). The compiler is confident about the return type because the global's type is guaranteed.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Rule of Thumb:&lt;/strong&gt; &lt;strong&gt;Always&lt;/strong&gt; declare global variables used in performance-critical code as &lt;code&gt;const&lt;/code&gt;. If you need a global whose &lt;em&gt;type&lt;/em&gt; might change, reconsider your design – perhaps pass it as a function argument instead. Accessing non-&lt;code&gt;const&lt;/code&gt; globals is one of the most common and easily fixed sources of poor performance in Julia.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;References:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Performance Tips":&lt;/strong&gt; "Avoid global variables." and "Declare variables as constant." These sections explicitly warn about the performance cost and recommend &lt;code&gt;const&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;To run the script:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Note: The exact output is verbose. Focus on the &lt;code&gt;Body::&lt;/code&gt; lines.)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0064_global_variable_pitfall.jl
&lt;span class="nt"&gt;---&lt;/span&gt; @code_warntype &lt;span class="k"&gt;for &lt;/span&gt;use_non_const_global&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
Variables
  &lt;span class="c"&gt;#self#::Core.Const(use_non_const_global)&lt;/span&gt;
Body::Any &lt;span class="c"&gt;# &amp;lt;--- Warning! Instability from non-const global&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;...]

&lt;span class="nt"&gt;---&lt;/span&gt; @code_warntype &lt;span class="k"&gt;for &lt;/span&gt;use_const_global&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
Variables
  &lt;span class="c"&gt;#self#::Core.Const(use_const_global)&lt;/span&gt;
Body::Int64 &lt;span class="c"&gt;# &amp;lt;--- Good! Type stable due to const global&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;...]

&lt;span class="nt"&gt;---&lt;/span&gt; Runtime Results &lt;span class="nt"&gt;---&lt;/span&gt;
Result &lt;span class="o"&gt;(&lt;/span&gt;non-const global&lt;span class="o"&gt;)&lt;/span&gt;: 200
Non-const global changed to: Changed!
Result &lt;span class="o"&gt;(&lt;/span&gt;const global&lt;span class="o"&gt;)&lt;/span&gt;: 400
Caught expected error trying to change const global &lt;span class="nb"&gt;type&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;...] invalid redefinition of constant const_global
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Union Types
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;0065_union_types_basics.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0065_union_types_basics.jl&lt;/span&gt;

&lt;span class="c"&gt;# 1. Define a function that might fail predictably.&lt;/span&gt;
&lt;span class="c"&gt;# A dictionary lookup is a perfect example: the key might not exist.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;my_dictionary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Dict&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"a"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"b"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 2. Function returning a Union type for error handling.&lt;/span&gt;
&lt;span class="c"&gt;# The return type annotation 'Union{Int64, Nothing}' explicitly states&lt;/span&gt;
&lt;span class="c"&gt;# that this function will return *either* an Int64 on success&lt;/span&gt;
&lt;span class="c"&gt;# or the special value 'nothing' on failure.&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; safe_get&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Union&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;Int64&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Nothing&lt;/span&gt;&lt;span class="x"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;haskey&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_dictionary&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;my_dictionary&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt; &lt;span class="c"&gt;# Returns Int64&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;nothing&lt;/span&gt;           &lt;span class="c"&gt;# Returns Nothing&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 3. Call the function and handle the Union result.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Calling safe_get ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;key_success&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"a"&lt;/span&gt;
&lt;span class="n"&gt;result_success&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;safe_get&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key_success&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Check the type of the result&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Result for key '&lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;key_success': "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result_success&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Type of result: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result_success&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# Int64&lt;/span&gt;

&lt;span class="c"&gt;# Idiomatic check for the 'nothing' failure case&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;result_success&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nb"&gt;nothing&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Success! Value is: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result_success&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Key '&lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;key_success' not found."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;key_fail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"c"&lt;/span&gt;
&lt;span class="n"&gt;result_fail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;safe_get&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key_fail&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Result for key '&lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;key_fail': "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result_fail&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Type of result: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result_fail&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# Nothing&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;result_fail&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nb"&gt;nothing&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Success! Value is: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result_fail&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Key '&lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;key_fail' not found."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 4. 'isbitstype' vs 'isbits' check&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- isbits checks ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# isbits(x) is true if typeof(x) is an isbitstype&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"isbits(result_success): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isbits&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result_success&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# true (Int64 is isbits)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"isbits(result_fail):    "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isbits&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result_fail&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;    &lt;span class="c"&gt;# true (Nothing is isbits)&lt;/span&gt;

&lt;span class="c"&gt;# The Union *type* itself is not isbits because it's abstract.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"isbitstype(Union{Int64, Nothing}): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isbitstype&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Union&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;Int64&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Nothing&lt;/span&gt;&lt;span class="x"&gt;}))&lt;/span&gt; &lt;span class="c"&gt;# false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces &lt;strong&gt;&lt;code&gt;Union&lt;/code&gt; types&lt;/strong&gt;, demonstrating their idiomatic use for handling predictable failure conditions in a type-stable and efficient way.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Core Concept: &lt;code&gt;Union{TypeA, TypeB, ...}&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
A &lt;code&gt;Union&lt;/code&gt; type represents a value that could be one of several specified types. &lt;code&gt;Union{Int64, Nothing}&lt;/code&gt; means "this variable can hold &lt;em&gt;either&lt;/em&gt; an &lt;code&gt;Int64&lt;/code&gt; &lt;em&gt;or&lt;/em&gt; the value &lt;code&gt;nothing&lt;/code&gt;."&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Error Handling Pattern:&lt;/strong&gt;&lt;br&gt;
Returning a &lt;code&gt;Union&lt;/code&gt; like &lt;code&gt;Union{ResultType, Nothing}&lt;/code&gt; (or &lt;code&gt;Union{ResultType, ErrorCode}&lt;/code&gt;) is Julia's preferred pattern for functions that might fail in expected ways. Instead of throwing an exception (which is computationally expensive), the function returns a value indicating success or failure.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;safe_get&lt;/code&gt; implements this: on success, it returns the &lt;code&gt;Int64&lt;/code&gt; value; on failure (key not found), it returns the special singleton value &lt;code&gt;nothing&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;caller&lt;/strong&gt; is then responsible for checking the return type. The idiomatic check is &lt;code&gt;if result !== nothing&lt;/code&gt;. The &lt;code&gt;!==&lt;/code&gt; operator checks for strict identity (and type) and is very fast.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Performance: Small &lt;code&gt;Union&lt;/code&gt;s are Efficiently Stored&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;While the &lt;code&gt;Union{Int64, Nothing}&lt;/code&gt; &lt;em&gt;type itself&lt;/em&gt; is technically abstract and therefore &lt;code&gt;isbitstype&lt;/code&gt; returns &lt;code&gt;false&lt;/code&gt;, Julia's compiler includes &lt;strong&gt;crucial optimizations&lt;/strong&gt; for small unions like this, especially when they are used &lt;strong&gt;inside arrays or structs&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How? (Inline Storage + Type Tag):&lt;/strong&gt; The compiler stores the data &lt;strong&gt;inline&lt;/strong&gt; (using enough space for the largest member, &lt;code&gt;Int64&lt;/code&gt;) and uses a hidden &lt;strong&gt;type tag&lt;/strong&gt; byte to track whether an &lt;code&gt;Int64&lt;/code&gt; or &lt;code&gt;Nothing&lt;/code&gt; is currently stored.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Result:&lt;/strong&gt; Accessing values from such a &lt;code&gt;Union&lt;/code&gt; field or array element is very fast (check tag, read inline data) and avoids heap allocation ("boxing") and pointer chasing. Checking &lt;code&gt;if result !== nothing&lt;/code&gt; compiles down to a simple, fast check of this internal type tag.&lt;/li&gt;
&lt;li&gt;This optimization makes the &lt;code&gt;Union{ResultType, Nothing}&lt;/code&gt; pattern a high-performance alternative to exceptions for predictable failure modes.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;isbits&lt;/code&gt; vs. &lt;code&gt;isbitstype&lt;/code&gt; Clarification:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;isbitstype(T::Type)&lt;/code&gt; asks: "Does the type &lt;code&gt;T&lt;/code&gt; itself describe a single, fixed, C-like memory layout?" For &lt;code&gt;Union{Int64, Nothing}&lt;/code&gt;, the answer is &lt;strong&gt;&lt;code&gt;false&lt;/code&gt;&lt;/strong&gt; because the &lt;code&gt;Union&lt;/code&gt; type is abstract; its representation depends on the current value.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;isbits(x)&lt;/code&gt; asks: "Is the &lt;em&gt;value&lt;/em&gt; &lt;code&gt;x&lt;/code&gt; of an &lt;code&gt;isbits&lt;/code&gt; type?" Since both &lt;code&gt;Int64&lt;/code&gt; and &lt;code&gt;Nothing&lt;/code&gt; are &lt;code&gt;isbits&lt;/code&gt; types, &lt;code&gt;isbits(result_success)&lt;/code&gt; and &lt;code&gt;isbits(result_fail)&lt;/code&gt; both return &lt;strong&gt;&lt;code&gt;true&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Contrast with Exceptions:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
This &lt;code&gt;Union&lt;/code&gt; return pattern should be preferred over &lt;code&gt;try...catch&lt;/code&gt; for common, expected failure modes like dictionary lookups, parsing attempts (&lt;code&gt;tryparse&lt;/code&gt;), or finding items in a list. Exceptions are reserved for truly &lt;em&gt;exceptional&lt;/em&gt; or unexpected errors where the high cost of stack unwinding is acceptable.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;References:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Types", "Union Types":&lt;/strong&gt; "Union types are a special abstract type..."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, &lt;code&gt;devdocs&lt;/code&gt;, "isbits Union Optimizations":&lt;/strong&gt; Details how Julia stores &lt;code&gt;isbits Union&lt;/code&gt; fields and arrays inline using type tags for performance, confirming the efficiency despite the &lt;code&gt;Union&lt;/code&gt; type being abstract.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, &lt;code&gt;isbits(x)&lt;/code&gt; and &lt;code&gt;isbitstype(T)&lt;/code&gt;:&lt;/strong&gt; Clarify the distinction between checking a value and checking a type.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0065_union_types_basics.jl
&lt;span class="nt"&gt;---&lt;/span&gt; Calling safe_get &lt;span class="nt"&gt;---&lt;/span&gt;
Result &lt;span class="k"&gt;for &lt;/span&gt;key &lt;span class="s1"&gt;'a'&lt;/span&gt;: 1
Type of result: Int64
  Success! Value is: 10
&lt;span class="nt"&gt;--------------------&lt;/span&gt;
Result &lt;span class="k"&gt;for &lt;/span&gt;key &lt;span class="s1"&gt;'c'&lt;/span&gt;: nothing
Type of result: Nothing
  Key &lt;span class="s1"&gt;'c'&lt;/span&gt; not found.

&lt;span class="nt"&gt;---&lt;/span&gt; isbits checks &lt;span class="nt"&gt;---&lt;/span&gt;
isbits&lt;span class="o"&gt;(&lt;/span&gt;result_success&lt;span class="o"&gt;)&lt;/span&gt;: &lt;span class="nb"&gt;true
&lt;/span&gt;isbits&lt;span class="o"&gt;(&lt;/span&gt;result_fail&lt;span class="o"&gt;)&lt;/span&gt;:    &lt;span class="nb"&gt;true
&lt;/span&gt;isbitstype&lt;span class="o"&gt;(&lt;/span&gt;Union&lt;span class="o"&gt;{&lt;/span&gt;Int64, Nothing&lt;span class="o"&gt;})&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;While &lt;code&gt;Union&lt;/code&gt; types are a powerful feature, their performance characteristics depend heavily on &lt;strong&gt;how many types&lt;/strong&gt; are included in the &lt;code&gt;Union&lt;/code&gt; and &lt;strong&gt;whether those types are &lt;code&gt;isbits&lt;/code&gt;&lt;/strong&gt;. There is a significant performance difference between "small" and "large" unions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Small &lt;code&gt;isbits&lt;/code&gt; Unions (Fast) ✨
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Example:&lt;/strong&gt; &lt;code&gt;Union{Int64, Nothing}&lt;/code&gt;, &lt;code&gt;Union{Float64, Bool}&lt;/code&gt;, &lt;code&gt;Union{Int8, UInt8}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance:&lt;/strong&gt; &lt;strong&gt;Excellent&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why? Compiler Optimization:&lt;/strong&gt; Julia's compiler has specific, highly effective optimizations for &lt;code&gt;Union&lt;/code&gt;s that contain a small number (typically 2-3) of &lt;code&gt;isbits&lt;/code&gt; types (and/or &lt;code&gt;Nothing&lt;/code&gt;).

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Inline Storage:&lt;/strong&gt; As seen in the previous lesson, the compiler can often store the value &lt;strong&gt;inline&lt;/strong&gt; within the memory allocated for the variable or struct field. It allocates enough space for the largest &lt;code&gt;isbits&lt;/code&gt; member.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type Tag:&lt;/strong&gt; An extra hidden &lt;strong&gt;type tag byte&lt;/strong&gt; is stored alongside the inline data. This byte efficiently encodes &lt;em&gt;which&lt;/em&gt; of the possible types is currently stored.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fast Dispatch:&lt;/strong&gt; Checking the type (e.g., &lt;code&gt;if x === nothing&lt;/code&gt;) becomes a simple, fast check of this tag byte, often compiling down to a single conditional branch instruction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No Boxing:&lt;/strong&gt; There is generally no heap allocation ("boxing") required for these small unions when used, for example, as struct fields or array elements.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Use Case:&lt;/strong&gt; Ideal for representing optional values (&lt;code&gt;Union{T, Nothing}&lt;/code&gt;), return codes (&lt;code&gt;Union{Result, ErrorCode}&lt;/code&gt;), or situations where a value can be one of just a few simple types.&lt;/p&gt;




&lt;h2&gt;
  
  
  Large Unions or Unions with Non-&lt;code&gt;isbits&lt;/code&gt; Types (Slow) 🐌
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Example:&lt;/strong&gt; &lt;code&gt;Union{Int64, Float64, String}&lt;/code&gt;, &lt;code&gt;Union{Int64, Vector{Float64}}&lt;/code&gt;, &lt;code&gt;Union{Circle, Rectangle, MutableSquare}&lt;/code&gt; (from Module 5)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance:&lt;/strong&gt; &lt;strong&gt;Poor&lt;/strong&gt;, approaching the performance of &lt;code&gt;Any&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why? Lack of Optimization:&lt;/strong&gt; The compiler's inline storage + type tag optimization breaks down or becomes inefficient when:

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Too Many Types:&lt;/strong&gt; Checking the type tag requires a complex series of branches (e.g., "is it type 1? no. is it type 2? no. is it type 3? ..."). This significantly slows down dispatch.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Non-&lt;code&gt;isbits&lt;/code&gt; Members:&lt;/strong&gt; If the &lt;code&gt;Union&lt;/code&gt; includes non-&lt;code&gt;isbits&lt;/code&gt; types (like &lt;code&gt;String&lt;/code&gt;, &lt;code&gt;Vector&lt;/code&gt;, or &lt;code&gt;mutable struct&lt;/code&gt;s), these types &lt;em&gt;must&lt;/em&gt; be heap-allocated anyway. The compiler often cannot store them inline. It must fall back to storing a &lt;strong&gt;pointer&lt;/strong&gt; to the heap-allocated object, similar to how &lt;code&gt;Any&lt;/code&gt; works. This involves &lt;strong&gt;boxing&lt;/strong&gt; and &lt;strong&gt;pointer chasing&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Variable Size:&lt;/strong&gt; If the types in the &lt;code&gt;Union&lt;/code&gt; have different sizes, efficient inline storage becomes impossible.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Performance Impact:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Boxing:&lt;/strong&gt; Values might be heap-allocated ("boxed") even if they are simple types like &lt;code&gt;Int&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic Dispatch:&lt;/strong&gt; Using a value from a large &lt;code&gt;Union&lt;/code&gt; almost always requires slow, runtime dynamic dispatch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type Instability:&lt;/strong&gt; Functions returning large &lt;code&gt;Union&lt;/code&gt;s are inherently type-unstable, preventing compiler specialization and optimization.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Guideline:&lt;/strong&gt; Avoid large unions in performance-critical code. If a variable or field truly needs to hold many different types, it often indicates a design issue. Consider using abstract types with multiple dispatch (as in Module 5) or redesigning your data structures. Small, &lt;code&gt;isbits&lt;/code&gt;-based unions are a targeted optimization; large unions are generally an anti-pattern for performance.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, &lt;code&gt;devdocs&lt;/code&gt;, "isbits Union Optimizations":&lt;/strong&gt; Explains the type tag mechanism and its limitations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Performance Tips":&lt;/strong&gt; Implicitly warns against large unions by emphasizing type stability and avoiding abstract containers.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  Array Slicing
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;0067_views_recap_performance.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0067_views_recap_performance.jl&lt;/span&gt;

&lt;span class="c"&gt;# Import necessary tools&lt;/span&gt;
&lt;span class="c"&gt;# BenchmarkTools is not in the standard library, so we need to add it.&lt;/span&gt;
&lt;span class="c"&gt;# See Explanation section for installation instructions.&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BenchmarkTools&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nd"&gt;@btime&lt;/span&gt;

&lt;span class="c"&gt;# 1. A function that processes a vector (e.g., calculates sum)&lt;/span&gt;
&lt;span class="c"&gt;# We make it type-stable by annotating the input.&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; process_data&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;AbstractVector&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;&lt;span class="x"&gt;})&lt;/span&gt;
    &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;
    &lt;span class="c"&gt;# Use @inbounds for performance; assumes data access is safe&lt;/span&gt;
    &lt;span class="nd"&gt;@inbounds&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;eachindex&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 2. Create a large vector&lt;/span&gt;
&lt;span class="n"&gt;N&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1_000_000&lt;/span&gt; &lt;span class="c"&gt;# 1 million elements&lt;/span&gt;
&lt;span class="n"&gt;original_vector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 3. Define the slice indices&lt;/span&gt;
&lt;span class="n"&gt;start_idx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;end_idx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;500_000&lt;/span&gt; &lt;span class="c"&gt;# Half the array&lt;/span&gt;

&lt;span class="c"&gt;# --- Benchmarking ---&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Benchmarking Slice (Copying) ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# 4. Benchmark passing a slice (A[start:end])&lt;/span&gt;
&lt;span class="c"&gt;# This creates a *new* vector containing a copy of the elements.&lt;/span&gt;
&lt;span class="c"&gt;# The benchmark measures:&lt;/span&gt;
&lt;span class="c"&gt;#   a) Time to allocate the new vector&lt;/span&gt;
&lt;span class="c"&gt;#   b) Time to copy the 500k elements&lt;/span&gt;
&lt;span class="c"&gt;#   c) Time to run process_data() on the copy&lt;/span&gt;
&lt;span class="nd"&gt;@btime&lt;/span&gt; &lt;span class="n"&gt;process_data&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;original_vector&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;start_idx&lt;/span&gt;&lt;span class="o"&gt;:$&lt;/span&gt;&lt;span class="n"&gt;end_idx&lt;/span&gt;&lt;span class="x"&gt;])&lt;/span&gt;


&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Benchmarking View (Zero-Copy) ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# 5. Benchmark passing a view (@view A[start:end])&lt;/span&gt;
&lt;span class="c"&gt;# This creates a lightweight 'SubArray' object that *refers*&lt;/span&gt;
&lt;span class="c"&gt;# to the original vector's memory. No allocation, no copying.&lt;/span&gt;
&lt;span class="c"&gt;# The benchmark measures *only*:&lt;/span&gt;
&lt;span class="c"&gt;#   a) Time to run process_data() directly on the original data&lt;/span&gt;
&lt;span class="nd"&gt;@btime&lt;/span&gt; &lt;span class="n"&gt;process_data&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@view&lt;/span&gt; &lt;span class="n"&gt;original_vector&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;start_idx&lt;/span&gt;&lt;span class="o"&gt;:$&lt;/span&gt;&lt;span class="n"&gt;end_idx&lt;/span&gt;&lt;span class="x"&gt;])&lt;/span&gt;

&lt;span class="c"&gt;# 6. Verify the view type&lt;/span&gt;
&lt;span class="n"&gt;view_obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;@view&lt;/span&gt; &lt;span class="n"&gt;original_vector&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="n"&gt;start_idx&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;end_idx&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Type of view object: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;view_obj&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Does view share memory with original? "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mightalias&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;original_vector&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;view_obj&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script revisits &lt;strong&gt;array slicing&lt;/strong&gt; and &lt;strong&gt;views&lt;/strong&gt;, focusing explicitly on the &lt;strong&gt;performance implications&lt;/strong&gt;. It uses the &lt;code&gt;BenchmarkTools.jl&lt;/code&gt; package to provide accurate measurements, demonstrating why views (&lt;code&gt;@view&lt;/code&gt;) are essential for high-performance code.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Installation Note:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This lesson uses &lt;code&gt;BenchmarkTools.jl&lt;/code&gt;, which is not part of Julia's standard library. You need to add it to your environment once.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Start the Julia REPL: &lt;code&gt;julia&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt; Enter Pkg mode by typing &lt;code&gt;]&lt;/code&gt; at the &lt;code&gt;julia&amp;gt;&lt;/code&gt; prompt. The prompt will change to &lt;code&gt;pkg&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; Type &lt;code&gt;add BenchmarkTools&lt;/code&gt; and press Enter. Julia will download and install the package.&lt;/li&gt;
&lt;li&gt; Exit Pkg mode by pressing Backspace or &lt;code&gt;Ctrl+C&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; You can now run this script.&lt;/li&gt;
&lt;/ol&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Recap: Slice vs. View&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Slice (&lt;code&gt;A[start:end]&lt;/code&gt;):&lt;/strong&gt; Creates a &lt;strong&gt;new&lt;/strong&gt; &lt;code&gt;Array&lt;/code&gt; object, allocates fresh memory, and &lt;strong&gt;copies&lt;/strong&gt; the selected elements from the original array into the new one. This is memory-intensive and CPU-intensive if the slice is large or done frequently.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;View (&lt;code&gt;@view A[start:end]&lt;/code&gt;):&lt;/strong&gt; Creates a lightweight &lt;code&gt;SubArray&lt;/code&gt; object. This object does &lt;strong&gt;not&lt;/strong&gt; allocate memory for the data itself; it simply holds a &lt;strong&gt;reference&lt;/strong&gt; to the &lt;em&gt;original&lt;/em&gt; array and stores the selected indices. It is a zero-copy, zero-allocation operation.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Benchmarking with &lt;code&gt;@btime&lt;/code&gt;:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;@btime&lt;/code&gt; macro (from &lt;code&gt;BenchmarkTools.jl&lt;/code&gt;) is the standard tool for accurate performance measurement in Julia. It runs the expression many times, measures the minimum execution time, and reports memory allocations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Crucial Interpolation (&lt;code&gt;$&lt;/code&gt;):&lt;/strong&gt; Notice &lt;code&gt;original_vector[$start_idx:$end_idx]&lt;/code&gt; inside &lt;code&gt;@btime&lt;/code&gt;. The &lt;code&gt;$&lt;/code&gt; is &lt;strong&gt;essential&lt;/strong&gt; here. It tells &lt;code&gt;@btime&lt;/code&gt; to treat &lt;code&gt;original_vector&lt;/code&gt;, &lt;code&gt;start_idx&lt;/code&gt;, and &lt;code&gt;end_idx&lt;/code&gt; as pre-computed &lt;em&gt;values&lt;/em&gt; rather than global variables to be looked up inside the timing loop. Without the &lt;code&gt;$&lt;/code&gt;, you would be benchmarking global variable access time, polluting the results.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Interpreting the Results:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Slice Benchmark:&lt;/strong&gt; The &lt;code&gt;@btime&lt;/code&gt; output for the slice will show a significant amount of &lt;strong&gt;memory allocation&lt;/strong&gt; (e.g., &lt;code&gt;allocs: 1&lt;/code&gt;) and a non-trivial execution time. This time includes the cost of allocating the new vector, copying half a million &lt;code&gt;Float64&lt;/code&gt;s, and &lt;em&gt;then&lt;/em&gt; running &lt;code&gt;process_data&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;View Benchmark:&lt;/strong&gt; The &lt;code&gt;@btime&lt;/code&gt; output for the &lt;code&gt;@view&lt;/code&gt; will show &lt;strong&gt;zero memory allocations&lt;/strong&gt; (&lt;code&gt;allocs: 0&lt;/code&gt;) and a significantly &lt;strong&gt;faster&lt;/strong&gt; execution time. This time represents &lt;em&gt;only&lt;/em&gt; the cost of running &lt;code&gt;process_data&lt;/code&gt; directly on the relevant portion of the original data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Base.mightalias&lt;/code&gt;:&lt;/strong&gt; This function returning &lt;code&gt;true&lt;/code&gt; confirms that the view object potentially shares memory with the original vector (which it does).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Performance Guideline (HFT Context):&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
In performance-critical code, especially within loops or functions called frequently, &lt;strong&gt;always use views (&lt;code&gt;@view&lt;/code&gt;) when you need to pass a portion of an array to another function without needing an independent copy&lt;/strong&gt;. Slicing (&lt;code&gt;A[start:end]&lt;/code&gt;) should only be used when you explicitly require a separate, mutable copy of the data. Unnecessary copying is a major source of avoidable overhead and GC pressure.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;References:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Multi-dimensional Arrays", "Views (SubArrays and other relevant types)":&lt;/strong&gt; Explains the concept of &lt;code&gt;SubArray&lt;/code&gt; and the &lt;code&gt;@view&lt;/code&gt; macro.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, &lt;code&gt;BenchmarkTools.jl&lt;/code&gt;:&lt;/strong&gt; Describes the usage of &lt;code&gt;@btime&lt;/code&gt; and the importance of variable interpolation (&lt;code&gt;$&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;To run the script:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(You must first install &lt;code&gt;BenchmarkTools.jl&lt;/code&gt; as described above.)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0067_views_recap_performance.jl 
&lt;span class="nt"&gt;---&lt;/span&gt; Benchmarking Slice &lt;span class="o"&gt;(&lt;/span&gt;Copying&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
  293.589 μs &lt;span class="o"&gt;(&lt;/span&gt;5 allocations: 3.81 MiB&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nt"&gt;---&lt;/span&gt; Benchmarking View &lt;span class="o"&gt;(&lt;/span&gt;Zero-Copy&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
  185.664 μs &lt;span class="o"&gt;(&lt;/span&gt;3 allocations: 96 bytes&lt;span class="o"&gt;)&lt;/span&gt;

Type of view object: SubArray&lt;span class="o"&gt;{&lt;/span&gt;Float64, 1, Vector&lt;span class="o"&gt;{&lt;/span&gt;Float64&lt;span class="o"&gt;}&lt;/span&gt;, Tuple&lt;span class="o"&gt;{&lt;/span&gt;UnitRange&lt;span class="o"&gt;{&lt;/span&gt;Int64&lt;span class="o"&gt;}}&lt;/span&gt;, &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
Does view share memory with original? &lt;span class="nb"&gt;true&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(Replace &lt;code&gt;### μs minimum time: X/Y ###&lt;/code&gt; with the actual timings you observe. Time X should be significantly larger than Time Y, and allocations should be 1 vs 0.)&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Broadcasting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;0068_broadcasting_basics.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0068_broadcasting_basics.jl&lt;/span&gt;

&lt;span class="c"&gt;# 1. Define a simple scalar function.&lt;/span&gt;
&lt;span class="c"&gt;# This function works on single numbers.&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; square_element&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Number&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 2. Create a vector of numbers.&lt;/span&gt;
&lt;span class="n"&gt;numbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt;

&lt;span class="c"&gt;# 3. Attempting to call the scalar function directly on the vector fails.&lt;/span&gt;
&lt;span class="c"&gt;# Julia doesn't automatically assume element-wise operation.&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="n"&gt;result_fail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;square_element&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Caught expected error (scalar function on vector):"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 4. The Broadcasting Dot '.' Syntax.&lt;/span&gt;
&lt;span class="c"&gt;# Placing a dot '.' after the function name tells Julia to apply&lt;/span&gt;
&lt;span class="c"&gt;# the function element-wise to the collection.&lt;/span&gt;
&lt;span class="n"&gt;result_broadcast&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;square_element&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Note the dot!&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Result of broadcasting square_element.(numbers): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result_broadcast&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Type of result: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result_broadcast&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# A new Vector&lt;/span&gt;

&lt;span class="c"&gt;# 5. Broadcasting works with standard operators too.&lt;/span&gt;
&lt;span class="c"&gt;# The dot goes *before* the operator.&lt;/span&gt;
&lt;span class="n"&gt;plus_one&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt; &lt;span class="o"&gt;.+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;times_two&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt; &lt;span class="o"&gt;.*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="n"&gt;powers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt; &lt;span class="o"&gt;.^&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="c"&gt;# Element-wise exponentiation&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Broadcasting operators:"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  numbers .+ 1: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;plus_one&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  numbers .* 2: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;times_two&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  numbers .^ 2: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;powers&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 6. Broadcasting with multiple arguments.&lt;/span&gt;
&lt;span class="c"&gt;# Arrays must have compatible dimensions (or be scalars).&lt;/span&gt;
&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;sums_broadcast&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;.+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Broadcasting a .+ b: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sums_broadcast&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Scalar broadcasting: The scalar '100' is automatically "expanded".&lt;/span&gt;
&lt;span class="n"&gt;sums_scalar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;.+&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Broadcasting a .+ 100: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sums_scalar&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces &lt;strong&gt;broadcasting&lt;/strong&gt;, one of Julia's most powerful and idiomatic features for working with arrays and collections, denoted by the dot (&lt;code&gt;.&lt;/code&gt;) syntax.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Core Concept:&lt;/strong&gt; Broadcasting provides a concise syntax to apply a function designed for &lt;strong&gt;scalar&lt;/strong&gt; (single) values &lt;strong&gt;element-wise&lt;/strong&gt; to arrays or collections.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Our &lt;code&gt;square_element&lt;/code&gt; function only knows how to square one number. Trying to pass it a &lt;code&gt;Vector&lt;/code&gt; fails because there's no method &lt;code&gt;square_element(::Vector)&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;The Dot (&lt;code&gt;.&lt;/code&gt;): Vectorizing Functions&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Placing a dot &lt;code&gt;.&lt;/code&gt; immediately after a function name (or before an operator) transforms it into a &lt;strong&gt;broadcasting operation&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;square_element.(numbers)&lt;/code&gt; tells Julia: "Take the &lt;code&gt;square_element&lt;/code&gt; function and apply it to &lt;em&gt;each element&lt;/em&gt; of the &lt;code&gt;numbers&lt;/code&gt; vector, collecting the results into a new vector."&lt;/li&gt;
&lt;li&gt;Similarly, &lt;code&gt;numbers .+ 1&lt;/code&gt; applies the scalar addition &lt;code&gt;+ 1&lt;/code&gt; to each element.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Syntax:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For function calls: &lt;code&gt;my_function.(arg1, arg2, ...)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;For operators: &lt;code&gt;arg1 .&amp;lt;operator&amp;gt; arg2&lt;/code&gt; (e.g., &lt;code&gt;.+&lt;/code&gt;, &lt;code&gt;.*&lt;/code&gt;, &lt;code&gt;.&amp;gt;&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Why is this important?&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1.  **Readability:** It avoids writing explicit `for` loops for simple element-wise operations. `y = sin.(x)` is much clearer than a manual loop.
2.  **Generality:** It works on *any* function and *any* iterable collection (arrays, tuples, ranges, etc.). You don't need specially written "vectorized" versions of your functions.
3.  **Performance (Next Lesson):** Broadcasting is **not just syntactic sugar for a loop**. Julia's compiler performs **loop fusion**, which can make broadcasted operations significantly faster than manual loops by avoiding temporary arrays.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Multiple Arguments &amp;amp; Dimension Rules:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Broadcasting works with functions/operators taking multiple arguments (e.g., &lt;code&gt;a .+ b&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;The arrays must have &lt;strong&gt;compatible dimensions&lt;/strong&gt;. This generally means they either have the same dimensions, or one of the arguments is a scalar (which is implicitly "expanded" to match the other argument's shape). More complex rules exist for arrays of different dimensions (e.g., adding a vector to a matrix column-wise), following standard broadcasting conventions found in languages like Python (NumPy) and R.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;References:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Functions", "Dot Syntax for Vectorizing Functions":&lt;/strong&gt; "For every function &lt;code&gt;f&lt;/code&gt;, the syntax &lt;code&gt;f.(args...)&lt;/code&gt; is automatically defined to perform &lt;code&gt;f&lt;/code&gt; elementwise over the collections &lt;code&gt;args...&lt;/code&gt;"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Multi-dimensional Arrays", "Broadcasting":&lt;/strong&gt; Provides detailed rules for dimension compatibility.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0068_broadcasting_basics.jl
Caught expected error &lt;span class="o"&gt;(&lt;/span&gt;scalar &lt;span class="k"&gt;function &lt;/span&gt;on vector&lt;span class="o"&gt;)&lt;/span&gt;:
MethodError: no method matching square_element&lt;span class="o"&gt;(&lt;/span&gt;::Vector&lt;span class="o"&gt;{&lt;/span&gt;Int64&lt;span class="o"&gt;})&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;...]

Result of broadcasting square_element.&lt;span class="o"&gt;(&lt;/span&gt;numbers&lt;span class="o"&gt;)&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;1, 4, 9, 16]
Type of result: Vector&lt;span class="o"&gt;{&lt;/span&gt;Int64&lt;span class="o"&gt;}&lt;/span&gt;

Broadcasting operators:
  numbers .+ 1: &lt;span class="o"&gt;[&lt;/span&gt;2, 3, 4, 5]
  numbers .&lt;span class="k"&gt;*&lt;/span&gt; 2: &lt;span class="o"&gt;[&lt;/span&gt;2, 4, 6, 8]
  numbers .^ 2: &lt;span class="o"&gt;[&lt;/span&gt;1, 4, 9, 16]

Broadcasting a .+ b: &lt;span class="o"&gt;[&lt;/span&gt;11, 22]
Broadcasting a .+ 100: &lt;span class="o"&gt;[&lt;/span&gt;110, 120]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;0069_broadcasting_performance.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0069_broadcasting_performance.jl&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BenchmarkTools&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nd"&gt;@btime&lt;/span&gt;

&lt;span class="c"&gt;# 1. Define input data&lt;/span&gt;
&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1_000_000&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# --- Method 1: Fused Broadcasting (Allocating) ---&lt;/span&gt;

&lt;span class="c"&gt;# 2. Perform multiple operations using broadcasting dots.&lt;/span&gt;
&lt;span class="c"&gt;# This creates and returns a NEW array.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Benchmarking Fused Broadcasting (Allocating): sin.(x .* 2.0 .+ 1.0) ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@btime&lt;/span&gt; &lt;span class="n"&gt;sin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="x"&gt;((&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="o"&gt;.*&lt;/span&gt; &lt;span class="mf"&gt;2.0&lt;/span&gt; &lt;span class="o"&gt;.+&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="x"&gt;);&lt;/span&gt;


&lt;span class="c"&gt;# --- Method 2: Non-Fused Operations (Allocating) ---&lt;/span&gt;

&lt;span class="c"&gt;# 3. Perform the same operations step-by-step, storing intermediates.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Benchmarking Non-Fused Operations (Allocating) ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; non_fused_calculation&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;temp1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;.*&lt;/span&gt; &lt;span class="mf"&gt;2.0&lt;/span&gt;
    &lt;span class="n"&gt;temp2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;temp1&lt;/span&gt; &lt;span class="o"&gt;.+&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;temp2&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nd"&gt;@btime&lt;/span&gt; &lt;span class="n"&gt;non_fused_calculation&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="x"&gt;);&lt;/span&gt;


&lt;span class="c"&gt;# --- Method 3: Manual Loop (Allocating) ---&lt;/span&gt;

&lt;span class="c"&gt;# 4. Perform the same operation with a manual loop, allocating a result.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Benchmarking Manual Loop (Allocating) ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; manual_loop_calculation&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;similar&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@inbounds&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;eachindex&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;val_step1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;2.0&lt;/span&gt;
        &lt;span class="n"&gt;val_step2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;val_step1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sin&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;val_step2&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nd"&gt;@btime&lt;/span&gt; &lt;span class="n"&gt;manual_loop_calculation&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="x"&gt;);&lt;/span&gt;


&lt;span class="c"&gt;# --- Method 4: In-Place Broadcasting on a View ---&lt;/span&gt;

&lt;span class="c"&gt;# 5. Define a function that modifies a view IN-PLACE.&lt;/span&gt;
&lt;span class="c"&gt;# The '.=' operator performs broadcasting and assigns the result&lt;/span&gt;
&lt;span class="c"&gt;# back into the original array (or view).&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; inplace_calculation_view!&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y_view&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x_view&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# y_view .= sin.(x_view .* 2.0 .+ 1.0) # Modifies y_view&lt;/span&gt;
    &lt;span class="c"&gt;# OR, if modifying x_view itself:&lt;/span&gt;
    &lt;span class="n"&gt;x_view&lt;/span&gt; &lt;span class="o"&gt;.=&lt;/span&gt; &lt;span class="n"&gt;sin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x_view&lt;/span&gt; &lt;span class="o"&gt;.*&lt;/span&gt; &lt;span class="mf"&gt;2.0&lt;/span&gt; &lt;span class="o"&gt;.+&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Modifies x_view&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Benchmarking In-Place Broadcasting on View ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Create a view (zero-cost)&lt;/span&gt;
&lt;span class="n"&gt;x_view&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;@view&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt;
&lt;span class="c"&gt;# IMPORTANT: Create a COPY for the benchmark, so we don't&lt;/span&gt;
&lt;span class="c"&gt;# modify the 'x' needed for other benchmarks if we run this multiple times.&lt;/span&gt;
&lt;span class="n"&gt;x_view_copy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;copy&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x_view&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Benchmark modifying the view copy in-place.&lt;/span&gt;
&lt;span class="c"&gt;# This should have ZERO allocations related to the result array.&lt;/span&gt;
&lt;span class="nd"&gt;@btime&lt;/span&gt; &lt;span class="n"&gt;inplace_calculation_view!&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;x_view_copy&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;x_view_copy&lt;/span&gt;&lt;span class="x"&gt;);&lt;/span&gt; &lt;span class="c"&gt;# Modify in place&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script demonstrates &lt;strong&gt;why broadcasting (&lt;code&gt;.&lt;/code&gt;) is fast&lt;/strong&gt; in Julia. It's not merely syntactic sugar for a &lt;code&gt;for&lt;/code&gt; loop; it enables a powerful compiler optimization called &lt;strong&gt;loop fusion&lt;/strong&gt;. We also compare allocating vs. in-place operations.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Core Concept: Loop Fusion&lt;/strong&gt;&lt;br&gt;
When Julia encounters a sequence of broadcasted operations like &lt;code&gt;sin.(x .* 2.0 .+ 1.0)&lt;/code&gt;, it &lt;strong&gt;fuses&lt;/strong&gt; them into a &lt;strong&gt;single loop&lt;/strong&gt;. Instead of calculating intermediates and storing them in temporary arrays, Julia compiles code that does all steps for one element at a time, directly writing the final result.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Fused Broadcasting (&lt;code&gt;Method 1&lt;/code&gt;)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The expression &lt;code&gt;sin.(x .* 2.0 .+ 1.0)&lt;/code&gt; is executed in a &lt;strong&gt;single pass&lt;/strong&gt;, allocating only the final result array.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Benchmark:&lt;/strong&gt; Minimal allocations (1 for the result) and fast execution.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Non-Fused Operations (&lt;code&gt;Method 2&lt;/code&gt;)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;temp1 = x .* 2.0; temp2 = temp1 .+ 1.0; result = sin.(temp2)&lt;/code&gt; forces &lt;strong&gt;three separate passes&lt;/strong&gt; and allocates &lt;strong&gt;three large arrays&lt;/strong&gt; (&lt;code&gt;temp1&lt;/code&gt;, &lt;code&gt;temp2&lt;/code&gt;, &lt;code&gt;result&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Benchmark:&lt;/strong&gt; Multiple large allocations and the slowest execution time.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Manual Loop (&lt;code&gt;Method 3&lt;/code&gt;)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Manually writing the loop and pre-allocating the &lt;code&gt;result&lt;/code&gt; also uses a &lt;strong&gt;single pass&lt;/strong&gt; and avoids intermediate allocations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Benchmark:&lt;/strong&gt; Performance similar to Method 1, minimal allocations (1 for the result).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;In-Place Broadcasting on a View (&lt;code&gt;Method 4&lt;/code&gt;)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;.=&lt;/code&gt; Operator:&lt;/strong&gt; The "dot-equals" operator (&lt;code&gt;.=&lt;/code&gt;) performs an &lt;strong&gt;in-place&lt;/strong&gt; broadcasting assignment. &lt;code&gt;y .= f.(x)&lt;/code&gt; calculates &lt;code&gt;f.(x)&lt;/code&gt; element-wise and stores the results directly &lt;em&gt;into the existing array &lt;code&gt;y&lt;/code&gt;&lt;/em&gt;, overwriting its previous contents.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;inplace_calculation_view!&lt;/code&gt;:&lt;/strong&gt; This function takes a view and modifies it directly using &lt;code&gt;.=&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Benchmarking:&lt;/strong&gt; We benchmark modifying a &lt;code&gt;copy&lt;/code&gt; of the view. The &lt;code&gt;@btime&lt;/code&gt; result for this method should show &lt;strong&gt;zero allocations&lt;/strong&gt; related to the data itself (perhaps a few small constant allocations from the benchmark overhead). Its execution time should be very similar to Method 1 and Method 3, confirming that fused broadcasting (Method 1) is essentially as fast as the optimal manual loop (Method 3) and the in-place operation (Method 4), but often more concise.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Performance Takeaway:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Broadcasting (&lt;code&gt;.&lt;/code&gt;) is the idiomatic, readable, and highly performant way to express element-wise operations due to loop fusion. For maximum efficiency when you don't need the original data, use the in-place &lt;code&gt;.=&lt;/code&gt; operator to avoid allocating a result array entirely.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;References:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Performance Tips", "More dots: Fuse vectorized operations":&lt;/strong&gt; Describes loop fusion.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Functions", "Dot Syntax for Vectorizing Functions":&lt;/strong&gt; Introduces &lt;code&gt;.=&lt;/code&gt; for in-place assignment.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;To run the script:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Requires &lt;code&gt;BenchmarkTools.jl&lt;/code&gt; installed: &lt;code&gt;import Pkg; Pkg.add("BenchmarkTools")&lt;/code&gt;)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0069_broadcasting_performance.jl
&lt;span class="nt"&gt;---&lt;/span&gt; Benchmarking Fused Broadcasting &lt;span class="o"&gt;(&lt;/span&gt;Allocating&lt;span class="o"&gt;)&lt;/span&gt;: sin.&lt;span class="o"&gt;(&lt;/span&gt;x .&lt;span class="k"&gt;*&lt;/span&gt; 2.0 .+ 1.0&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
  5.510 ms &lt;span class="o"&gt;(&lt;/span&gt;3 allocations: 7.63 MiB&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nt"&gt;---&lt;/span&gt; Benchmarking Non-Fused Operations &lt;span class="o"&gt;(&lt;/span&gt;Allocating&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
  6.465 ms &lt;span class="o"&gt;(&lt;/span&gt;9 allocations: 22.89 MiB&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nt"&gt;---&lt;/span&gt; Benchmarking Manual Loop &lt;span class="o"&gt;(&lt;/span&gt;Allocating&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
  6.065 ms &lt;span class="o"&gt;(&lt;/span&gt;3 allocations: 7.63 MiB&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nt"&gt;---&lt;/span&gt; Benchmarking In-Place Broadcasting on View &lt;span class="nt"&gt;---&lt;/span&gt;
  5.348 ms &lt;span class="o"&gt;(&lt;/span&gt;0 allocations: 0 bytes&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Module 7: I/O and Concurrency
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Streams And Basic Io
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;0070_streams_intro.md&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Input/Output (I/O) is fundamental to any real-world application, involving reading data from files, writing to the network, or interacting with other processes. Julia provides a clean and unified abstraction for all these operations through the &lt;strong&gt;&lt;code&gt;IO&lt;/code&gt; abstract type&lt;/strong&gt;, often referred to as a &lt;strong&gt;stream&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The &lt;code&gt;IO&lt;/code&gt; Abstraction
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Core Concept:&lt;/strong&gt; &lt;code&gt;abstract type IO end&lt;/code&gt; defines the &lt;strong&gt;interface&lt;/strong&gt; for all byte streams in Julia. It's a contract, not a concrete object. Any type that subtypes &lt;code&gt;IO&lt;/code&gt; represents a sequence of bytes that can be read from or written to.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why Abstract?&lt;/strong&gt; You don't just "read data"; you read data &lt;em&gt;from&lt;/em&gt; something specific (a file, a network socket, an in-memory buffer). The &lt;code&gt;IO&lt;/code&gt; type allows us to write &lt;strong&gt;generic functions&lt;/strong&gt; that work correctly regardless of the underlying source or destination of the bytes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Common Concrete Subtypes:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;IOStream&lt;/code&gt;:&lt;/strong&gt; Represents a file opened on the filesystem. Created by &lt;code&gt;open()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;TCPSocket&lt;/code&gt;:&lt;/strong&gt; Represents a network connection. Created by &lt;code&gt;Sockets.connect()&lt;/code&gt; or &lt;code&gt;Sockets.accept()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Pipe&lt;/code&gt;:&lt;/strong&gt; Represents a connection between processes (e.g., standard input/output).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;IOBuffer&lt;/code&gt;:&lt;/strong&gt; An in-memory buffer that acts like a stream. Useful for building data before writing it elsewhere.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  Generic Stream Functions
&lt;/h2&gt;

&lt;p&gt;The power of the &lt;code&gt;IO&lt;/code&gt; abstraction comes from the generic functions that operate on &lt;em&gt;any&lt;/em&gt; &lt;code&gt;IO&lt;/code&gt; subtype. You don't need separate functions for writing to a file versus writing to a socket.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Writing:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;write(io::IO, x)&lt;/code&gt;: Writes the canonical binary representation of &lt;code&gt;x&lt;/code&gt; to the stream. Crucial for raw data.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;print(io::IO, args...)&lt;/code&gt;: Writes the textual representation of &lt;code&gt;args&lt;/code&gt; (like &lt;code&gt;string(arg)&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;println(io::IO, args...)&lt;/code&gt;: Same as &lt;code&gt;print&lt;/code&gt;, but adds a newline (&lt;code&gt;\n&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Reading:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;read(io::IO, T)&lt;/code&gt;: Reads a single value of binary type &lt;code&gt;T&lt;/code&gt; (e.g., &lt;code&gt;read(io, UInt8)&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;read(io::IO, nb::Integer)&lt;/code&gt;: Reads &lt;code&gt;nb&lt;/code&gt; bytes into a &lt;code&gt;Vector{UInt8}&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;read(io::IO)&lt;/code&gt;: Reads all remaining bytes into a &lt;code&gt;Vector{UInt8}&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;readline(io::IO)&lt;/code&gt;: Reads a line of text (up to &lt;code&gt;\n&lt;/code&gt;), returning it as a &lt;code&gt;String&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;readchomp(io::IO)&lt;/code&gt;: Reads all remaining data as a string, removing trailing whitespace.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;readstring(io::IO)&lt;/code&gt;: Reads all remaining data as a string.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Other Operations:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;close(io::IO)&lt;/code&gt;: Closes the stream, releasing associated resources (like file handles or network ports).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;flush(io::IO)&lt;/code&gt;: Forces any buffered output to be written to the underlying device.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;seek(io::IO, pos)&lt;/code&gt;: Moves the stream's current position (for seekable streams like files or &lt;code&gt;IOBuffer&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;eof(io::IO)&lt;/code&gt;: Checks if the end of the stream has been reached.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  Significance for Systems Programming
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Unified Interface:&lt;/strong&gt; The &lt;code&gt;IO&lt;/code&gt; system means you can write generic data processing logic (e.g., parsing a specific binary format) that works identically whether the data comes from a file, a network socket, or an in-memory buffer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance:&lt;/strong&gt; While the interface is generic, Julia compiles specialized, fast methods for concrete types like &lt;code&gt;IOStream&lt;/code&gt; or &lt;code&gt;TCPSocket&lt;/code&gt;. When you &lt;code&gt;write&lt;/code&gt; to a file, it ultimately compiles down to efficient system calls.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resource Management:&lt;/strong&gt; Understanding that streams represent underlying OS resources (file descriptors, sockets) is crucial. They &lt;strong&gt;must be closed&lt;/strong&gt; to avoid resource leaks. The &lt;code&gt;open(...) do ... end&lt;/code&gt; pattern (next lesson) is the standard, safe way to manage this automatically.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the following lessons, we will see how to create and use specific &lt;code&gt;IO&lt;/code&gt; subtypes like &lt;code&gt;IOStream&lt;/code&gt; and &lt;code&gt;IOBuffer&lt;/code&gt;.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Networking and Streams":&lt;/strong&gt; Introduces the &lt;code&gt;IO&lt;/code&gt; type and basic stream operations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Base Documentation, "I/O and Network":&lt;/strong&gt; Lists the concrete subtypes and the generic functions available for &lt;code&gt;IO&lt;/code&gt; objects.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;0071_file_io.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0071_file_io.jl&lt;/span&gt;

&lt;span class="c"&gt;# Define the filename we'll work with&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"my_test_file.txt"&lt;/span&gt;

&lt;span class="c"&gt;# --- Method 1: The Idiomatic 'do' Block (Recommended) ---&lt;/span&gt;

&lt;span class="c"&gt;# 1. Writing to a file using 'open' with a 'do' block.&lt;/span&gt;
&lt;span class="c"&gt;#    'open(filename, "w")' opens the file for writing ("w").&lt;/span&gt;
&lt;span class="c"&gt;#    If the file exists, it's truncated (emptied). If not, it's created.&lt;/span&gt;
&lt;span class="c"&gt;#    The 'do f -&amp;gt; ... end' syntax passes an anonymous function.&lt;/span&gt;
&lt;span class="c"&gt;#    'f' (an IOStream) is the opened file stream, passed to the function.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Writing using 'open...do' block ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"w"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="c"&gt;# f is the IOStream&lt;/span&gt;
        &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"File opened successfully for writing."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="c"&gt;# Use generic IO functions on the file stream 'f'&lt;/span&gt;
        &lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Hello, file!&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"This is line 2."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# No newline added by print&lt;/span&gt;
        &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Add a newline&lt;/span&gt;
        &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"The value is: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="c"&gt;# The file 'f' is AUTOMATICALLY closed when the 'do' block ends,&lt;/span&gt;
        &lt;span class="c"&gt;# even if an error occurs inside.&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"File writing complete, file closed."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error during file writing: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 2. Reading from a file using 'open' with a 'do' block.&lt;/span&gt;
&lt;span class="c"&gt;#    'open(filename, "r")' or just 'open(filename)' opens for reading ("r").&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Reading using 'open...do' block ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"r"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="c"&gt;# f is the IOStream&lt;/span&gt;
        &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"File opened successfully for reading."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="c"&gt;# Read the entire file content as a single string&lt;/span&gt;
        &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- File Content ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Use print to show exact content&lt;/span&gt;
        &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- End of Content ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="c"&gt;# File 'f' is automatically closed here.&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"File reading complete, file closed."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error during file reading: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# --- Method 2: Manual Open and Close (Use with Caution) ---&lt;/span&gt;

&lt;span class="c"&gt;# 3. Manually opening a file for appending ("a").&lt;/span&gt;
&lt;span class="c"&gt;#    This adds to the end of the file without truncating.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Appending using manual open/close ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;f_manual&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;nothing&lt;/span&gt; &lt;span class="c"&gt;# Initialize outside try block&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="n"&gt;f_manual&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"a"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Open for append&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f_manual&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Appending a new line."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# MUST explicitly close the file!&lt;/span&gt;
    &lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f_manual&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"File appended and manually closed."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error during manual append: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# Ensure close is attempted even if write fails&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;f_manual&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nb"&gt;nothing&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;isopen&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f_manual&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f_manual&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"File closed after error."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# --- Cleanup ---&lt;/span&gt;
&lt;span class="c"&gt;# Remove the test file afterwards&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="n"&gt;rm&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Removed test file: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Error removing test file: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# --- Investigation: IOBuffer Resizing (Not part of article) ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Investigating IOBuffer Resizing ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;investigation_buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;IOBuffer&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Initial state:"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Size: &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;(investigation_buffer.size) bytes"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Capacity (maxsize): &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;(investigation_buffer.maxsize) bytes"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Write ~512 KB&lt;/span&gt;
&lt;span class="n"&gt;kb_512&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;512&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;
&lt;span class="n"&gt;data_512kb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;UInt8&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;kb_512&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;investigation_buffer&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data_512kb&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;After writing 512 KB:"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Size: &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;(investigation_buffer.size) bytes"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Capacity (maxsize): &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;(investigation_buffer.maxsize) bytes"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Should have grown&lt;/span&gt;

&lt;span class="c"&gt;# Take the data&lt;/span&gt;
&lt;span class="n"&gt;taken_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;take!&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;investigation_buffer&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;After take!:"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Size: &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;(investigation_buffer.size) bytes"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Should be 0&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Capacity (maxsize): &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;(investigation_buffer.maxsize) bytes"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Does it reset?&lt;/span&gt;

&lt;span class="c"&gt;# Write ~16 MB&lt;/span&gt;
&lt;span class="n"&gt;mb_16&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;
&lt;span class="n"&gt;data_16mb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;UInt8&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mb_16&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;investigation_buffer&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data_16mb&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;After writing 16 MB:"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Size: &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;(investigation_buffer.size) bytes"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Capacity (maxsize): &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;(investigation_buffer.maxsize) bytes"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Should have grown significantly&lt;/span&gt;

&lt;span class="c"&gt;# Empty the buffer using seekstart + truncate&lt;/span&gt;
&lt;span class="n"&gt;seekstart&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;investigation_buffer&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;truncate&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;investigation_buffer&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;After seekstart() + truncate(0):"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Size: &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;(investigation_buffer.size) bytes"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Should be 0&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Capacity (maxsize): &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;(investigation_buffer.maxsize) bytes"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Does it reset?&lt;/span&gt;

&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;investigation_buffer&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Investigation buffer closed."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script demonstrates basic file Input/Output (I/O) operations in Julia, focusing on the safe and idiomatic &lt;code&gt;open(...) do ... end&lt;/code&gt; pattern.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Core Concept: &lt;code&gt;open()&lt;/code&gt; and &lt;code&gt;IOStream&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
The &lt;code&gt;open(filename, mode)&lt;/code&gt; function interacts with the operating system to access a file.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;filename::String&lt;/code&gt;: The path to the file.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;mode::String&lt;/code&gt; (optional, defaults to &lt;code&gt;"r"&lt;/code&gt;): Specifies how to open the file:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;"r"&lt;/code&gt;: Read (default). File must exist.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;"w"&lt;/code&gt;: Write. Create if non-existent, &lt;strong&gt;truncate (empty)&lt;/strong&gt; if it exists.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;"a"&lt;/code&gt;: Append. Create if non-existent, add to the end if it exists.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;"r+"&lt;/code&gt;: Read and Write. File must exist.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;"w+"&lt;/code&gt;: Read and Write. Create/Truncate.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;"a+"&lt;/code&gt;: Read and Append. Create.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;On success, &lt;code&gt;open&lt;/code&gt; returns an &lt;code&gt;IOStream&lt;/code&gt; object, which is a concrete subtype of the &lt;code&gt;IO&lt;/code&gt; abstract type we discussed. This &lt;code&gt;IOStream&lt;/code&gt; represents the opened file.&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Idiomatic &lt;code&gt;do&lt;/code&gt; Block Pattern (Resource Management)&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The most crucial pattern for file I/O (and other resources like network connections) is &lt;code&gt;open(filename, mode) do file_stream ... end&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1.  `open` acquires the resource (the file handle from the OS).
2.  It passes the opened `IOStream` object (`f` in our example) as an argument to the anonymous function defined by the `do ... end` block.
3.  Your code inside the `do` block operates on the stream `f` using generic `IO` functions like `write`, `println`, `read`.
4.  **Automatic Cleanup:** When the `do` block finishes (either normally or due to an error), Julia **automatically guarantees** that the `close(f)` function is called. This releases the file handle back to the operating system.

&amp;lt;!-- end list --&amp;gt;

  * **Why it's Essential:** Forgetting to `close` files is a common source of bugs and resource leaks. The `do` block makes correct resource management effortless and robust. It's the direct equivalent of Python's `with open(...) as f:` or C\#'s `using`.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Manual &lt;code&gt;open&lt;/code&gt;/&lt;code&gt;close&lt;/code&gt; (Less Safe)&lt;/strong&gt;&lt;br&gt;
You &lt;em&gt;can&lt;/em&gt; manually call &lt;code&gt;f = open(...)&lt;/code&gt; and later &lt;code&gt;close(f)&lt;/code&gt;. However, this is strongly discouraged because it's easy to forget &lt;code&gt;close&lt;/code&gt;, especially if an error occurs between &lt;code&gt;open&lt;/code&gt; and &lt;code&gt;close&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you &lt;em&gt;must&lt;/em&gt; do it manually, you &lt;strong&gt;absolutely must&lt;/strong&gt; use a &lt;code&gt;try...finally&lt;/code&gt; block to guarantee &lt;code&gt;close&lt;/code&gt; is called, as demonstrated (partially) in the append example. The &lt;code&gt;do&lt;/code&gt; block is simply syntactic sugar for this &lt;code&gt;try...finally&lt;/code&gt; pattern.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Generic &lt;code&gt;IO&lt;/code&gt; Functions:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Notice that once the file is opened (&lt;code&gt;f&lt;/code&gt; is an &lt;code&gt;IOStream&lt;/code&gt;), we use the &lt;em&gt;same&lt;/em&gt; functions (&lt;code&gt;write&lt;/code&gt;, &lt;code&gt;println&lt;/code&gt;, &lt;code&gt;read&lt;/code&gt;) that work on &lt;em&gt;any&lt;/em&gt; &lt;code&gt;IO&lt;/code&gt; object. This demonstrates the power of the &lt;code&gt;IO&lt;/code&gt; abstraction.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;References:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Base Documentation, &lt;code&gt;open&lt;/code&gt;:&lt;/strong&gt; Describes the function signatures and modes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Networking and Streams":&lt;/strong&gt; Shows the &lt;code&gt;open(...) do ... end&lt;/code&gt; pattern as the standard way to handle files.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;To run the script:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(This will create and then delete &lt;code&gt;my_test_file.txt&lt;/code&gt; in the current directory.)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0071_file_io.jl
&lt;span class="nt"&gt;---&lt;/span&gt; Writing using &lt;span class="s1"&gt;'open...do'&lt;/span&gt; block &lt;span class="nt"&gt;---&lt;/span&gt;
File opened successfully &lt;span class="k"&gt;for &lt;/span&gt;writing.
File writing &lt;span class="nb"&gt;complete&lt;/span&gt;, file closed.

&lt;span class="nt"&gt;---&lt;/span&gt; Reading using &lt;span class="s1"&gt;'open...do'&lt;/span&gt; block &lt;span class="nt"&gt;---&lt;/span&gt;
File opened successfully &lt;span class="k"&gt;for &lt;/span&gt;reading.
&lt;span class="nt"&gt;---&lt;/span&gt; File Content &lt;span class="nt"&gt;---&lt;/span&gt;
Hello, file!
This is line 2.
The value is: 123
&lt;span class="nt"&gt;---&lt;/span&gt; End of Content &lt;span class="nt"&gt;---&lt;/span&gt;
File reading &lt;span class="nb"&gt;complete&lt;/span&gt;, file closed.

&lt;span class="nt"&gt;---&lt;/span&gt; Appending using manual open/close &lt;span class="nt"&gt;---&lt;/span&gt;
File appended and manually closed.

Removed &lt;span class="nb"&gt;test &lt;/span&gt;file: my_test_file.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;0072_iobuffer.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0072_iobuffer.jl&lt;/span&gt;

&lt;span class="c"&gt;# IOBuffer provides an in-memory I/O stream.&lt;/span&gt;
&lt;span class="c"&gt;# Useful for efficiently building byte sequences or strings&lt;/span&gt;
&lt;span class="c"&gt;# without creating many intermediate objects.&lt;/span&gt;

&lt;span class="c"&gt;# 1. Create an IOBuffer.&lt;/span&gt;
&lt;span class="c"&gt;# By default, it's writable and dynamically sized.&lt;/span&gt;
&lt;span class="n"&gt;io&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;IOBuffer&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;

&lt;span class="c"&gt;# 2. Write data to the buffer using generic IO functions.&lt;/span&gt;
&lt;span class="c"&gt;# These operations append to the buffer.&lt;/span&gt;
&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Hello"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;", "&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Use print for text&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"World!"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Adds a newline character&lt;/span&gt;
&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;UInt8&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0xFF&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# Write a raw byte&lt;/span&gt;

&lt;span class="c"&gt;# 3. Check the current size of the buffer.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Current buffer size: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;" bytes"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 4. Get the buffer's content as a Vector{UInt8}.&lt;/span&gt;
&lt;span class="c"&gt;# 'take!' reads all data *and clears the buffer*.&lt;/span&gt;
&lt;span class="n"&gt;data_bytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;take!&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Data as bytes: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data_bytes&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Type of data: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_bytes&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Buffer size after take!: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Should be 0&lt;/span&gt;

&lt;span class="c"&gt;# --- Re-populate and read as String ---&lt;/span&gt;

&lt;span class="c"&gt;# 5. Write some string data again.&lt;/span&gt;
&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Line 1&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Line 2"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Reading as String ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Buffer size before reading string: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 6. Reading requires 'seeking' back to the beginning.&lt;/span&gt;
&lt;span class="c"&gt;# Buffers maintain a read/write position.&lt;/span&gt;
&lt;span class="n"&gt;seekstart&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Position after seekstart: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;

&lt;span class="c"&gt;# 7. Read the entire buffer content as a String.&lt;/span&gt;
&lt;span class="c"&gt;# This reads from the current position to the end.&lt;/span&gt;
&lt;span class="n"&gt;content_string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Content as string:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content_string&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Type of content: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content_string&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Position after reading string: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# Should be at the end&lt;/span&gt;

&lt;span class="c"&gt;# 8. Using IOBuffer to build a string efficiently.&lt;/span&gt;
&lt;span class="c"&gt;# Contrast with repeated string concatenation (Module 1, lesson 0015)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Efficient String Building ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;IOBuffer&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;
    &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Item "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"; "&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="c"&gt;# Get the final string *once* at the end.&lt;/span&gt;
&lt;span class="n"&gt;final_string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;take!&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Built string: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;final_string&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Close the buffer (optional for IOBuffer, but good practice)&lt;/span&gt;
&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Buffers closed."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;###################################&lt;/span&gt;
&lt;span class="c"&gt;# --- Investigation: IOBuffer Resizing (Not part of article) ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Investigating IOBuffer Resizing ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;investigation_buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;IOBuffer&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Initial state:"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Size: &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;(investigation_buffer.size) bytes"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Capacity (maxsize): &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;(investigation_buffer.maxsize) bytes"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Write ~512 KB&lt;/span&gt;
&lt;span class="n"&gt;kb_512&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;512&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;
&lt;span class="n"&gt;data_512kb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;UInt8&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;kb_512&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;investigation_buffer&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data_512kb&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;After writing 512 KB:"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Size: &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;(investigation_buffer.size) bytes"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Capacity (maxsize): &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;(investigation_buffer.maxsize) bytes"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Should have grown&lt;/span&gt;

&lt;span class="c"&gt;# Take the data&lt;/span&gt;
&lt;span class="n"&gt;taken_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;take!&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;investigation_buffer&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;After take!:"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Size: &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;(investigation_buffer.size) bytes"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Should be 0&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Capacity (maxsize): &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;(investigation_buffer.maxsize) bytes"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Does it reset?&lt;/span&gt;

&lt;span class="c"&gt;# Write ~16 MB&lt;/span&gt;
&lt;span class="n"&gt;mb_16&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;
&lt;span class="n"&gt;data_16mb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;UInt8&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mb_16&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;investigation_buffer&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data_16mb&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;After writing 16 MB:"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Size: &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;(investigation_buffer.size) bytes"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Capacity (maxsize): &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;(investigation_buffer.maxsize) bytes"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Should have grown significantly&lt;/span&gt;

&lt;span class="c"&gt;# Empty the buffer using seekstart + truncate&lt;/span&gt;
&lt;span class="n"&gt;seekstart&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;investigation_buffer&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;truncate&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;investigation_buffer&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;After seekstart() + truncate(0):"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Size: &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;(investigation_buffer.size) bytes"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Should be 0&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Capacity (maxsize): &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;(investigation_buffer.maxsize) bytes"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Does it reset?&lt;/span&gt;

&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;investigation_buffer&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Investigation buffer closed."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;


&lt;span class="c"&gt;# --- Investigation: IOBuffer with Supplied Vector (Not part of article) ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Investigating IOBuffer with Supplied Vector ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 1. Create our initial vector&lt;/span&gt;
&lt;span class="n"&gt;initial_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="c"&gt;# Start small&lt;/span&gt;
&lt;span class="n"&gt;backing_vector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Vector&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;UInt8&lt;/span&gt;&lt;span class="x"&gt;}(&lt;/span&gt;&lt;span class="nb"&gt;undef&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;initial_size&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Initial state:"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Vector length: &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;(length(backing_vector)) bytes"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# We cannot check capacity directly.&lt;/span&gt;

&lt;span class="c"&gt;# 2. Create IOBuffer with the vector, making it writable&lt;/span&gt;
&lt;span class="c"&gt;# WARNING: IOBuffer now "takes ownership" conceptually&lt;/span&gt;
&lt;span class="n"&gt;investigation_buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;IOBuffer&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;backing_vector&lt;/span&gt;&lt;span class="x"&gt;;&lt;/span&gt; &lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"IOBuffer created with backing_vector:"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  IOBuffer size: &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;(investigation_buffer.size) bytes"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Should be 0 initially&lt;/span&gt;

&lt;span class="c"&gt;# 3. Write data *within* the initial size&lt;/span&gt;
&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;investigation_buffer&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Hello"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# 5 bytes &amp;lt; 10&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;After writing 'Hello' (5 bytes):"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  IOBuffer size: &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;(investigation_buffer.size) bytes"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Backing vector length: &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;(length(backing_vector)) bytes"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Should still be 10&lt;/span&gt;

&lt;span class="c"&gt;# 4. Write data that *exceeds* the initial size&lt;/span&gt;
&lt;span class="c"&gt;# This will likely force IOBuffer to resize its internal storage.&lt;/span&gt;
&lt;span class="c"&gt;# It *might* resize our 'backing_vector' in place, or it might&lt;/span&gt;
&lt;span class="c"&gt;# allocate a completely new vector internally.&lt;/span&gt;
&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;investigation_buffer&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;" World! This is a longer string."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# &amp;gt; 10 bytes total&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;After writing more data (exceeding initial 10 bytes):"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  IOBuffer size: &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;(investigation_buffer.size) bytes"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Backing vector length: &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;(length(backing_vector)) bytes"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Did it change? Maybe, maybe not.&lt;/span&gt;

&lt;span class="c"&gt;# 5. Let's see the content via take!&lt;/span&gt;
&lt;span class="n"&gt;seekstart&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;investigation_buffer&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Need to rewind before take!&lt;/span&gt;
&lt;span class="n"&gt;taken_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;take!&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;investigation_buffer&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;After take!:"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Taken data length: &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;(length(taken_data)) bytes"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  IOBuffer size: &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;(investigation_buffer.size) bytes"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Should be 0&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Backing vector length: &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;(length(backing_vector)) bytes"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Unlikely to shrink&lt;/span&gt;

&lt;span class="c"&gt;# 6. Check if the original vector reference was modified (unlikely but possible)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"First 5 bytes of original backing_vector now: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;backing_vector&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="x"&gt;)])&lt;/span&gt;

&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;investigation_buffer&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Investigation buffer closed."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces &lt;code&gt;IOBuffer&lt;/code&gt;, an in-memory byte stream that conforms to the &lt;code&gt;IO&lt;/code&gt; interface. It's a highly useful tool for efficiently building up data (like strings or binary messages) piece by piece before using the final result.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Core Concept:&lt;/strong&gt; An &lt;code&gt;IOBuffer&lt;/code&gt; acts like a virtual file that exists only in RAM. You can &lt;code&gt;write&lt;/code&gt;, &lt;code&gt;print&lt;/code&gt;, &lt;code&gt;read&lt;/code&gt;, &lt;code&gt;seek&lt;/code&gt;, etc., just like with a file (&lt;code&gt;IOStream&lt;/code&gt;), but all operations happen directly in memory, making them very fast.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Creating an &lt;code&gt;IOBuffer&lt;/code&gt;:&lt;/strong&gt; &lt;code&gt;IOBuffer()&lt;/code&gt; creates an empty, dynamically resizable buffer ready for writing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Writing:&lt;/strong&gt; You use the standard &lt;code&gt;IO&lt;/code&gt; functions like &lt;code&gt;write&lt;/code&gt;, &lt;code&gt;print&lt;/code&gt;, and &lt;code&gt;println&lt;/code&gt;. These append data to the buffer, automatically resizing it as needed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Retrieving Data:&lt;/strong&gt; There are two main ways to get the accumulated data out:&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1.  **`take!(io)`:** This function returns the entire contents of the buffer as a `Vector{UInt8}` (a byte array). Crucially, `take!` also **resets the buffer**, making it empty again. This is useful when you want to "consume" the data.
2.  **`seekstart(io)` + `read(io, String)` (or other reads):** `IOBuffer` maintains an internal position for reading and writing. After writing, the position is at the end. To read the data back, you must first move the position to the beginning using `seekstart(io)`. Then, you can use standard read functions like `read(io, String)` to get the content. This method does **not** clear the buffer.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Efficient String Building:&lt;/strong&gt;&lt;br&gt;
A key use case for &lt;code&gt;IOBuffer&lt;/code&gt; is efficiently constructing complex strings. Recall from Module 1 (lesson &lt;code&gt;0015_string_concatenation.jl&lt;/code&gt;) that repeated string concatenation (&lt;code&gt;s *= "part"&lt;/code&gt;) is very slow because it creates many intermediate temporary strings.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The pattern shown here (&lt;code&gt;buffer = IOBuffer(); for ... print(buffer, ...) end; final_string = String(take!(buffer))&lt;/code&gt;) is the &lt;strong&gt;high-performance, idiomatic way&lt;/strong&gt; to build a string from many pieces.&lt;/li&gt;
&lt;li&gt;You perform all the &lt;code&gt;print&lt;/code&gt; operations into the fast, in-memory buffer (which minimizes allocations), and only create the single, final &lt;code&gt;String&lt;/code&gt; object at the very end using &lt;code&gt;String(take!(buffer))&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Resource Management:&lt;/strong&gt; While &lt;code&gt;IOBuffer&lt;/code&gt; doesn't hold an operating system resource like a file handle, it does hold allocated memory. Calling &lt;code&gt;close(io)&lt;/code&gt; signals that the buffer is no longer needed and allows its memory to be garbage collected sooner. It's good practice, though not strictly required as the GC will eventually collect it anyway.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;References:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Base Documentation, &lt;code&gt;IOBuffer&lt;/code&gt;:&lt;/strong&gt; "Create an in-memory I/O stream."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Base Documentation, &lt;code&gt;take!&lt;/code&gt;:&lt;/strong&gt; "Take ownership of the contents of an &lt;code&gt;IOBuffer&lt;/code&gt;... leaving the &lt;code&gt;IOBuffer&lt;/code&gt; empty."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Base Documentation, &lt;code&gt;seekstart&lt;/code&gt;:&lt;/strong&gt; "Seek a stream to its beginning."&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0072_iobuffer.jl
Current buffer size: 15bytes
Data as bytes: UInt8[0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x21, 0x0a, 0xff]
Type of data: Vector&lt;span class="o"&gt;{&lt;/span&gt;UInt8&lt;span class="o"&gt;}&lt;/span&gt;
Buffer size after take!: 0

&lt;span class="nt"&gt;---&lt;/span&gt; Reading as String &lt;span class="nt"&gt;---&lt;/span&gt;
Buffer size before reading string: 13
Position after seekstart: 0
Content as string:
Line 1
Line 2
Type of content: String
Position after reading string: 13

&lt;span class="nt"&gt;---&lt;/span&gt; Efficient String Building &lt;span class="nt"&gt;---&lt;/span&gt;
Build string: Item 1&lt;span class="p"&gt;;&lt;/span&gt; Item 2&lt;span class="p"&gt;;&lt;/span&gt; Item 3&lt;span class="p"&gt;;&lt;/span&gt; Item 4&lt;span class="p"&gt;;&lt;/span&gt; Item 5&lt;span class="p"&gt;;&lt;/span&gt; 
Buffers closed.

&lt;span class="nt"&gt;---&lt;/span&gt; Investigating IOBuffer Resizing &lt;span class="nt"&gt;---&lt;/span&gt;
Initial state:
  Size: 0 bytes
  Capacity &lt;span class="o"&gt;(&lt;/span&gt;maxsize&lt;span class="o"&gt;)&lt;/span&gt;: 9223372036854775807 bytes

After writing 512 KB:
  Size: 524288 bytes
  Capacity &lt;span class="o"&gt;(&lt;/span&gt;maxsize&lt;span class="o"&gt;)&lt;/span&gt;: 9223372036854775807 bytes

After take!:
  Size: 0 bytes
  Capacity &lt;span class="o"&gt;(&lt;/span&gt;maxsize&lt;span class="o"&gt;)&lt;/span&gt;: 9223372036854775807 bytes

After writing 16 MB:
  Size: 16777216 bytes
  Capacity &lt;span class="o"&gt;(&lt;/span&gt;maxsize&lt;span class="o"&gt;)&lt;/span&gt;: 9223372036854775807 bytes

After seekstart&lt;span class="o"&gt;()&lt;/span&gt; + &lt;span class="nb"&gt;truncate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;0&lt;span class="o"&gt;)&lt;/span&gt;:
  Size: 0 bytes
  Capacity &lt;span class="o"&gt;(&lt;/span&gt;maxsize&lt;span class="o"&gt;)&lt;/span&gt;: 9223372036854775807 bytes

Investigation buffer closed.

&lt;span class="nt"&gt;---&lt;/span&gt; Investigating IOBuffer with Supplied Vector &lt;span class="nt"&gt;---&lt;/span&gt;
Initial state:
  Vector length: 10 bytes
IOBuffer created with backing_vector:
  IOBuffer size: 0 bytes

After writing &lt;span class="s1"&gt;'Hello'&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;5 bytes&lt;span class="o"&gt;)&lt;/span&gt;:
  IOBuffer size: 5 bytes
  Backing vector length: 10 bytes

After writing more data &lt;span class="o"&gt;(&lt;/span&gt;exceeding initial 10 bytes&lt;span class="o"&gt;)&lt;/span&gt;:
  IOBuffer size: 37 bytes
  Backing vector length: 10 bytes

After take!:
  Taken data length: 37 bytes
  IOBuffer size: 0 bytes
  Backing vector length: 10 bytes
First 5 bytes of original backing_vector now: UInt8[0x48, 0x65, 0x6c, 0x6c, 0x6f]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Appendix: Investigating IOBuffer Resizing
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;(This section details experiments run after the main script and is for informational purposes)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We performed two experiments to understand &lt;code&gt;IOBuffer&lt;/code&gt;'s memory management:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Default &lt;code&gt;IOBuffer&lt;/code&gt;:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We observed that the &lt;code&gt;io.maxsize&lt;/code&gt; field reported &lt;code&gt;typemax(Int)&lt;/code&gt;, indicating the theoretical maximum size, &lt;strong&gt;not&lt;/strong&gt; the currently allocated capacity.&lt;/li&gt;
&lt;li&gt;Writing data increased &lt;code&gt;io.size&lt;/code&gt;, but &lt;code&gt;io.maxsize&lt;/code&gt; remained unchanged.&lt;/li&gt;
&lt;li&gt;Operations like &lt;code&gt;take!&lt;/code&gt; and &lt;code&gt;truncate&lt;/code&gt; reset &lt;code&gt;io.size&lt;/code&gt; to 0 but did not change &lt;code&gt;io.maxsize&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Conclusion:&lt;/strong&gt; There is no public API to directly inspect the current &lt;em&gt;allocated capacity&lt;/em&gt; of a default &lt;code&gt;IOBuffer&lt;/code&gt;. Julia manages this internally.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;IOBuffer&lt;/code&gt; with a Supplied &lt;code&gt;Vector{UInt8}&lt;/code&gt;:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We created an &lt;code&gt;IOBuffer&lt;/code&gt; using a pre-allocated &lt;code&gt;backing_vector&lt;/code&gt; of size 10, passing &lt;code&gt;write=true&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Writing data &lt;em&gt;within&lt;/em&gt; the initial 10 bytes updated &lt;code&gt;io.size&lt;/code&gt; but left &lt;code&gt;length(backing_vector)&lt;/code&gt; unchanged.&lt;/li&gt;
&lt;li&gt;Writing data &lt;em&gt;exceeding&lt;/em&gt; the initial 10 bytes updated &lt;code&gt;io.size&lt;/code&gt; but &lt;strong&gt;still left &lt;code&gt;length(backing_vector)&lt;/code&gt; unchanged at 10&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Conclusion:&lt;/strong&gt; When the provided vector's capacity was exceeded, the &lt;code&gt;IOBuffer&lt;/code&gt; allocated its own &lt;strong&gt;internal, larger buffer&lt;/strong&gt; rather than resizing the original &lt;code&gt;backing_vector&lt;/code&gt;. The original vector reference remained unchanged and only contained the data written before the resize occurred. This confirms the documentation's warning that &lt;code&gt;IOBuffer&lt;/code&gt; takes ownership and may replace the provided buffer.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;







&lt;h2&gt;
  
  
  Concurrency With Tasks And Channels
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;0073_tasks_async.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0073_tasks_async.jl&lt;/span&gt;

&lt;span class="c"&gt;# 1. Define a function that simulates a slow operation (like I/O).&lt;/span&gt;
&lt;span class="c"&gt;#    'sleep()' yields control to Julia's scheduler, allowing other Tasks to run.&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; slow_operation&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Task &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;id: Starting on thread "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Threads&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;threadid&lt;/span&gt;&lt;span class="x"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Task &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;id: Finished after &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;duration seconds."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# --- Part 1: @async without @sync ---&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Part 1: @async without @sync ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Main code running on thread "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Threads&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;threadid&lt;/span&gt;&lt;span class="x"&gt;())&lt;/span&gt;

&lt;span class="c"&gt;# 2. Launch tasks asynchronously using '@async'.&lt;/span&gt;
&lt;span class="c"&gt;#    '@async' starts the task and immediately returns control.&lt;/span&gt;
&lt;span class="c"&gt;#    The main code continues *without* waiting.&lt;/span&gt;
&lt;span class="n"&gt;t1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;@async&lt;/span&gt; &lt;span class="n"&gt;slow_operation&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;t2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;@async&lt;/span&gt; &lt;span class="n"&gt;slow_operation&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Tasks 1 and 2 launched. Main code continues..."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# The script might end here *before* the tasks finish, depending on timing.&lt;/span&gt;
&lt;span class="c"&gt;# We add a sleep to give them a chance to complete for demonstration.&lt;/span&gt;
&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.5&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Main code finished Part 1."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;


&lt;span class="c"&gt;# --- Part 2: @async within @sync ---&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Part 2: @async within @sync ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Main code starting @sync block..."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 3. Use '@sync' to wait for all enclosed '@async' tasks.&lt;/span&gt;
&lt;span class="nd"&gt;@sync&lt;/span&gt; &lt;span class="k"&gt;begin&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Inside @sync block, launching tasks..."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# These tasks are launched concurrently.&lt;/span&gt;
    &lt;span class="nd"&gt;@async&lt;/span&gt; &lt;span class="n"&gt;slow_operation&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@async&lt;/span&gt; &lt;span class="n"&gt;slow_operation&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Tasks 3 and 4 launched within @sync."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# Control flow waits *here* (at the 'end' of the @sync block)&lt;/span&gt;
    &lt;span class="c"&gt;# until both task 3 and task 4 have completed.&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="c"&gt;# &amp;lt;--- Synchronization point&lt;/span&gt;

&lt;span class="c"&gt;# 4. This line only executes *after* both task 3 and task 4 are finished.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Main code finished @sync block. All tasks completed."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces &lt;strong&gt;&lt;code&gt;Task&lt;/code&gt;s&lt;/strong&gt; and the &lt;strong&gt;&lt;code&gt;@async&lt;/code&gt;&lt;/strong&gt; macro, which are Julia's fundamental tools for &lt;strong&gt;concurrency&lt;/strong&gt;. Concurrency allows managing multiple operations seemingly simultaneously, crucial for responsive applications dealing with I/O or background processing.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Concurrency vs. Parallelism:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Concurrency:&lt;/strong&gt; Managing multiple tasks over time, often interleaving their execution on a &lt;strong&gt;single OS thread&lt;/strong&gt;. Tasks yield control during blocking operations (like I/O or &lt;code&gt;sleep&lt;/code&gt;). This prevents one slow task from blocking others. This is what &lt;code&gt;@async&lt;/code&gt; provides by default.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parallelism:&lt;/strong&gt; Executing multiple tasks simultaneously on &lt;strong&gt;multiple CPU cores&lt;/strong&gt; using multiple OS threads. (This is covered later with &lt;code&gt;Threads.@spawn&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key Point:&lt;/strong&gt; Notice that &lt;code&gt;Threads.threadid()&lt;/code&gt; typically prints &lt;code&gt;1&lt;/code&gt; for all tasks here. &lt;code&gt;@async&lt;/code&gt; achieves concurrency, not necessarily parallelism by default.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;Task&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
A &lt;code&gt;Task&lt;/code&gt; is Julia's basic unit of concurrent execution. It's a lightweight construct (lighter than an OS thread) that represents a computation that can be paused and resumed. They are managed by Julia's cooperative scheduler.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;@async expression&lt;/code&gt;:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This macro takes an expression (like a function call), wraps it in a &lt;code&gt;Task&lt;/code&gt;, and submits it to Julia's scheduler to run &lt;strong&gt;asynchronously&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Non-blocking:&lt;/strong&gt; The key feature is that &lt;code&gt;@async&lt;/code&gt; returns &lt;strong&gt;immediately&lt;/strong&gt;, allowing the code following it to execute without waiting for the task to finish. It returns a &lt;code&gt;Task&lt;/code&gt; object, which is a handle to the running task.&lt;/li&gt;
&lt;li&gt;In Part 1, the main script launches tasks 1 and 2 and continues. Without the &lt;code&gt;sleep(1.5)&lt;/code&gt;, the script might exit before the tasks even get a chance to print their "Finished" messages.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;@sync begin ... end&lt;/code&gt;:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This macro creates a &lt;strong&gt;synchronization point&lt;/strong&gt;. It executes the code within its &lt;code&gt;begin...end&lt;/code&gt; block.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Waiting:&lt;/strong&gt; The crucial behavior is that the code &lt;em&gt;after&lt;/em&gt; the &lt;code&gt;@sync&lt;/code&gt; block's &lt;code&gt;end&lt;/code&gt; will &lt;strong&gt;only execute once all &lt;code&gt;@async&lt;/code&gt; tasks launched &lt;em&gt;directly within&lt;/em&gt; that block have completed&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;In Part 2, tasks 3 and 4 are launched. The &lt;code&gt;@sync&lt;/code&gt; block waits at its &lt;code&gt;end&lt;/code&gt; until both &lt;code&gt;slow_operation(3, ...)&lt;/code&gt; and &lt;code&gt;slow_operation(4, ...)&lt;/code&gt; have finished. Only then does the final &lt;code&gt;println&lt;/code&gt; execute. This guarantees completion.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cooperative Scheduling:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Julia's &lt;code&gt;Task&lt;/code&gt;s are scheduled &lt;strong&gt;cooperatively&lt;/strong&gt;. A &lt;code&gt;Task&lt;/code&gt; runs until it hits an operation that yields control, such as &lt;code&gt;sleep()&lt;/code&gt;, network I/O, &lt;code&gt;yield()&lt;/code&gt;, or waiting on a &lt;code&gt;Channel&lt;/code&gt; (next lesson). This yielding allows the scheduler to run another waiting &lt;code&gt;Task&lt;/code&gt;. This is efficient for I/O-bound workloads but means a CPU-bound task (&lt;code&gt;for i in 1:1e12 end&lt;/code&gt;) will hog the thread unless it explicitly &lt;code&gt;yield&lt;/code&gt;s.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;References:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Asynchronous Programming":&lt;/strong&gt; Explains &lt;code&gt;Task&lt;/code&gt;s, &lt;code&gt;@async&lt;/code&gt;, &lt;code&gt;@sync&lt;/code&gt;, and cooperative scheduling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Base Documentation, &lt;code&gt;@async&lt;/code&gt; and &lt;code&gt;@sync&lt;/code&gt;:&lt;/strong&gt; Detailed descriptions of the macros.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;To run the script:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(The exact interleaving of "Starting" and "Finished" messages may vary slightly due to scheduling.)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0073_tasks_async.jl
&lt;span class="nt"&gt;---&lt;/span&gt; Part 1: @async without @sync &lt;span class="nt"&gt;---&lt;/span&gt;
Main code running on thread 1
Tasks 1 and 2 launched. Main code continues...
Task 1: Starting on thread 1
Task 2: Starting on thread 1
Task 2: Finished after 0.5 seconds.
Task 1: Finished after 1.0 seconds.
Main code finished Part 1.

&lt;span class="nt"&gt;---&lt;/span&gt; Part 2: @async within @sync &lt;span class="nt"&gt;---&lt;/span&gt;
Main code starting @sync block...
Inside @sync block, launching tasks...
Tasks 3 and 4 launched within @sync.
Task 3: Starting on thread 1
Task 4: Starting on thread 1
Task 4: Finished after 0.5 seconds.
Task 3: Finished after 1.0 seconds.
Main code finished @sync block. All tasks completed.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;0074_tasks_fetch.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0074_tasks_fetch.jl&lt;/span&gt;

&lt;span class="c"&gt;# 1. Define a function that returns a value after some work.&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; compute_value&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Task &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;id: Starting computation..."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Simulate work&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Task &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;id: Finished computation, returning &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;result."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="c"&gt;# Return the computed value&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Launching tasks with @async ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 2. Launch tasks asynchronously. '@async' returns Task objects.&lt;/span&gt;
&lt;span class="n"&gt;task_a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;@async&lt;/span&gt; &lt;span class="n"&gt;compute_value&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;task_b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;@async&lt;/span&gt; &lt;span class="n"&gt;compute_value&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Tasks launched. Main code continues..."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Type of task_a: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task_a&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;

&lt;span class="c"&gt;# 3. Use 'fetch()' to wait for a task and get its result.&lt;/span&gt;
&lt;span class="c"&gt;#    'fetch(t)' blocks the *current* task until task 't' completes.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Waiting for Task B..."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;result_b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fetch&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task_b&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Waits for task_b (0.5s)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Result from Task B: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result_b&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Type of result_b: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result_b&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# Int64&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Waiting for Task A..."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;result_a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fetch&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task_a&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Waits for task_a (remaining 0.5s)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Result from Task A: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result_a&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Type of result_a: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result_a&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# Int64&lt;/span&gt;

&lt;span class="c"&gt;# 4. Fetching multiple tasks (often done after a @sync block conceptually)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Fetching after @sync ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;result_c&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result_d&lt;/span&gt; &lt;span class="c"&gt;# Define variables outside the sync block scope&lt;/span&gt;
&lt;span class="nd"&gt;@sync&lt;/span&gt; &lt;span class="k"&gt;begin&lt;/span&gt;
    &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;task_c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;@async&lt;/span&gt; &lt;span class="n"&gt;compute_value&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;task_d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;@async&lt;/span&gt; &lt;span class="n"&gt;compute_value&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# The @sync block waits here until both task_c and task_d finish.&lt;/span&gt;

    &lt;span class="c"&gt;# We can fetch inside the @sync block *after* they finish if needed,&lt;/span&gt;
    &lt;span class="c"&gt;# but often you fetch afterwards. Fetching here is redundant due to @sync.&lt;/span&gt;
    &lt;span class="c"&gt;# result_c = fetch(task_c)&lt;/span&gt;
    &lt;span class="c"&gt;# result_d = fetch(task_d)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="c"&gt;# Both tasks are guaranteed complete now&lt;/span&gt;

&lt;span class="c"&gt;# Fetching after the @sync block is guaranteed not to block&lt;/span&gt;
&lt;span class="c"&gt;# (unless accessing task handles defined outside the block scope requires care).&lt;/span&gt;
&lt;span class="c"&gt;# For tasks defined *inside* @sync, accessing them outside requires care with scope.&lt;/span&gt;
&lt;span class="c"&gt;# A better pattern involves storing tasks in a collection defined outside @sync.&lt;/span&gt;

&lt;span class="c"&gt;# Better pattern for collecting results after @sync&lt;/span&gt;
&lt;span class="n"&gt;tasks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="x"&gt;[]&lt;/span&gt;
&lt;span class="nd"&gt;@sync&lt;/span&gt; &lt;span class="k"&gt;begin&lt;/span&gt;
    &lt;span class="n"&gt;push!&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;@async&lt;/span&gt; &lt;span class="n"&gt;compute_value&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.6&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;push!&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;@async&lt;/span&gt; &lt;span class="n"&gt;compute_value&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="c"&gt;# Both tasks 5 &amp;amp; 6 are done&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Fetching results after @sync using a collection:"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fetch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Use broadcasting '.' for fetch on a collection&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Results [5, 6]: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;


&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Main code finished."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script demonstrates how to retrieve the &lt;strong&gt;return value&lt;/strong&gt; from a concurrently running &lt;code&gt;Task&lt;/code&gt; using the &lt;code&gt;fetch()&lt;/code&gt; function.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Core Concept: Tasks Return Values&lt;/strong&gt;&lt;br&gt;
Just like regular functions, computations wrapped in &lt;code&gt;@async&lt;/code&gt; can &lt;code&gt;return&lt;/code&gt; a value. The &lt;code&gt;@async&lt;/code&gt; macro captures this eventual return value.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;fetch(t::Task)&lt;/code&gt; Function&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;fetch(t)&lt;/code&gt; is the primary mechanism to &lt;strong&gt;wait for a specific task &lt;code&gt;t&lt;/code&gt; to complete&lt;/strong&gt; and then retrieve its return value.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Blocking Behavior:&lt;/strong&gt; If task &lt;code&gt;t&lt;/code&gt; has not yet finished when &lt;code&gt;fetch(t)&lt;/code&gt; is called, the &lt;em&gt;current&lt;/em&gt; task (the one calling &lt;code&gt;fetch&lt;/code&gt;) will &lt;strong&gt;block&lt;/strong&gt; (pause execution and yield control) until task &lt;code&gt;t&lt;/code&gt; completes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Return Value:&lt;/strong&gt; Once task &lt;code&gt;t&lt;/code&gt; completes, &lt;code&gt;fetch(t)&lt;/code&gt; returns the value that the task's expression evaluated to (i.e., the value returned by the function wrapped in &lt;code&gt;@async&lt;/code&gt;). The type of the value returned by &lt;code&gt;fetch&lt;/code&gt; is the type returned by the task's function.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fetching Again:&lt;/strong&gt; If you call &lt;code&gt;fetch(t)&lt;/code&gt; on a task that has already completed, it immediately returns the stored result without blocking.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Example Walkthrough:&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1.  `task_a` and `task_b` are launched concurrently. The main code continues.
2.  `fetch(task_b)` is called. Since `task_b` only needs 0.5s and likely hasn't finished immediately, the main task blocks here.
3.  After \~0.5s, `task_b` finishes, returns `200`. `fetch(task_b)` unblocks and returns `200`.
4.  `fetch(task_a)` is called. `task_a` needs 1.0s total. Since \~0.5s has already passed, the main task blocks for the remaining \~0.5s.
5.  After \~1.0s total, `task_a` finishes, returns `100`. `fetch(task_a)` unblocks and returns `100`.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;fetch&lt;/code&gt; and &lt;code&gt;@sync&lt;/code&gt;:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;@sync&lt;/code&gt; block guarantees that all &lt;code&gt;@async&lt;/code&gt; tasks launched &lt;em&gt;directly within it&lt;/em&gt; are complete before the block finishes.&lt;/li&gt;
&lt;li&gt;Therefore, calling &lt;code&gt;fetch&lt;/code&gt; on a task &lt;em&gt;after&lt;/em&gt; the &lt;code&gt;@sync&lt;/code&gt; block it was defined in will &lt;strong&gt;not block&lt;/strong&gt;, because the task is already guaranteed to be finished.&lt;/li&gt;
&lt;li&gt;A common pattern is to collect &lt;code&gt;Task&lt;/code&gt; objects created within &lt;code&gt;@sync&lt;/code&gt; into an array defined &lt;em&gt;outside&lt;/em&gt; the block, and then use broadcasted &lt;code&gt;fetch.&lt;/code&gt; after the block to gather all results efficiently.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Error Handling:&lt;/strong&gt; If a task terminates due to an exception, &lt;code&gt;fetch(t)&lt;/code&gt; will &lt;strong&gt;re-throw that same exception&lt;/strong&gt; in the calling task. This allows you to handle errors from asynchronous tasks using standard &lt;code&gt;try...catch&lt;/code&gt; blocks around the &lt;code&gt;fetch&lt;/code&gt; call.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;References:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Base Documentation, &lt;code&gt;fetch&lt;/code&gt;:&lt;/strong&gt; "Wait for a &lt;code&gt;Task&lt;/code&gt; to complete and return its value."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Asynchronous Programming":&lt;/strong&gt; Shows examples of using &lt;code&gt;fetch&lt;/code&gt; to get results from tasks.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;To run the script:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Output timing and interleaving may vary slightly.)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0074_tasks_fetch.jl
&lt;span class="nt"&gt;---&lt;/span&gt; Launching tasks with @async &lt;span class="nt"&gt;---&lt;/span&gt;
Tasks launched. Main code continues...
Type of task_a: Task &lt;span class="o"&gt;(&lt;/span&gt;runnable&lt;span class="o"&gt;)&lt;/span&gt; @0x...
Task 1: Starting computation...
Task 2: Starting computation...

Waiting &lt;span class="k"&gt;for &lt;/span&gt;Task B...
Task 2: Finished computation, returning 200.
Result from Task B: 200
Type of result_b: Int64

Waiting &lt;span class="k"&gt;for &lt;/span&gt;Task A...
Task 1: Finished computation, returning 100.
Result from Task A: 100
Type of result_a: Int64

&lt;span class="nt"&gt;---&lt;/span&gt; Fetching after @sync &lt;span class="nt"&gt;---&lt;/span&gt;
Task 5: Starting computation...
Task 6: Starting computation...
Task 6: Finished computation, returning 600.
Task 5: Finished computation, returning 500.
Fetching results after @sync using a collection:
Results &lt;span class="o"&gt;[&lt;/span&gt;5, 6]: &lt;span class="o"&gt;[&lt;/span&gt;500, 600]

Main code finished.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;0075_channels_basics.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0075_channels_basics.jl&lt;/span&gt;

&lt;span class="c"&gt;# 1. Create a Channel.&lt;/span&gt;
&lt;span class="c"&gt;#    A Channel is a thread-safe FIFO (First-In, First-Out) queue&lt;/span&gt;
&lt;span class="c"&gt;#    for passing messages between Tasks.&lt;/span&gt;
&lt;span class="c"&gt;#    Channel{String}(3) creates a channel that can hold Strings,&lt;/span&gt;
&lt;span class="c"&gt;#    with an internal buffer size of 3.&lt;/span&gt;
&lt;span class="n"&gt;chan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Channel&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="x"&gt;}(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 2. Define a "producer" task.&lt;/span&gt;
&lt;span class="c"&gt;#    This task will put data *into* the channel.&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; producer&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Channel&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;num_messages&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Producer &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;id: Starting..."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;num_messages&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Producer &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;id - Message &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;i"&lt;/span&gt;
        &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Producer &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;id: Putting '&lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;message'"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="c"&gt;# 'put!' blocks if the channel buffer is full.&lt;/span&gt;
        &lt;span class="n"&gt;put!&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Simulate some work&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Producer &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;id: Finished putting messages."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# Note: The producer often closes the channel if it's the only one.&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 3. Define a "consumer" task.&lt;/span&gt;
&lt;span class="c"&gt;#    This task will take data *out* of the channel.&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; consumer&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Channel&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Consumer &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;id: Starting..."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# Iterating over a channel is the idiomatic way to consume.&lt;/span&gt;
    &lt;span class="c"&gt;# The loop blocks if the channel is empty and waits for data.&lt;/span&gt;
    &lt;span class="c"&gt;# It automatically terminates when the channel is closed AND empty.&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;
        &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Consumer &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;id: Received '&lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;message'"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Simulate processing&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="c"&gt;# This line is reached only after the channel is closed and emptied.&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Consumer &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;id: Channel closed and empty. Finishing."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Starting Producer/Consumer with Channel ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 4. Launch the tasks concurrently.&lt;/span&gt;
&lt;span class="nd"&gt;@sync&lt;/span&gt; &lt;span class="k"&gt;begin&lt;/span&gt;
    &lt;span class="c"&gt;# Start two consumers listening on the *same* channel.&lt;/span&gt;
    &lt;span class="nd"&gt;@async&lt;/span&gt; &lt;span class="n"&gt;consumer&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chan&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@async&lt;/span&gt; &lt;span class="n"&gt;consumer&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chan&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# Give consumers a moment to start up (optional, for demo clarity)&lt;/span&gt;
    &lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# Start two producers putting data into the *same* channel.&lt;/span&gt;
    &lt;span class="nd"&gt;@async&lt;/span&gt; &lt;span class="n"&gt;producer&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chan&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@async&lt;/span&gt; &lt;span class="n"&gt;producer&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chan&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# Wait here until *all* launched tasks (consumers &amp;amp; producers) finish&lt;/span&gt;
    &lt;span class="c"&gt;# OR until we manually intervene (like closing the channel).&lt;/span&gt;
    &lt;span class="c"&gt;# Since consumers loop until the channel is closed, @sync would wait&lt;/span&gt;
    &lt;span class="c"&gt;# forever without a close operation.&lt;/span&gt;

    &lt;span class="c"&gt;# 5. Wait for producers specifically (alternative to @sync on everything)&lt;/span&gt;
    &lt;span class="c"&gt;#    We need a way to know when all data has been sent before closing.&lt;/span&gt;
    &lt;span class="c"&gt;#    (A more robust system might use multiple channels or atomic counters)&lt;/span&gt;
    &lt;span class="c"&gt;#    For simplicity, we'll just wait a fixed time, assuming producers finish.&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Main: Waiting for producers to likely finish..."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;4.0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Adjust time based on producer work/sleep&lt;/span&gt;

    &lt;span class="c"&gt;# 6. Close the channel.&lt;/span&gt;
    &lt;span class="c"&gt;#    This signals to consumers that no more data will ever be put!.&lt;/span&gt;
    &lt;span class="c"&gt;#    Consumers will finish their current loop iteration and then exit.&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Main: Closing the channel..."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chan&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Main: Channel closed. @sync will now wait for consumers to finish."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="c"&gt;# @sync waits for consumers to exit their loops&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- All tasks finished ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces &lt;strong&gt;&lt;code&gt;Channel&lt;/code&gt;s&lt;/strong&gt;, the primary mechanism in Julia for safe and efficient communication &lt;strong&gt;between concurrent &lt;code&gt;Task&lt;/code&gt;s&lt;/strong&gt;. They act as thread-safe queues for passing messages.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Core Concept:&lt;/strong&gt; A &lt;code&gt;Channel&lt;/code&gt; is like a conveyor belt between tasks. One or more "producer" tasks can &lt;code&gt;put!&lt;/code&gt; items onto the belt, and one or more "consumer" tasks can &lt;code&gt;take!&lt;/code&gt; items off the belt. The channel manages synchronization and buffering automatically.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Creating a Channel: &lt;code&gt;Channel{T}(size)&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Channel{String}(3)&lt;/code&gt; creates a channel designed to hold &lt;code&gt;String&lt;/code&gt; messages.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;size&lt;/code&gt; argument (&lt;code&gt;3&lt;/code&gt; in this case) defines the &lt;strong&gt;buffer capacity&lt;/strong&gt;. This channel can hold up to 3 messages internally before blocking. A &lt;code&gt;size&lt;/code&gt; of 0 creates an unbuffered (rendezvous) channel where &lt;code&gt;put!&lt;/code&gt; blocks until a &lt;code&gt;take!&lt;/code&gt; occurs.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Sending Data: &lt;code&gt;put!(channel, value)&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The producer uses &lt;code&gt;put!(c, message)&lt;/code&gt; to place a message onto the channel.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Blocking Behavior:&lt;/strong&gt; If the channel's buffer is &lt;strong&gt;full&lt;/strong&gt; (already holding &lt;code&gt;size&lt;/code&gt; items), the &lt;code&gt;put!&lt;/code&gt; call will &lt;strong&gt;block&lt;/strong&gt; the producer task until a consumer task calls &lt;code&gt;take!&lt;/code&gt; and makes space.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Receiving Data: &lt;code&gt;take!(channel)&lt;/code&gt; or Iteration&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1.  **`take!(channel)`:** Explicitly removes and returns one item from the channel. If the channel is **empty**, `take!` **blocks** the consumer task until a producer `put!`s an item.
2.  **Iteration (`for message in channel`):** This is the **idiomatic** way to consume data. The `for` loop automatically calls `take!` internally.
      * It **blocks** if the channel is empty, waiting for the next item.
      * It **automatically terminates** only when two conditions are met: the channel has been `close`d AND the buffer is empty.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Closing the Channel: &lt;code&gt;close(channel)&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;close(c)&lt;/code&gt; signals that &lt;strong&gt;no more items will ever be &lt;code&gt;put!&lt;/code&gt;&lt;/strong&gt; into the channel.&lt;/li&gt;
&lt;li&gt;This is crucial for terminating consumer loops that iterate (&lt;code&gt;for message in c&lt;/code&gt;). Once closed, &lt;code&gt;put!&lt;/code&gt; will error. &lt;code&gt;take!&lt;/code&gt; and iteration will continue to drain any remaining items in the buffer and then stop.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Thread Safety:&lt;/strong&gt; Channels are &lt;strong&gt;guaranteed to be thread-safe&lt;/strong&gt;. You can have multiple producers and multiple consumers interacting with the same channel from different tasks (and potentially different OS threads if using &lt;code&gt;Threads.@spawn&lt;/code&gt;) without needing any external locks. The channel handles all the internal synchronization.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Producer/Consumer Pattern:&lt;/strong&gt; This example demonstrates the classic producer-consumer pattern. Producers generate data independently, and consumers process data independently, decoupled by the channel acting as a synchronized buffer. This is fundamental for building concurrent systems (e.g., one task reads network data, puts messages on a channel, another task processes those messages).&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;References:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Asynchronous Programming", "Channels":&lt;/strong&gt; Introduces channels for inter-task communication.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Base Documentation, &lt;code&gt;Channel&lt;/code&gt;, &lt;code&gt;put!&lt;/code&gt;, &lt;code&gt;take!&lt;/code&gt;, &lt;code&gt;close&lt;/code&gt;:&lt;/strong&gt; Detailed API descriptions.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;To run the script:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(The exact order of messages will vary due to concurrent execution and random sleeps, but all messages should be produced and consumed.)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0075_channels_basics.jl
&lt;span class="nt"&gt;---&lt;/span&gt; Starting Producer/Consumer with Channel &lt;span class="nt"&gt;---&lt;/span&gt;
Inside @sync block, launching tasks...
Consumer 1: Starting...
Consumer 2: Starting...
Main: Waiting &lt;span class="k"&gt;for &lt;/span&gt;producers to likely finish...
Producer 1: Starting...
Producer 1: Putting &lt;span class="s1"&gt;'Producer 1 - Message 1'&lt;/span&gt;
Producer 2: Starting...
Producer 2: Putting &lt;span class="s1"&gt;'Producer 2 - Message 1'&lt;/span&gt;
Consumer 1: Received &lt;span class="s1"&gt;'Producer 1 - Message 1'&lt;/span&gt;
Consumer 2: Received &lt;span class="s1"&gt;'Producer 2 - Message 1'&lt;/span&gt;
Producer 1: Putting &lt;span class="s1"&gt;'Producer 1 - Message 2'&lt;/span&gt;
Producer 2: Putting &lt;span class="s1"&gt;'Producer 2 - Message 2'&lt;/span&gt;
Consumer 1: Received &lt;span class="s1"&gt;'Producer 1 - Message 2'&lt;/span&gt;
Consumer 2: Received &lt;span class="s1"&gt;'Producer 2 - Message 2'&lt;/span&gt;
Producer 1: Putting &lt;span class="s1"&gt;'Producer 1 - Message 3'&lt;/span&gt;
Producer 2: Putting &lt;span class="s1"&gt;'Producer 2 - Message 3'&lt;/span&gt;
Consumer 1: Received &lt;span class="s1"&gt;'Producer 1 - Message 3'&lt;/span&gt;
Producer 2: Finished putting messages.
Consumer 2: Received &lt;span class="s1"&gt;'Producer 2 - Message 3'&lt;/span&gt;
Producer 1: Putting &lt;span class="s1"&gt;'Producer 1 - Message 4'&lt;/span&gt;
Consumer 1: Received &lt;span class="s1"&gt;'Producer 1 - Message 4'&lt;/span&gt;
Producer 1: Finished putting messages.
Main: Closing the channel...
Main: Channel closed. @sync will now &lt;span class="nb"&gt;wait &lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;consumers to finish.
Consumer 2: Channel closed and empty. Finishing.
Consumer 1: Channel closed and empty. Finishing.
&lt;span class="nt"&gt;---&lt;/span&gt; All tasks finished &lt;span class="nt"&gt;---&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Network Programming Sockets
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;0076_sockets_tcp_server.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0076_sockets_tcp_server.jl&lt;/span&gt;

&lt;span class="c"&gt;# Import the Sockets standard library&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Sockets&lt;/span&gt;

&lt;span class="c"&gt;# Define the host IP and port to listen on.&lt;/span&gt;
&lt;span class="c"&gt;# Sockets.localhost (typically 127.0.0.1) means listen only for connections&lt;/span&gt;
&lt;span class="c"&gt;# from the same machine. Use Sockets.ip"0.0.0.0" to listen on all interfaces.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;HOST&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Sockets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;localhost&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8080&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Starting TCP Echo Server ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Listening on &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;HOST:&lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;PORT..."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 1. Create a TCP Server object.&lt;/span&gt;
&lt;span class="c"&gt;#    'listen()' binds to the address and starts listening for connections.&lt;/span&gt;
&lt;span class="c"&gt;#    It returns a TCPServer object, which is itself an IO stream used&lt;/span&gt;
&lt;span class="c"&gt;#    only for accepting new connections.&lt;/span&gt;
&lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Sockets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;listen&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HOST&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PORT&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="c"&gt;# 2. Loop indefinitely to accept incoming connections.&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
        &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Server: Waiting for a new client connection..."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="c"&gt;# 3. Accept a connection.&lt;/span&gt;
        &lt;span class="c"&gt;#    'accept()' blocks until a client connects.&lt;/span&gt;
        &lt;span class="c"&gt;#    It returns a TCPSocket object representing the connection to *that* client.&lt;/span&gt;
        &lt;span class="c"&gt;#    The TCPSocket is also an IO stream (subtype of IO).&lt;/span&gt;
        &lt;span class="n"&gt;client_socket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Sockets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;accept&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;client_addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Sockets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getpeername&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client_socket&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Get client IP and port&lt;/span&gt;
        &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Server: Accepted connection from &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;client_addr"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

        &lt;span class="c"&gt;# 4. Handle the client connection asynchronously.&lt;/span&gt;
        &lt;span class="c"&gt;#    We launch a new Task for each client using '@async'.&lt;/span&gt;
        &lt;span class="c"&gt;#    This allows the server to immediately go back to 'accept()'&lt;/span&gt;
        &lt;span class="c"&gt;#    and handle other clients concurrently without blocking.&lt;/span&gt;
        &lt;span class="nd"&gt;@async&lt;/span&gt; &lt;span class="k"&gt;begin&lt;/span&gt;
            &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  [Client &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;client_addr]: Handling connection in new Task."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt;
                &lt;span class="c"&gt;# 5. Interact with the client using the client_socket IO stream.&lt;/span&gt;
                &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;eof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client_socket&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Loop until client closes connection&lt;/span&gt;
                    &lt;span class="c"&gt;# Read a line of text sent by the client.&lt;/span&gt;
                    &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;readline&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client_socket&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
                    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  [Client &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;client_addr]: Received: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;repr&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# repr shows quotes/newlines&lt;/span&gt;

                    &lt;span class="c"&gt;# Check if client wants to quit&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"quit"&lt;/span&gt;
                        &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  [Client &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;client_addr]: Quit command received. Closing connection."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
                        &lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client_socket&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Goodbye!&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
                        &lt;span class="n"&gt;break&lt;/span&gt; &lt;span class="c"&gt;# Exit the while loop for this client&lt;/span&gt;
                    &lt;span class="k"&gt;end&lt;/span&gt;

                    &lt;span class="c"&gt;# Echo the line back to the client.&lt;/span&gt;
                    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Server Echo: "&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
                    &lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client_socket&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
                    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  [Client &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;client_addr]: Sent: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;repr&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
                &lt;span class="k"&gt;end&lt;/span&gt;
            &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
                &lt;span class="c"&gt;# Handle potential errors during client communication (e.g., connection reset)&lt;/span&gt;
                &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  [Client &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;client_addr]: Error: &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;e"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;finally&lt;/span&gt;
                &lt;span class="c"&gt;# 6. Ensure the client socket is closed when done or on error.&lt;/span&gt;
                &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  [Client &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;client_addr]: Closing socket."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client_socket&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;end&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="c"&gt;# End of @async block for this client&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="c"&gt;# End of while true loop (accepting connections)&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="c"&gt;# Handle potential errors with the server itself (e.g., port already in use)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Server Error: &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;e"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;finally&lt;/span&gt;
    &lt;span class="c"&gt;# 7. Ensure the main server socket is closed when the server stops.&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Server: Shutting down."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script demonstrates how to create a basic &lt;strong&gt;TCP server&lt;/strong&gt; using Julia's built-in &lt;code&gt;Sockets&lt;/code&gt; standard library. The server listens for incoming connections and handles each client concurrently using &lt;code&gt;@async&lt;/code&gt;, echoing back any text the client sends.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Core Concept: Server Socket vs. Client Socket&lt;/strong&gt;
Networking involves two types of sockets:&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1.  **Server Socket (`TCPServer`):** Created by `Sockets.listen()`. Its *only* job is to wait for incoming connection requests on a specific IP address and port. It acts like a receptionist waiting for the phone to ring.
2.  **Client Socket (`TCPSocket`):** Created by `Sockets.accept()` on the server side (or `Sockets.connect()` on the client side). This represents the **actual two-way communication channel** with a *specific* client. It's the phone line used for the conversation after the receptionist connects the call. Both `TCPServer` and `TCPSocket` are subtypes of `IO`.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Steps to Create a Server:&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1.  **`Sockets.listen(HOST, PORT)`:** Binds the server to the specified `HOST` IP address and `PORT` number. If the port is already in use, this will error. It returns the `TCPServer` object.
2.  **`while true ... Sockets.accept(server) ... end`:** The main server loop. `Sockets.accept(server)` **blocks** execution until a client attempts to connect. When a client connects, `accept` returns a **new `TCPSocket` object** dedicated to that client.
3.  **`@async begin ... end`:** To handle multiple clients simultaneously, we immediately launch a **new `Task`** using `@async` to handle the `client_socket`. The main server loop then instantly goes back to `accept`, ready for the next client, without waiting for the first client's session to finish. This is crucial for server responsiveness.
4.  **Client Handling Loop (`while !eof(...) ... end`):** Inside the `@async` block, we interact with the specific client using the `client_socket` (which is an `IO` stream). We use standard `IO` functions like `readline()` to receive data and `write()` to send data. The `eof(client_socket)` function checks if the client has closed their end of the connection. `Sockets.getpeername(client_socket)` retrieves the IP address and port of the connected client.
5.  **`close(client_socket)`:** When communication with a specific client is finished (or an error occurs), its dedicated `TCPSocket` **must be closed** within the `@async` task to release the connection resources. Using `try...finally` guarantees this.
6.  **`close(server)`:** When the server itself shuts down (e.g., due to an error or `Ctrl+C`), the main listening `TCPServer` socket must also be closed to unbind the port. The outer `try...finally` ensures this.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Concurrency Model:&lt;/strong&gt;&lt;br&gt;
This server uses the &lt;strong&gt;Task-per-Client&lt;/strong&gt; concurrency model. Each incoming connection spawns a new Julia &lt;code&gt;Task&lt;/code&gt;. Thanks to Julia's efficient, non-blocking I/O and lightweight tasks, this model can handle many concurrent connections effectively on a single OS thread (though multi-threading can be added for CPU-bound work within tasks).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;repr()&lt;/code&gt; Function:&lt;/strong&gt; We use &lt;code&gt;repr(line)&lt;/code&gt; in the output. This function provides a string representation that includes quotes and escape sequences (like &lt;code&gt;\n&lt;/code&gt;), making it clearer exactly what data was received or sent over the network.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;References:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Standard Library, &lt;code&gt;Sockets&lt;/code&gt;:&lt;/strong&gt; Documents &lt;code&gt;listen&lt;/code&gt;, &lt;code&gt;accept&lt;/code&gt;, &lt;code&gt;connect&lt;/code&gt;, &lt;code&gt;getpeername&lt;/code&gt;, &lt;code&gt;TCPSocket&lt;/code&gt;, etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Networking and Streams":&lt;/strong&gt; Provides examples of socket programming.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;To run the script:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Save the code as &lt;code&gt;0076_sockets_tcp_server.jl&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; Run it from your terminal: &lt;code&gt;julia 0076_sockets_tcp_server.jl&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt; The server will start and print &lt;code&gt;Listening on 127.0.0.1:8080...&lt;/code&gt; and &lt;code&gt;Waiting for a new client connection...&lt;/code&gt;. It is now waiting.&lt;/li&gt;
&lt;li&gt; You will need a &lt;strong&gt;client&lt;/strong&gt; (like the one in the next lesson, or a tool like &lt;code&gt;telnet&lt;/code&gt; or &lt;code&gt;netcat&lt;/code&gt;) to connect to it. For example, in another terminal: &lt;code&gt;telnet 127.0.0.1 8080&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; Type messages in the &lt;code&gt;telnet&lt;/code&gt; window and press Enter. The server should echo them back. Type &lt;code&gt;quit&lt;/code&gt; to disconnect that client.&lt;/li&gt;
&lt;li&gt; Press &lt;code&gt;Ctrl+C&lt;/code&gt; in the server's terminal to stop it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;(Expected output when running and connecting with a client will show the accept/receive/send messages, including the client address from &lt;code&gt;getpeername&lt;/code&gt;.)&lt;/em&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;0077_sockets_tcp_client.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0077_sockets_tcp_client.jl&lt;/span&gt;

&lt;span class="c"&gt;# Import the Sockets standard library&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Sockets&lt;/span&gt;

&lt;span class="c"&gt;# Define the host and port of the server we want to connect to.&lt;/span&gt;
&lt;span class="c"&gt;# This should match the HOST and PORT in the server script (0076).&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;SERVER_HOST&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Sockets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;localhost&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;SERVER_PORT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8080&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Starting TCP Client ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Attempting to connect to &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;SERVER_HOST:&lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;SERVER_PORT..."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Initialize socket variable for the finally block&lt;/span&gt;
&lt;span class="c"&gt;# We use 'Ref' trick or declare outside if needed in 'finally' reliably&lt;/span&gt;
&lt;span class="c"&gt;# Simpler: Use @isdefined check in finally block&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;client_socket&lt;/span&gt; &lt;span class="c"&gt;# Declare local to avoid scope ambiguity warning&lt;/span&gt;
    &lt;span class="c"&gt;# 1. Connect to the server.&lt;/span&gt;
    &lt;span class="c"&gt;#    'Sockets.connect()' attempts to establish a TCP connection.&lt;/span&gt;
    &lt;span class="c"&gt;#    It blocks until the connection succeeds or fails.&lt;/span&gt;
    &lt;span class="c"&gt;#    On success, it returns a TCPSocket representing the connection.&lt;/span&gt;
    &lt;span class="n"&gt;client_socket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Sockets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SERVER_HOST&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SERVER_PORT&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;server_addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Sockets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getpeername&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client_socket&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Get server IP and port&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Successfully connected to server at &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;server_addr"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# 2. Start interaction loop.&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Enter messages to send to the server. Type 'quit' to exit."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
        &lt;span class="c"&gt;# Read a line of input from the user's terminal (stdin).&lt;/span&gt;
        &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&amp;gt; "&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Prompt&lt;/span&gt;
        &lt;span class="n"&gt;user_input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;readline&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;

        &lt;span class="c"&gt;# Send the user's input to the server using 'write()'.&lt;/span&gt;
        &lt;span class="c"&gt;# We must add the newline character for the server's 'readline()'.&lt;/span&gt;
        &lt;span class="n"&gt;bytes_written&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client_socket&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_input&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Client: Sent &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;bytes_written bytes: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;repr&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_input&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;

        &lt;span class="c"&gt;# If the user typed 'quit', break the loop after sending.&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user_input&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"quit"&lt;/span&gt;
            &lt;span class="c"&gt;# Read the server's final "Goodbye!" message before closing.&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;eof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client_socket&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;server_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;readline&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client_socket&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Client: Received: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;repr&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server_response&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
            &lt;span class="k"&gt;end&lt;/span&gt;
            &lt;span class="n"&gt;break&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;

        &lt;span class="c"&gt;# Read the server's echo response using 'readline()'.&lt;/span&gt;
        &lt;span class="c"&gt;# This blocks until the server sends a line ending in '\n'.&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;eof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client_socket&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Check if server closed connection unexpectedly&lt;/span&gt;
            &lt;span class="n"&gt;server_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;readline&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client_socket&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Client: Received: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;repr&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server_response&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
            &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Client: Server closed the connection unexpectedly."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;break&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="c"&gt;# End of while loop&lt;/span&gt;

&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="c"&gt;# Handle connection errors (e.g., server not running)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Client Error: &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;e"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Ensure the server script (0076_sockets_tcp_server.jl) is running."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;finally&lt;/span&gt;
    &lt;span class="c"&gt;# 3. Ensure the socket is closed if it was successfully opened.&lt;/span&gt;
    &lt;span class="c"&gt;#    Check '@isdefined' in case 'connect' failed before assignment.&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nd"&gt;@isdefined&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client_socket&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;client_socket&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nb"&gt;nothing&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;isopen&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client_socket&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Client: Closing connection."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client_socket&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Client finished."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script demonstrates how to create a simple &lt;strong&gt;TCP client&lt;/strong&gt; using the &lt;code&gt;Sockets&lt;/code&gt; library. It connects to the echo server created in the previous lesson (&lt;code&gt;0076_sockets_tcp_server.jl&lt;/code&gt;), sends user input to it, and prints the server's response.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Core Concept: Client Connection&lt;/strong&gt;&lt;br&gt;
While a server &lt;code&gt;listen&lt;/code&gt;s and &lt;code&gt;accept&lt;/code&gt;s, a client actively initiates a connection using &lt;code&gt;Sockets.connect()&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Steps to Create a Client:&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1.  **`Sockets.connect(HOST, PORT)`:** This function attempts to establish a TCP connection to the server running at the specified `HOST` and `PORT`.
      * **Blocking:** This call **blocks** until the TCP handshake completes successfully or an error occurs (e.g., the server isn't running (`ECONNREFUSED`), a firewall blocks the connection, or it times out).
      * **Return Value:** On success, it returns a `TCPSocket` object, which is an `IO` stream representing the established two-way communication channel with the server.
2.  **Interact using `IO` functions:** Once connected, the `client_socket` is used just like any other `IO` stream (e.g., the file stream from `open()`).
      * **`write(socket, data)`:** Sends data *to* the server. We append `\n` because our server uses `readline()`, which expects newline-terminated messages.
      * **`readline(socket)`:** Reads data *from* the server, blocking until a complete line (ending in `\n`) is received.
      * **`eof(socket)`:** Checks if the server has closed its end of the connection.
3.  **`close(socket)`:** When the client is finished interacting, it **must close** its socket using `close(client_socket)`. This signals the server that the conversation is over and releases the associated operating system resources. Using `try...finally` ensures the socket is closed even if errors occur during communication. The `@isdefined` check in `finally` ensures we don't try to close a socket that was never successfully created (e.g., if `connect` itself failed).
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Client-Server Interaction:&lt;/strong&gt;&lt;br&gt;
This script, together with the server script, forms a complete client-server application.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The client connects.&lt;/li&gt;
&lt;li&gt;The client reads user input from the terminal (&lt;code&gt;stdin&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;The client sends the input (plus &lt;code&gt;\n&lt;/code&gt;) to the server (&lt;code&gt;write&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;The server reads the line (&lt;code&gt;readline&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;The server sends the echoed response (plus &lt;code&gt;\n&lt;/code&gt;) back (&lt;code&gt;write&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;The client reads the echo (&lt;code&gt;readline&lt;/code&gt;) and displays it.&lt;/li&gt;
&lt;li&gt;This continues until the client sends &lt;code&gt;"quit"&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Error Handling:&lt;/strong&gt; The &lt;code&gt;try...catch&lt;/code&gt; block is essential for handling potential network errors, most commonly &lt;code&gt;Sockets.ECONNREFUSED&lt;/code&gt; if the server is not running when the client tries to connect.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;References:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Standard Library, &lt;code&gt;Sockets&lt;/code&gt;:&lt;/strong&gt; Documents &lt;code&gt;connect&lt;/code&gt;, &lt;code&gt;TCPSocket&lt;/code&gt;, etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Base Documentation, &lt;code&gt;readline&lt;/code&gt;:&lt;/strong&gt; "Read a single line of text from the given I/O stream..."&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;To run the script:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Start the server first:&lt;/strong&gt; In one terminal, run &lt;code&gt;julia 0076_sockets_tcp_server.jl&lt;/code&gt;. Wait until it says &lt;code&gt;Waiting for a new client connection...&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Run the client:&lt;/strong&gt; In a &lt;em&gt;second&lt;/em&gt; terminal, run &lt;code&gt;julia 0077_sockets_tcp_client.jl&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; You should see the client connect successfully.&lt;/li&gt;
&lt;li&gt; Type messages (e.g., &lt;code&gt;Hello Server!&lt;/code&gt;) in the client terminal and press Enter. The server should echo them back.&lt;/li&gt;
&lt;li&gt; Type &lt;code&gt;quit&lt;/code&gt; in the client terminal to disconnect cleanly.&lt;/li&gt;
&lt;li&gt; You can then stop the server with &lt;code&gt;Ctrl+C&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;(Expected output will show the connection message, prompts, sent/received lines, and disconnection messages.)&lt;/em&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Module 8: Project Tooling
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Package Management
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;0078_pkg_mode.md&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Julia comes with a built-in &lt;strong&gt;package manager&lt;/strong&gt;, &lt;code&gt;Pkg&lt;/code&gt;, which handles installing, updating, and managing project dependencies (the libraries your code uses). The easiest way to interact with &lt;code&gt;Pkg&lt;/code&gt; is through its dedicated &lt;strong&gt;REPL mode&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Entering and Exiting Pkg Mode
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;How to Enter:&lt;/strong&gt; From the standard Julia REPL (&lt;code&gt;julia&amp;gt;&lt;/code&gt;), simply type the &lt;strong&gt;right square bracket &lt;code&gt;]&lt;/code&gt;&lt;/strong&gt; key. The prompt will change to a blue &lt;code&gt;pkg&amp;gt;&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="n"&gt;julia&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="x"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;pkg&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;How to Exit:&lt;/strong&gt; Press &lt;strong&gt;Backspace&lt;/strong&gt; (if the current line is empty) or &lt;strong&gt;Ctrl+C&lt;/strong&gt;. The prompt will return to &lt;code&gt;julia&amp;gt;&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Basic Pkg Commands
&lt;/h2&gt;

&lt;p&gt;Once in &lt;code&gt;pkg&amp;gt;&lt;/code&gt; mode, you use simple commands to manage your environment:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;status&lt;/code&gt; (or &lt;code&gt;st&lt;/code&gt;)&lt;/strong&gt;: Shows the packages currently installed in the &lt;strong&gt;active environment&lt;/strong&gt;, along with their versions. This is the first command you should use to see what's going on.&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;``&lt;code&gt;pkg&amp;gt; st&lt;br&gt;
Status&lt;/code&gt;~/.julia/environments/v1.10/Project.toml`&lt;br&gt;
[7876af07] Example v0.5.1&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;activate .&lt;/code&gt;&lt;/strong&gt;: This is crucial for &lt;strong&gt;project-specific environments&lt;/strong&gt;. It tells Pkg to manage dependencies for the &lt;em&gt;current directory&lt;/em&gt; (&lt;code&gt;.&lt;/code&gt;). If &lt;code&gt;Project.toml&lt;/code&gt; and &lt;code&gt;Manifest.toml&lt;/code&gt; files don't exist, it creates them. If they do exist, it makes that project the active environment. &lt;strong&gt;Always use this&lt;/strong&gt; when starting a new project.&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;``&lt;code&gt;pkg&amp;gt; activate .&lt;br&gt;
Activating project at&lt;/code&gt;~/MyJuliaProject`&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;add PackageName&lt;/code&gt;&lt;/strong&gt;: Adds a package (like &lt;code&gt;BenchmarkTools&lt;/code&gt; or &lt;code&gt;JSON&lt;/code&gt;) to the active environment. Pkg downloads it from the central registry, resolves its dependencies, and adds it to your &lt;code&gt;Project.toml&lt;/code&gt; and &lt;code&gt;Manifest.toml&lt;/code&gt; files.&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;``&lt;code&gt;pkg&amp;gt; add BenchmarkTools&lt;br&gt;
Resolving package versions...&lt;br&gt;
Updating&lt;/code&gt;~/MyJuliaProject/Project.toml&lt;code&gt;&lt;br&gt;
[6e4b80f9] + BenchmarkTools&lt;br&gt;
Updating&lt;/code&gt;~/MyJuliaProject/Manifest.toml`&lt;br&gt;
[...]&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;rm PackageName&lt;/code&gt;&lt;/strong&gt;: Removes a package from the active environment.&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;``&lt;code&gt;pkg&amp;gt; rm BenchmarkTools&lt;br&gt;
Updating&lt;/code&gt;~/MyJuliaProject/Project.toml&lt;code&gt;&lt;br&gt;
[6e4b80f9] - BenchmarkTools&lt;br&gt;
Updating&lt;/code&gt;~/MyJuliaProject/Manifest.toml`&lt;br&gt;
[...]&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;update&lt;/code&gt; (or &lt;code&gt;up&lt;/code&gt;)&lt;/strong&gt;: Updates all packages in the active environment to their latest compatible versions, respecting the constraints in &lt;code&gt;Project.toml&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;``&lt;code&gt;pkg&amp;gt; up&lt;br&gt;
Updating registry at&lt;/code&gt;~/.julia/registries/General.toml&lt;code&gt;&lt;br&gt;
No Changes to&lt;/code&gt;~/MyJuliaProject/Project.toml&lt;code&gt;&lt;br&gt;
No Changes to&lt;/code&gt;~/MyJuliaProject/Manifest.toml`&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;help&lt;/code&gt;&lt;/strong&gt;: Shows a list of available Pkg commands.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why Environments Matter
&lt;/h2&gt;

&lt;p&gt;Using &lt;code&gt;activate .&lt;/code&gt; creates an &lt;strong&gt;isolated environment&lt;/strong&gt; for each project. This means:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Reproducibility:&lt;/strong&gt; Project A can use version 1.0 of a package, while Project B uses version 2.0, without conflicts. The &lt;code&gt;Manifest.toml&lt;/code&gt; file (next lesson) records the &lt;em&gt;exact&lt;/em&gt; versions, ensuring anyone else can reproduce your environment perfectly.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Dependency Management:&lt;/strong&gt; Pkg handles finding and installing all the &lt;em&gt;indirect&lt;/em&gt; dependencies (libraries that your libraries depend on).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The &lt;code&gt;pkg&amp;gt;&lt;/code&gt; mode provides a convenient, interactive way to manage these environments directly within Julia.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, &lt;code&gt;Pkg.jl&lt;/code&gt; Manual:&lt;/strong&gt; Comprehensive guide to the package manager.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Getting Started", "Interacting With Julia":&lt;/strong&gt; Briefly mentions the REPL modes including Pkg mode.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;0079_project_manifest.md&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;When you use &lt;code&gt;Pkg.jl&lt;/code&gt; commands like &lt;code&gt;activate .&lt;/code&gt; and &lt;code&gt;add PackageName&lt;/code&gt;, two crucial files are created and managed in your project directory: &lt;code&gt;Project.toml&lt;/code&gt; and &lt;code&gt;Manifest.toml&lt;/code&gt;. Understanding their roles is essential for managing dependencies and ensuring your project is &lt;strong&gt;reproducible&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;code&gt;Project.toml&lt;/code&gt; - Your Direct Dependencies
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Purpose:&lt;/strong&gt; This file lists the packages that your project &lt;strong&gt;directly depends on&lt;/strong&gt;. It specifies the &lt;strong&gt;names&lt;/strong&gt; of these packages and the &lt;strong&gt;range of versions&lt;/strong&gt; that are compatible with your code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Format:&lt;/strong&gt; It uses the &lt;a href="https://toml.io/en/" rel="noopener noreferrer"&gt;TOML&lt;/a&gt; (Tom's Obvious, Minimal Language) format, which is designed to be easy for humans to read.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Example &lt;code&gt;Project.toml&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[deps]&lt;/span&gt;
&lt;span class="py"&gt;BenchmarkTools&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"&lt;/span&gt;
&lt;span class="py"&gt;JSON&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"682c06a0-de6a-54ab-a142-c8b1cf79cde6"&lt;/span&gt;

&lt;span class="nn"&gt;[compat]&lt;/span&gt;
&lt;span class="py"&gt;julia&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1.6"&lt;/span&gt; &lt;span class="c"&gt;# Specifies compatible Julia versions&lt;/span&gt;
&lt;span class="py"&gt;BenchmarkTools&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1.0"&lt;/span&gt; &lt;span class="c"&gt;# Allows version 1.0 or any later 1.x version&lt;/span&gt;
&lt;span class="py"&gt;JSON&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.21"&lt;/span&gt; &lt;span class="c"&gt;# Allows version 0.21 or any later 0.x version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Key Sections:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;[deps]&lt;/code&gt;:&lt;/strong&gt; Lists the direct dependencies by name and their &lt;strong&gt;UUID&lt;/strong&gt; (Universally Unique Identifier). The UUID is how &lt;code&gt;Pkg&lt;/code&gt; uniquely identifies packages, even if names clash. &lt;code&gt;Pkg add PackageName&lt;/code&gt; automatically finds the UUID and adds it here.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;[compat]&lt;/code&gt;:&lt;/strong&gt; This is the most important section for &lt;strong&gt;version constraints&lt;/strong&gt;. It tells &lt;code&gt;Pkg&lt;/code&gt; which versions of Julia and which versions of each dependency are compatible with your project.

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;julia = "1.6"&lt;/code&gt; means your code requires Julia version 1.6 or higher (but less than 2.0).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;BenchmarkTools = "1.0"&lt;/code&gt; uses semantic versioning (SemVer) compatibility rules. It means your code works with version 1.0 and any later &lt;em&gt;minor&lt;/em&gt; or &lt;em&gt;patch&lt;/em&gt; release within version 1 (e.g., 1.1, 1.2.3), but &lt;strong&gt;not&lt;/strong&gt; version 2.0. This prevents breaking changes from major version updates. &lt;code&gt;Pkg add&lt;/code&gt; usually adds a compatible entry here automatically.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Version Control:&lt;/strong&gt; You &lt;strong&gt;should commit&lt;/strong&gt; &lt;code&gt;Project.toml&lt;/code&gt; to your version control system (like Git). It defines the intended dependencies of your project.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  &lt;code&gt;Manifest.toml&lt;/code&gt; - The Exact Blueprint 📜
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Purpose:&lt;/strong&gt; This file is an &lt;strong&gt;exact snapshot&lt;/strong&gt; of &lt;em&gt;all&lt;/em&gt; the packages in your project environment, including not just your direct dependencies (&lt;code&gt;Project.toml&lt;/code&gt;) but also &lt;strong&gt;all indirect dependencies&lt;/strong&gt; (dependencies of dependencies, recursively). Crucially, it lists the &lt;strong&gt;exact version&lt;/strong&gt; of every single package used.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Format:&lt;/strong&gt; Also TOML, but much longer and more detailed. It's primarily intended for &lt;code&gt;Pkg&lt;/code&gt; to read, not for humans to edit directly.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Example Snippet &lt;code&gt;Manifest.toml&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="c"&gt;# This file is machine-generated - editing it directly is not advised&lt;/span&gt;

&lt;span class="py"&gt;julia_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1.10.0"&lt;/span&gt;

&lt;span class="nn"&gt;[[deps.BenchmarkTools]]&lt;/span&gt;
&lt;span class="py"&gt;deps&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"JSON"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Logging"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Printf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Statistics"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"UUIDs"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="py"&gt;git-tree-sha1&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"..."&lt;/span&gt;
&lt;span class="py"&gt;uuid&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"&lt;/span&gt;
&lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1.3.1"&lt;/span&gt;

&lt;span class="nn"&gt;[[deps.JSON]]&lt;/span&gt;
&lt;span class="py"&gt;deps&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Dates"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Mmap"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="py"&gt;git-tree-sha1&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"..."&lt;/span&gt;
&lt;span class="py"&gt;uuid&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"682c06a0-de6a-54ab-a142-c8b1cf79cde6"&lt;/span&gt;
&lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.21.3"&lt;/span&gt;

&lt;span class="c"&gt;# ... entries for Dates, Logging, Mmap, Printf, Statistics, UUIDs, etc. ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reproducibility:&lt;/strong&gt; This file is the key to &lt;strong&gt;100% reproducible builds&lt;/strong&gt;. When someone else (or you, on a different machine or later time) clones your project and runs &lt;code&gt;Pkg.instantiate()&lt;/code&gt;, &lt;code&gt;Pkg&lt;/code&gt; reads &lt;em&gt;only&lt;/em&gt; &lt;code&gt;Manifest.toml&lt;/code&gt;. It ignores &lt;code&gt;Project.toml&lt;/code&gt;'s version ranges and installs the &lt;em&gt;exact&lt;/em&gt; versions specified in the manifest. This guarantees everyone runs the code with the exact same set of dependencies, eliminating "works on my machine" problems.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Version Control:&lt;/strong&gt; You &lt;strong&gt;should commit&lt;/strong&gt; &lt;code&gt;Manifest.toml&lt;/code&gt; to your version control system alongside &lt;code&gt;Project.toml&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Workflow
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Start:&lt;/strong&gt; &lt;code&gt;cd MyProject; julia&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Activate:&lt;/strong&gt; &lt;code&gt;pkg&amp;gt; activate .&lt;/code&gt; (Creates &lt;code&gt;Project.toml&lt;/code&gt; if needed)&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Add:&lt;/strong&gt; &lt;code&gt;pkg&amp;gt; add PackageA PackageB&lt;/code&gt; (Adds to &lt;code&gt;[deps]&lt;/code&gt; in &lt;code&gt;Project.toml&lt;/code&gt;, adds compat entries, resolves &lt;em&gt;all&lt;/em&gt; dependencies, and writes exact versions to &lt;code&gt;Manifest.toml&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Develop:&lt;/strong&gt; Write your code (&lt;code&gt;import .MyModule: ...&lt;/code&gt; etc.)&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Share:&lt;/strong&gt; Commit &lt;code&gt;Project.toml&lt;/code&gt;, &lt;code&gt;Manifest.toml&lt;/code&gt;, and your source code (&lt;code&gt;src/&lt;/code&gt;, &lt;code&gt;test/&lt;/code&gt;) to Git.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Collaborator Clones:&lt;/strong&gt; &lt;code&gt;git clone ...; cd MyProject; julia&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Instantiate:&lt;/strong&gt; &lt;code&gt;pkg&amp;gt; activate .; instantiate&lt;/code&gt; (&lt;code&gt;instantiate&lt;/code&gt; reads &lt;code&gt;Manifest.toml&lt;/code&gt; and installs the exact versions listed). Now the collaborator has an identical environment.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Understanding these two files is fundamental to professional Julia development, ensuring projects are manageable, shareable, and reproducible.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, &lt;code&gt;Pkg.jl&lt;/code&gt; Manual, "Project.toml and Manifest.toml":&lt;/strong&gt; Provides the definitive explanation of these files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TOML Specification:&lt;/strong&gt; &lt;a href="https://toml.io/en/" rel="noopener noreferrer"&gt;https://toml.io/en/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  Unit Testing
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;0080_test_basics.jl&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;This lesson requires creating **two&lt;/em&gt;* files: the code to be tested (&lt;code&gt;my_math.jl&lt;/code&gt;) and the test script itself (&lt;code&gt;run_tests.jl&lt;/code&gt;).*&lt;/p&gt;




&lt;h4&gt;
  
  
  File 1: &lt;code&gt;my_math.jl&lt;/code&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# my_math.jl&lt;/span&gt;
&lt;span class="c"&gt;# Contains the function(s) we want to test.&lt;/span&gt;

&lt;span class="c"&gt;# (We define it inside a module for good practice, though not strictly required)&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="n"&gt;MyMath&lt;/span&gt;

&lt;span class="c"&gt;# Function to test: adds 2 to its input&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; add_two&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="c"&gt;# module MyMath&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h4&gt;
  
  
  File 2: &lt;code&gt;run_tests.jl&lt;/code&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# run_tests.jl&lt;/span&gt;
&lt;span class="c"&gt;# Contains the tests for the code in my_math.jl&lt;/span&gt;

&lt;span class="c"&gt;# 1. Import the '@test' macro from the standard 'Test' library.&lt;/span&gt;
&lt;span class="c"&gt;#    'Test' is always available, no need to add it via Pkg.&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Test&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nd"&gt;@test&lt;/span&gt;

&lt;span class="c"&gt;# 2. Include the source code file we want to test.&lt;/span&gt;
&lt;span class="c"&gt;#    This executes 'my_math.jl', defining the 'MyMath' module.&lt;/span&gt;
&lt;span class="n"&gt;include&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"my_math.jl"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 3. Write a basic test using the '@test' macro.&lt;/span&gt;
&lt;span class="c"&gt;#    '@test' evaluates the expression that follows it.&lt;/span&gt;
&lt;span class="c"&gt;#    - If the expression is 'true', the test passes (silently by default).&lt;/span&gt;
&lt;span class="c"&gt;#    - If the expression is 'false', the test fails (prints an error).&lt;/span&gt;
&lt;span class="c"&gt;#    - If the expression throws an error, the test errors.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Running basic tests..."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Test case 1: Check if adding 2 to 3 gives 5.&lt;/span&gt;
&lt;span class="nd"&gt;@test&lt;/span&gt; &lt;span class="n"&gt;MyMath&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_two&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;

&lt;span class="c"&gt;# Test case 2: Check if adding 2 to 0 gives 2.&lt;/span&gt;
&lt;span class="nd"&gt;@test&lt;/span&gt; &lt;span class="n"&gt;MyMath&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_two&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;

&lt;span class="c"&gt;# Test case 3: A failing test (uncomment to see failure)&lt;/span&gt;
&lt;span class="c"&gt;# println("\nRunning a failing test...")&lt;/span&gt;
&lt;span class="c"&gt;# @test MyMath.add_two(1) == 4 # This will fail&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Basic tests finished."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# You run this file from the command line: julia run_tests.jl&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces the built-in &lt;strong&gt;&lt;code&gt;Test&lt;/code&gt; standard library&lt;/strong&gt;, which is Julia's primary tool for writing &lt;strong&gt;unit tests&lt;/strong&gt;. Unit tests are small, automated checks that verify the correctness of individual pieces of code (like functions).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Core Concept:&lt;/strong&gt; Testing is fundamental to writing reliable software. The &lt;code&gt;Test&lt;/code&gt; library provides macros and functions to make writing and running these checks easy.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Structure: Code File vs. Test File&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It's standard practice to keep your main application code (e.g., &lt;code&gt;my_math.jl&lt;/code&gt;) separate from your test code (e.g., &lt;code&gt;run_tests.jl&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;The test file uses &lt;code&gt;include("my_math.jl")&lt;/code&gt; to load the code it needs to test. This ensures the tests run against the actual source code.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;The &lt;code&gt;@test&lt;/code&gt; Macro:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This is the most basic assertion tool. You wrap a boolean expression inside &lt;code&gt;@test&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@test MyMath.add_two(3) == 5&lt;/code&gt;: This checks if the result of calling &lt;code&gt;MyMath.add_two(3)&lt;/code&gt; is equal (&lt;code&gt;==&lt;/code&gt;) to &lt;code&gt;5&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pass:&lt;/strong&gt; If the expression evaluates to &lt;code&gt;true&lt;/code&gt;, the test passes. By default, passing tests don't print anything to keep output clean.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fail:&lt;/strong&gt; If the expression evaluates to &lt;code&gt;false&lt;/code&gt; (like in the commented-out example where &lt;code&gt;1 + 2 == 4&lt;/code&gt; is false), the &lt;code&gt;@test&lt;/code&gt; macro prints a detailed failure message, including the expression, the expected value, and the actual result.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error:&lt;/strong&gt; If evaluating the expression &lt;em&gt;itself&lt;/em&gt; throws an error (e.g., if &lt;code&gt;add_two&lt;/code&gt; was called with a &lt;code&gt;String&lt;/code&gt;), the test errors and prints the exception.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Running Tests:&lt;/strong&gt; You typically run your test suite by executing the test script directly from the terminal: &lt;code&gt;julia run_tests.jl&lt;/code&gt;. A clean run (no output other than your &lt;code&gt;println&lt;/code&gt; statements) means all tests passed.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Why Test?&lt;/strong&gt; Automated tests catch regressions (when a change breaks existing functionality), document how code is supposed to work, and give you confidence to refactor and improve your code base.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;References:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Standard Library, &lt;code&gt;Test&lt;/code&gt;:&lt;/strong&gt; Complete guide to the testing framework.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;To run the script:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Save the first code block as &lt;code&gt;my_math.jl&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; Save the second code block as &lt;code&gt;run_tests.jl&lt;/code&gt; in the &lt;em&gt;same directory&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt; Run &lt;code&gt;julia run_tests.jl&lt;/code&gt; from your terminal.&lt;/li&gt;
&lt;/ol&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia run_tests.jl
Running basic tests...

Basic tests finished.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(If you uncomment the failing test, you will see detailed failure output.)&lt;/em&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;0081_testset_test.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0081_testset_test.jl&lt;/span&gt;
&lt;span class="c"&gt;# Demonstrates using @testset for better test organization.&lt;/span&gt;

&lt;span class="c"&gt;# 1. Import macros from the 'Test' library.&lt;/span&gt;
&lt;span class="c"&gt;#    We now import '@testset' in addition to '@test'.&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Test&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nd"&gt;@test&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;@testset&lt;/span&gt;

&lt;span class="c"&gt;# 2. Include the source code file we want to test.&lt;/span&gt;
&lt;span class="n"&gt;include&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"my_math.jl"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 3. Use '@testset' to group related tests.&lt;/span&gt;
&lt;span class="c"&gt;#    The string argument provides a descriptive name for the group.&lt;/span&gt;
&lt;span class="nd"&gt;@testset&lt;/span&gt; &lt;span class="s"&gt;"MyMath.add_two Tests"&lt;/span&gt; &lt;span class="k"&gt;begin&lt;/span&gt;
    &lt;span class="c"&gt;# 4. Place individual '@test' calls inside the 'begin...end' block.&lt;/span&gt;
    &lt;span class="nd"&gt;@test&lt;/span&gt; &lt;span class="n"&gt;MyMath&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_two&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
    &lt;span class="nd"&gt;@test&lt;/span&gt; &lt;span class="n"&gt;MyMath&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_two&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="nd"&gt;@test&lt;/span&gt; &lt;span class="n"&gt;MyMath&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_two&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;

    &lt;span class="c"&gt;# 5. Testsets can be nested for further organization.&lt;/span&gt;
    &lt;span class="nd"&gt;@testset&lt;/span&gt; &lt;span class="s"&gt;"Floating Point Tests"&lt;/span&gt; &lt;span class="k"&gt;begin&lt;/span&gt;
        &lt;span class="c"&gt;# Use '≈' (\approx&amp;lt;tab&amp;gt;) for approximate floating-point comparison.&lt;/span&gt;
        &lt;span class="nd"&gt;@test&lt;/span&gt; &lt;span class="n"&gt;MyMath&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_two&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.5&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="n"&gt;≈&lt;/span&gt; &lt;span class="mf"&gt;3.5&lt;/span&gt;
        &lt;span class="nd"&gt;@test&lt;/span&gt; &lt;span class="n"&gt;MyMath&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_two&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="n"&gt;≈&lt;/span&gt; &lt;span class="mf"&gt;1.5&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="c"&gt;# 6. Include a failing test to see the output.&lt;/span&gt;
    &lt;span class="nd"&gt;@testset&lt;/span&gt; &lt;span class="s"&gt;"Failing Test Example"&lt;/span&gt; &lt;span class="k"&gt;begin&lt;/span&gt;
        &lt;span class="nd"&gt;@test&lt;/span&gt; &lt;span class="n"&gt;MyMath&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_two&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt; &lt;span class="c"&gt;# This will fail&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="c"&gt;# End of "MyMath.add_two Tests" testset&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Test execution finished."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Run this file: julia 0081_testset_test.jl&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces the &lt;strong&gt;&lt;code&gt;@testset&lt;/code&gt;&lt;/strong&gt; macro, which is the standard and highly recommended way to &lt;strong&gt;organize&lt;/strong&gt; tests and get &lt;strong&gt;summarized results&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;@testset&lt;/code&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Grouping Tests:&lt;/strong&gt; &lt;code&gt;@testset "Description" begin ... end&lt;/code&gt; groups related &lt;code&gt;@test&lt;/code&gt; calls under a descriptive name. This makes it much easier to understand the structure of your test suite. You can &lt;strong&gt;nest testsets&lt;/strong&gt; to create hierarchical organization (e.g., grouping all tests for a module, then sub-groups for each function).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Summarized Output:&lt;/strong&gt; This is the primary benefit. Instead of just running silently on success, &lt;code&gt;@testset&lt;/code&gt; &lt;strong&gt;counts&lt;/strong&gt; the number of passing and failing tests within it. At the end of the testset, it prints a &lt;strong&gt;summary&lt;/strong&gt; line. If all tests within the set pass, it prints a concise "Pass" summary. If any test fails, it prints the details of the failure &lt;em&gt;and&lt;/em&gt; a summary indicating how many passed and failed. This makes it much easier to quickly see the overall status of your tests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Failure Isolation (Default):&lt;/strong&gt; By default, if one &lt;code&gt;@test&lt;/code&gt; within a &lt;code&gt;@testset&lt;/code&gt; fails, the testset records the failure but &lt;strong&gt;continues executing&lt;/strong&gt; the remaining tests within that set. This helps you see &lt;em&gt;all&lt;/em&gt; failures in a group at once, rather than stopping at the first one. (This behavior can be changed with options if needed).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Floating-Point Comparison (&lt;code&gt;≈&lt;/code&gt;):&lt;/strong&gt; When testing floating-point numbers, direct equality (&lt;code&gt;==&lt;/code&gt;) is often unreliable due to tiny precision errors. The &lt;code&gt;Test&lt;/code&gt; library automatically loads the &lt;code&gt;isapprox&lt;/code&gt; function (aliased as &lt;code&gt;≈&lt;/code&gt;, typed &lt;code&gt;\approx&amp;lt;tab&amp;gt;&lt;/code&gt;). Using &lt;code&gt;@test a ≈ b&lt;/code&gt; checks if &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt; are approximately equal within a default tolerance, which is the correct way to compare floats.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using &lt;code&gt;@testset&lt;/code&gt; transforms your tests from simple assertion scripts into a structured, informative test suite, which is essential for maintaining larger projects.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Standard Library, &lt;code&gt;Test&lt;/code&gt;, "Organizing Tests":&lt;/strong&gt; Explains &lt;code&gt;@testset&lt;/code&gt; and its benefits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Standard Library, &lt;code&gt;Test&lt;/code&gt;, "Testing Floating Point Numbers":&lt;/strong&gt; Recommends using &lt;code&gt;isapprox&lt;/code&gt; or &lt;code&gt;≈&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;To run the script:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Make sure &lt;code&gt;my_math.jl&lt;/code&gt; (from lesson 0080) is in the same directory.&lt;/li&gt;
&lt;li&gt; Run &lt;code&gt;julia 0081_testset_test.jl&lt;/code&gt; from your terminal.&lt;/li&gt;
&lt;/ol&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0081_testset_test.jl
Test Summary:           | Pass  Fail  Total  Time
MyMath.add_two Tests    |    5     1      6  0.0s
  Floating Point Tests  |    2            2  0.0s
  Failing Test Example  |          1      1  0.0s
    Test Failed at 0081_testset_test.jl:30
      Expression: MyMath.add_two&lt;span class="o"&gt;(&lt;/span&gt;10&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; 11
       Evaluated: 12 &lt;span class="o"&gt;==&lt;/span&gt; 11
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(Note: The exact time will vary. The output clearly shows the nested structure, the failure details, and the final summary.)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;
Test execution finished.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;0082_test_assertions.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0082_test_assertions.jl&lt;/span&gt;
&lt;span class="c"&gt;# Demonstrates other useful assertion macros from the Test standard library.&lt;/span&gt;

&lt;span class="c"&gt;# 1. Import necessary macros.&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Test&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nd"&gt;@test&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;@testset&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;@test_throws&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;@test_broken&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;@test_skip&lt;/span&gt;

&lt;span class="c"&gt;# 2. Include the source code file.&lt;/span&gt;
&lt;span class="n"&gt;include&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"my_math.jl"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 3. Use '@test_throws' to check for expected errors.&lt;/span&gt;
&lt;span class="nd"&gt;@testset&lt;/span&gt; &lt;span class="s"&gt;"@test_throws Examples"&lt;/span&gt; &lt;span class="k"&gt;begin&lt;/span&gt;
    &lt;span class="c"&gt;# This function expects a Number. Passing a String should error.&lt;/span&gt;
    &lt;span class="c"&gt;# @test_throws ExpectedErrorType Expression&lt;/span&gt;
    &lt;span class="nd"&gt;@test_throws&lt;/span&gt; &lt;span class="kt"&gt;MethodError&lt;/span&gt; &lt;span class="n"&gt;MyMath&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_two&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hello"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# You can also test for specific exception types beyond MethodError,&lt;/span&gt;
    &lt;span class="c"&gt;# like DivideError, DomainError, ArgumentError etc.&lt;/span&gt;
    &lt;span class="nd"&gt;@test_throws&lt;/span&gt; &lt;span class="kt"&gt;DivideError&lt;/span&gt; &lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# Example of a test that *fails* because the expected error doesn't happen&lt;/span&gt;
    &lt;span class="c"&gt;# @test_throws DomainError MyMath.add_two(5) # This would fail the testset&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 4. Use '@test_broken' for tests that are known to fail but shouldn't stop CI.&lt;/span&gt;
&lt;span class="nd"&gt;@testset&lt;/span&gt; &lt;span class="s"&gt;"@test_broken Example"&lt;/span&gt; &lt;span class="k"&gt;begin&lt;/span&gt;
    &lt;span class="c"&gt;# Perhaps this feature isn't implemented yet, or there's a known bug.&lt;/span&gt;
    &lt;span class="c"&gt;# The test runs, and if it FAILS (as expected), it's recorded as 'Broken'.&lt;/span&gt;
    &lt;span class="c"&gt;# If it unexpectedly PASSES, it's recorded as an 'Error' (because it should be fixed).&lt;/span&gt;
    &lt;span class="nd"&gt;@test_broken&lt;/span&gt; &lt;span class="n"&gt;MyMath&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_two&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mf"&gt;0.3&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;2.0&lt;/span&gt; &lt;span class="c"&gt;# Fails due to floating point inaccuracy&lt;/span&gt;

    &lt;span class="c"&gt;# Example: If this test unexpectedly passed, it would error&lt;/span&gt;
    &lt;span class="c"&gt;# @test_broken MyMath.add_two(1) == 3 # This would unexpectedly pass and error&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 5. Use '@test_skip' for tests that should not be run at all.&lt;/span&gt;
&lt;span class="nd"&gt;@testset&lt;/span&gt; &lt;span class="s"&gt;"@test_skip Example"&lt;/span&gt; &lt;span class="k"&gt;begin&lt;/span&gt;
    &lt;span class="c"&gt;# Use this for tests that are incomplete, depend on unavailable resources,&lt;/span&gt;
    &lt;span class="c"&gt;# or are temporarily disabled.&lt;/span&gt;
    &lt;span class="c"&gt;# The expression is *not* evaluated.&lt;/span&gt;
    &lt;span class="nd"&gt;@test_skip&lt;/span&gt; &lt;span class="n"&gt;MyMath&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_two&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"this code won't even run"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Test execution finished."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Run this file: julia 0082_test_assertions.jl&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces several other useful assertion macros provided by the &lt;code&gt;Test&lt;/code&gt; standard library beyond the basic &lt;code&gt;@test&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;@test_throws ExpectedErrorType Expression&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Purpose:&lt;/strong&gt; Use this when you &lt;em&gt;expect&lt;/em&gt; a specific piece of code to throw an error. This is crucial for testing error handling, invalid inputs, and boundary conditions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How it Works:&lt;/strong&gt; It runs the &lt;code&gt;Expression&lt;/code&gt;.

&lt;ul&gt;
&lt;li&gt;If the expression throws an error that &lt;strong&gt;is a subtype of&lt;/strong&gt; &lt;code&gt;ExpectedErrorType&lt;/code&gt;, the test &lt;strong&gt;passes&lt;/strong&gt;. ✅&lt;/li&gt;
&lt;li&gt;If the expression throws an error of a &lt;strong&gt;different type&lt;/strong&gt;, the test &lt;strong&gt;errors&lt;/strong&gt;. ❌&lt;/li&gt;
&lt;li&gt;If the expression &lt;strong&gt;does not throw any error&lt;/strong&gt;, the test &lt;strong&gt;fails&lt;/strong&gt;. ❌&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Example:&lt;/strong&gt; &lt;code&gt;@test_throws MethodError MyMath.add_two("hello")&lt;/code&gt; passes because calling &lt;code&gt;add_two&lt;/code&gt; with a &lt;code&gt;String&lt;/code&gt; correctly throws a &lt;code&gt;MethodError&lt;/code&gt;. &lt;code&gt;@test_throws DivideError div(1, 0)&lt;/code&gt; passes because integer division by zero throws a &lt;code&gt;DivideError&lt;/code&gt;.&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;@test_broken Expression&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Purpose:&lt;/strong&gt; Marks a test that is &lt;strong&gt;currently failing&lt;/strong&gt; due to a known bug or unimplemented feature.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How it Works:&lt;/strong&gt; It runs the &lt;code&gt;Expression&lt;/code&gt;.

&lt;ul&gt;
&lt;li&gt;If the expression is &lt;code&gt;false&lt;/code&gt; or throws an error (i.e., the test &lt;strong&gt;fails&lt;/strong&gt; as expected), it's recorded as &lt;strong&gt;"Broken"&lt;/strong&gt;. This does &lt;em&gt;not&lt;/em&gt; typically fail your overall test suite in CI environments. ✅💔&lt;/li&gt;
&lt;li&gt;If the expression is &lt;code&gt;true&lt;/code&gt; (i.e., the test &lt;strong&gt;unexpectedly passes&lt;/strong&gt;), it's recorded as an &lt;strong&gt;"Error"&lt;/strong&gt;. This &lt;em&gt;does&lt;/em&gt; typically fail the test suite, signaling that the underlying issue might be fixed and the &lt;code&gt;@test_broken&lt;/code&gt; should be changed back to &lt;code&gt;@test&lt;/code&gt;. ❗✅&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Example:&lt;/strong&gt; &lt;code&gt;@test_broken MyMath.add_two(0.1 + 0.2) == 0.3 + 2.0&lt;/code&gt; correctly identifies a known floating-point inaccuracy issue. NOTE: seems to pass, probably need a better example.&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;@test_skip Expression&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Purpose:&lt;/strong&gt; Completely &lt;strong&gt;skips&lt;/strong&gt; the evaluation of a test.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How it Works:&lt;/strong&gt; The &lt;code&gt;Expression&lt;/code&gt; is &lt;strong&gt;never executed&lt;/strong&gt;. The test is simply recorded as &lt;strong&gt;"Skipped"&lt;/strong&gt;. ⏭️&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use Cases:&lt;/strong&gt; Useful for tests that are incomplete, rely on external resources that might not be available (like a network service), or need to be temporarily disabled for debugging.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;These macros provide more nuanced ways to handle expected failures, known issues, and temporary skips, making your test suite more robust and informative.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Standard Library, &lt;code&gt;Test&lt;/code&gt;:&lt;/strong&gt; Describes &lt;code&gt;@test_throws&lt;/code&gt;, &lt;code&gt;@test_broken&lt;/code&gt;, and &lt;code&gt;@test_skip&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;To run the script:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Make sure &lt;code&gt;my_math.jl&lt;/code&gt; (from lesson 0080) is in the same directory.&lt;/li&gt;
&lt;li&gt; Run &lt;code&gt;julia 0082_test_assertions.jl&lt;/code&gt; from your terminal.&lt;/li&gt;
&lt;/ol&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0082_test_assertions.jl
Test Summary:        | Pass  Broken  Skip  Total  Time
@test_throws Examples |    2                 2  0.0s
@test_broken Example  |         1            1  0.0s
@test_skip Example    |                 1      1  0.0s

Test execution finished.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(Note: The output shows the different test outcomes correctly recorded by the testsets.)&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Benchmarking
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;0083_benchmark_tools.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0083_benchmark_tools.jl&lt;/span&gt;
&lt;span class="c"&gt;# Introduces BenchmarkTools.jl for accurate performance measurement.&lt;/span&gt;

&lt;span class="c"&gt;# 1. Import the '@btime' macro.&lt;/span&gt;
&lt;span class="c"&gt;#    Requires BenchmarkTools.jl to be installed. See Explanation.&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BenchmarkTools&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nd"&gt;@btime&lt;/span&gt;

&lt;span class="c"&gt;# 2. Include the code we want to benchmark.&lt;/span&gt;
&lt;span class="n"&gt;include&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"my_math.jl"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 3. Define a slightly more complex function to benchmark.&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; sum_of_add_two&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;
        &lt;span class="c"&gt;# Call the function inside the loop&lt;/span&gt;
        &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;MyMath&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_two&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# --- Benchmarking ---&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Benchmarking sum_of_add_two(1000) ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 4. Use the '@btime' macro.&lt;/span&gt;
&lt;span class="c"&gt;#    '@btime Expression' runs the expression many times to get a&lt;/span&gt;
&lt;span class="c"&gt;#    statistically accurate measurement of its *minimum* execution time.&lt;/span&gt;
&lt;span class="c"&gt;#    It automatically handles things like warmup runs.&lt;/span&gt;
&lt;span class="nd"&gt;@btime&lt;/span&gt; &lt;span class="n"&gt;sum_of_add_two&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 5. Benchmark with input variables (Incorrectly - see next lesson)&lt;/span&gt;
&lt;span class="c"&gt;#    If the input is a variable, simply putting it in the expression&lt;/span&gt;
&lt;span class="c"&gt;#    can lead to inaccurate results because it might measure&lt;/span&gt;
&lt;span class="c"&gt;#    global variable lookup time.&lt;/span&gt;
&lt;span class="n"&gt;input_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Benchmarking sum_of_add_two(input_size) ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@btime&lt;/span&gt; &lt;span class="n"&gt;sum_of_add_two&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input_size&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"(Note: This result might be inaccurate, see next lesson on interpolation)"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces the &lt;strong&gt;&lt;code&gt;BenchmarkTools.jl&lt;/code&gt;&lt;/strong&gt; package, the standard and most reliable tool in Julia for measuring the performance of code accurately.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Installation Note:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;BenchmarkTools.jl&lt;/code&gt; is not part of Julia's standard library. You need to add it to your project environment once.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Start the Julia REPL: &lt;code&gt;julia&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt; Enter Pkg mode: &lt;code&gt;]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt; Add the package: &lt;code&gt;add BenchmarkTools&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt; Exit Pkg mode: Press Backspace or &lt;code&gt;Ctrl+C&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; You can now run this script.&lt;/li&gt;
&lt;/ol&gt;




&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Core Concept: Accurate Measurement&lt;/strong&gt;&lt;br&gt;
Simply running code once with &lt;code&gt;@time&lt;/code&gt; (Julia's basic timing macro) is often unreliable for measuring performance. Results can be noisy due to JIT compilation overhead on the first run, system background tasks, CPU frequency scaling, etc. &lt;code&gt;BenchmarkTools.jl&lt;/code&gt; is designed to overcome these issues.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The &lt;code&gt;@btime&lt;/code&gt; Macro:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Purpose:&lt;/strong&gt; &lt;code&gt;@btime Expression&lt;/code&gt; provides a quick and easy way to get a reliable estimate of the &lt;strong&gt;minimum execution time&lt;/strong&gt; of an &lt;code&gt;Expression&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How it Works (Simplified):&lt;/strong&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Warmup:&lt;/strong&gt; It runs the &lt;code&gt;Expression&lt;/code&gt; once or twice initially to ensure everything (including the function itself and any functions it calls) is compiled by the JIT.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Sampling:&lt;/strong&gt; It then runs the &lt;code&gt;Expression&lt;/code&gt; in a loop many times, collecting execution times for each run.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Statistics:&lt;/strong&gt; It calculates statistics on these times, paying special attention to the &lt;strong&gt;minimum&lt;/strong&gt; time, which is usually the best estimate of the code's performance when system conditions are optimal (e.g., caches are hot).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Output:&lt;/strong&gt; It prints a concise summary including the minimum time, the number of memory allocations, and the total memory allocated.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Why Minimum Time?&lt;/strong&gt; In performance tuning, we are often most interested in the best possible execution time the code can achieve under ideal conditions. Average time can be skewed upwards by random system events, but the minimum time reflects the code's inherent speed limit more closely.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Memory Allocations:&lt;/strong&gt; &lt;code&gt;@btime&lt;/code&gt; also reports memory allocations (&lt;code&gt;allocs:&lt;/code&gt; and memory size). This is &lt;strong&gt;critical&lt;/strong&gt;. Unexpected memory allocations are a major sign of type instability or inefficient code (like creating temporary arrays). Aiming for &lt;code&gt;0 allocations&lt;/code&gt; is often a key goal in high-performance code.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Benchmarking with Variables (Caveat):&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
As noted in the script, simply using a global variable like &lt;code&gt;input_size&lt;/code&gt; inside &lt;code&gt;@btime&lt;/code&gt; can skew the results. &lt;code&gt;@btime&lt;/code&gt; might include the time it takes to look up that global variable in its measurement. The next lesson (&lt;code&gt;0084_benchmark_interpolation.jl&lt;/code&gt;) will show the correct way to handle this using &lt;code&gt;$&lt;/code&gt; interpolation.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Rule of Thumb:&lt;/strong&gt; Use &lt;code&gt;@btime&lt;/code&gt; whenever you need a quick but reliable measurement of a function call or code snippet's speed and memory usage. It's the go-to tool for performance iteration.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;BenchmarkTools.jl&lt;/code&gt; Documentation:&lt;/strong&gt; &lt;a href="https://github.com/JuliaCI/BenchmarkTools.jl" rel="noopener noreferrer"&gt;https://github.com/JuliaCI/BenchmarkTools.jl&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Performance Tips":&lt;/strong&gt; Recommends using &lt;code&gt;BenchmarkTools.jl&lt;/code&gt; for accurate measurements.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;To run the script:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Make sure &lt;code&gt;my_math.jl&lt;/code&gt; (from lesson 0080) is in the same directory.&lt;/li&gt;
&lt;li&gt; Ensure &lt;code&gt;BenchmarkTools.jl&lt;/code&gt; is installed (see installation note).&lt;/li&gt;
&lt;li&gt; Run &lt;code&gt;julia 0083_benchmark_tools.jl&lt;/code&gt; from your terminal.&lt;/li&gt;
&lt;/ol&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0083_benchmark_tools.jl
&lt;span class="nt"&gt;---&lt;/span&gt; Benchmarking sum_of_add_two&lt;span class="o"&gt;(&lt;/span&gt;1000&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
  1.485 ns &lt;span class="o"&gt;(&lt;/span&gt;0 allocations: 0 bytes&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nt"&gt;---&lt;/span&gt; Benchmarking sum_of_add_two&lt;span class="o"&gt;(&lt;/span&gt;input_size&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
  5.192 ns &lt;span class="o"&gt;(&lt;/span&gt;0 allocations: 0 bytes&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;Note: This result might be inaccurate, see next lesson on interpolation&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(Replace &lt;code&gt;### ns minimum time: ... ###&lt;/code&gt; with the actual timings and allocations you observe. The exact numbers will vary based on your CPU.)&lt;/em&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;0084_benchmark_interpolation.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0084_benchmark_interpolation.jl&lt;/span&gt;
&lt;span class="c"&gt;# Demonstrates the CRUCIAL use of '$' interpolation in BenchmarkTools.&lt;/span&gt;

&lt;span class="c"&gt;# 1. Import the '@btime' macro.&lt;/span&gt;
&lt;span class="c"&gt;#    (Assumes BenchmarkTools.jl is installed)&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BenchmarkTools&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nd"&gt;@btime&lt;/span&gt;

&lt;span class="c"&gt;# 2. Define a simple function to benchmark (we'll use a built-in one).&lt;/span&gt;
&lt;span class="c"&gt;#    Using 'sin()' which is fast, making overhead more visible.&lt;/span&gt;

&lt;span class="c"&gt;# 3. Define a NON-CONST global variable.&lt;/span&gt;
&lt;span class="c"&gt;#    This is key to reliably showing the lookup overhead.&lt;/span&gt;
&lt;span class="n"&gt;global_x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;100.0&lt;/span&gt; &lt;span class="c"&gt;# Use a Float64 for sin&lt;/span&gt;

&lt;span class="c"&gt;# --- Benchmarking ---&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Benchmark 1: Non-Const Global Directly (INCORRECT) ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# 4. Incorrect way: Use the non-const global variable directly.&lt;/span&gt;
&lt;span class="c"&gt;#    '@btime' creates a timing function internally. Accessing&lt;/span&gt;
&lt;span class="c"&gt;#    'global_x' involves a slow, runtime global lookup.&lt;/span&gt;
&lt;span class="c"&gt;#    We measure lookup cost + sin() cost.&lt;/span&gt;
&lt;span class="nd"&gt;@btime&lt;/span&gt; &lt;span class="n"&gt;sin&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;global_x&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;


&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Benchmark 2: Non-Const Global Interpolated (CORRECT) ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# 5. Correct way: Use '$' to interpolate the *value* of 'global_x'.&lt;/span&gt;
&lt;span class="c"&gt;#    Before timing, '@btime' evaluates '$global_x' (getting 100.0)&lt;/span&gt;
&lt;span class="c"&gt;#    and substitutes this *value* into the expression.&lt;/span&gt;
&lt;span class="c"&gt;#    The benchmark effectively becomes '@btime sin(100.0)'.&lt;/span&gt;
&lt;span class="c"&gt;#    This eliminates the global lookup overhead.&lt;/span&gt;
&lt;span class="nd"&gt;@btime&lt;/span&gt; &lt;span class="n"&gt;sin&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;global_x&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;


&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Benchmark 3: Using a Literal (Reference) ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# 6. For comparison, benchmark with the literal value.&lt;/span&gt;
&lt;span class="c"&gt;#    Allows maximum compiler optimization (constant propagation).&lt;/span&gt;
&lt;span class="nd"&gt;@btime&lt;/span&gt; &lt;span class="n"&gt;sin&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;100.0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script demonstrates one of the most critical details for using &lt;code&gt;BenchmarkTools.jl&lt;/code&gt; correctly: &lt;strong&gt;variable interpolation&lt;/strong&gt; using the dollar sign (&lt;code&gt;$&lt;/code&gt;). Failing to use &lt;code&gt;$&lt;/code&gt; when benchmarking expressions involving variables (especially non-&lt;code&gt;const&lt;/code&gt; globals) is the &lt;strong&gt;#1 mistake&lt;/strong&gt; leading to inaccurate results.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The Problem: Benchmarking Global Variable Access&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;@btime&lt;/code&gt; macro wraps the expression in a function for timing.&lt;/li&gt;
&lt;li&gt;When you write &lt;code&gt;@btime sin(global_x)&lt;/code&gt; using a &lt;strong&gt;non-&lt;code&gt;const&lt;/code&gt; global&lt;/strong&gt;, the timing function must perform a &lt;strong&gt;runtime lookup&lt;/strong&gt; for &lt;code&gt;global_x&lt;/code&gt; &lt;em&gt;every time&lt;/em&gt; it runs. Accessing non-&lt;code&gt;const&lt;/code&gt; globals is &lt;strong&gt;slow&lt;/strong&gt; because the compiler cannot know its type or value beforehand.&lt;/li&gt;
&lt;li&gt;Therefore, the first benchmark &lt;strong&gt;incorrectly measures&lt;/strong&gt; the combined time of:

&lt;ol&gt;
&lt;li&gt; Looking up the global variable &lt;code&gt;global_x&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; Calling &lt;code&gt;sin&lt;/code&gt; with the retrieved value.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;This &lt;strong&gt;pollutes&lt;/strong&gt; the measurement; you're not just timing &lt;code&gt;sin&lt;/code&gt;, but also the slow global access, often leading to extra memory allocations as well.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;The Solution: &lt;code&gt;$&lt;/code&gt; Interpolation&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;$&lt;/code&gt; symbol within &lt;code&gt;@btime&lt;/code&gt; (and other &lt;code&gt;BenchmarkTools&lt;/code&gt; macros) triggers &lt;strong&gt;interpolation&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;When &lt;code&gt;@btime&lt;/code&gt; sees &lt;code&gt;$global_x&lt;/code&gt;, it &lt;strong&gt;first evaluates&lt;/strong&gt; &lt;code&gt;global_x&lt;/code&gt; in the current scope to get its &lt;em&gt;value&lt;/em&gt; (which is &lt;code&gt;100.0&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;It then &lt;strong&gt;substitutes this value&lt;/strong&gt; into the expression &lt;em&gt;before&lt;/em&gt; creating the timing function.&lt;/li&gt;
&lt;li&gt;So, &lt;code&gt;@btime sin($global_x)&lt;/code&gt; becomes equivalent to &lt;code&gt;@btime sin(100.0)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The internal timing function now operates on a &lt;strong&gt;constant value&lt;/strong&gt;, eliminating the slow global lookup and allowing the compiler to generate type-stable code.&lt;/li&gt;
&lt;li&gt;This &lt;strong&gt;correctly measures&lt;/strong&gt; only the execution time of &lt;code&gt;sin&lt;/code&gt; operating on that value.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Interpreting Results:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Benchmark 1 (No &lt;code&gt;$&lt;/code&gt;)&lt;/strong&gt;: Shows a &lt;strong&gt;slower&lt;/strong&gt; time and likely &lt;strong&gt;memory allocations&lt;/strong&gt; (e.g., &lt;code&gt;1 allocation: 16 bytes&lt;/code&gt;) due to the runtime global lookup and potential type instability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Benchmark 2 (&lt;code&gt;$&lt;/code&gt;)&lt;/strong&gt;: Shows a &lt;strong&gt;significantly faster&lt;/strong&gt; time and &lt;strong&gt;zero allocations&lt;/strong&gt;. This accurately reflects the cost of the &lt;code&gt;sin&lt;/code&gt; call itself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Benchmark 3 (Literal)&lt;/strong&gt;: May show an even &lt;strong&gt;faster&lt;/strong&gt; time than Benchmark 2, also with &lt;strong&gt;zero allocations&lt;/strong&gt;. This is because the compiler can perform the most aggressive constant propagation optimizations when it sees the literal value directly in the code at compile time. It represents the absolute lower bound.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Rule of Thumb:&lt;/strong&gt; &lt;strong&gt;ALWAYS&lt;/strong&gt; use &lt;code&gt;$&lt;/code&gt; to interpolate variables (global or local) into expressions benchmarked with &lt;code&gt;@btime&lt;/code&gt; or &lt;code&gt;@benchmark&lt;/code&gt;. Treat the expression inside &lt;code&gt;@btime&lt;/code&gt; as if it were running in its own little function world where it can't see outside variables unless you explicitly pass their values in via &lt;code&gt;$&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;BenchmarkTools.jl&lt;/code&gt; Documentation, Manual, "Interpolating values into benchmark expressions":&lt;/strong&gt; This section explicitly explains the purpose and necessity of &lt;code&gt;$&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;To run the script:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Requires &lt;code&gt;BenchmarkTools.jl&lt;/code&gt; installed)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0084_benchmark_interpolation.jl
&lt;span class="nt"&gt;---&lt;/span&gt; Benchmark 1: Non-Const Global Directly &lt;span class="o"&gt;(&lt;/span&gt;INCORRECT&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
  14.647 ns &lt;span class="o"&gt;(&lt;/span&gt;1 allocation: 16 bytes&lt;span class="o"&gt;)&lt;/span&gt;  &lt;span class="c"&gt;# Slow, Allocates&lt;/span&gt;

&lt;span class="nt"&gt;---&lt;/span&gt; Benchmark 2: Non-Const Global Interpolated &lt;span class="o"&gt;(&lt;/span&gt;CORRECT&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
  3.706 ns &lt;span class="o"&gt;(&lt;/span&gt;0 allocations: 0 bytes&lt;span class="o"&gt;)&lt;/span&gt;   &lt;span class="c"&gt;# Faster, No Allocations&lt;/span&gt;

&lt;span class="nt"&gt;---&lt;/span&gt; Benchmark 3: Using a Literal &lt;span class="o"&gt;(&lt;/span&gt;Reference&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
  0.743 ns &lt;span class="o"&gt;(&lt;/span&gt;0 allocations: 0 bytes&lt;span class="o"&gt;)&lt;/span&gt;   &lt;span class="c"&gt;# Fastest (Constant Propagation), No Allocations&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(Your exact times will vary based on CPU, but the relative differences and allocation patterns should be similar.)&lt;/em&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;0085_benchmark_suite.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0085_benchmark_suite.jl&lt;/span&gt;
&lt;span class="c"&gt;# Briefly demonstrates @benchmark for detailed stats and BenchmarkGroup.&lt;/span&gt;

&lt;span class="c"&gt;# 1. Import necessary components.&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BenchmarkTools&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nd"&gt;@benchmark&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;@benchmarkable&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BenchmarkGroup&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;minimum&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;median&lt;/span&gt;

&lt;span class="c"&gt;# 2. Include our math functions.&lt;/span&gt;
&lt;span class="n"&gt;include&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"my_math.jl"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Contains MyMath.add_two(x)&lt;/span&gt;

&lt;span class="c"&gt;# 3. Define another function to compare.&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; add_two_alternative&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# A slightly different (though likely optimized identically) way&lt;/span&gt;
    &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
    &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# --- @benchmark Macro ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- @benchmark for detailed analysis ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 4. Use '@benchmark' for a more thorough analysis than '@btime'.&lt;/span&gt;
&lt;span class="c"&gt;#    It runs many more samples and provides richer statistical output.&lt;/span&gt;
&lt;span class="c"&gt;#    Remember to interpolate the argument!&lt;/span&gt;
&lt;span class="n"&gt;input_val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;
&lt;span class="n"&gt;bench_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;@benchmark&lt;/span&gt; &lt;span class="n"&gt;MyMath&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_two&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;input_val&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 5. Display the detailed result.&lt;/span&gt;
&lt;span class="c"&gt;#    The raw result object contains a lot of information.&lt;/span&gt;
&lt;span class="c"&gt;#    Printing it shows detailed quantiles, memory, etc.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Detailed @benchmark result for MyMath.add_two:"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bench_result&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# In interactive sessions (like REPL), just running '@benchmark' prints this.&lt;/span&gt;

&lt;span class="c"&gt;# --- BenchmarkGroup ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Comparing functions with BenchmarkGroup ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 6. Create a BenchmarkGroup to organize related benchmarks.&lt;/span&gt;
&lt;span class="c"&gt;#    It acts like a dictionary mapping names (Strings) to benchmarks.&lt;/span&gt;
&lt;span class="n"&gt;suite&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BenchmarkGroup&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;

&lt;span class="c"&gt;# 7. Add benchmarks to the suite using dictionary-like syntax.&lt;/span&gt;
&lt;span class="c"&gt;#    The value side uses '@benchmarkable' which *defines* a benchmark&lt;/span&gt;
&lt;span class="c"&gt;#    without running it immediately. Remember interpolation!&lt;/span&gt;
&lt;span class="n"&gt;suite&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"original"&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;@benchmarkable&lt;/span&gt; &lt;span class="n"&gt;MyMath&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_two&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;input_val&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;suite&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"alternative"&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;@benchmarkable&lt;/span&gt; &lt;span class="n"&gt;add_two_alternative&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;input_val&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 8. Run the entire suite.&lt;/span&gt;
&lt;span class="c"&gt;#    'run(suite, verbose=true)' executes all defined benchmarks.&lt;/span&gt;
&lt;span class="c"&gt;#    'verbose=true' prints results as they complete.&lt;/span&gt;
&lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;suite&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;verbose&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 9. Access results programmatically.&lt;/span&gt;
&lt;span class="c"&gt;#    'results' is also like a dictionary holding the Trial objects.&lt;/span&gt;
&lt;span class="c"&gt;#    BenchmarkTools provides 'minimum()' and 'median()' functions&lt;/span&gt;
&lt;span class="c"&gt;#    that extract the relevant TrialEstimate from a Trial. Access '.time'.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Accessing results programmatically:"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Minimum time for 'original': "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;minimum&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"original"&lt;/span&gt;&lt;span class="x"&gt;])&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;" ns"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Median time for 'alternative': "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;median&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"alternative"&lt;/span&gt;&lt;span class="x"&gt;])&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;" ns"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Note: More advanced comparison/judging tools exist within BenchmarkTools.jl&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script briefly introduces more advanced features of &lt;code&gt;BenchmarkTools.jl&lt;/code&gt;: the &lt;strong&gt;&lt;code&gt;@benchmark&lt;/code&gt;&lt;/strong&gt; macro for detailed statistics and &lt;strong&gt;&lt;code&gt;BenchmarkGroup&lt;/code&gt;&lt;/strong&gt; for organizing and comparing multiple benchmarks.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;@benchmark&lt;/code&gt; vs. &lt;code&gt;@btime&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@btime Expression&lt;/code&gt;&lt;/strong&gt;: Quick, easy, provides the &lt;strong&gt;minimum&lt;/strong&gt; time and basic allocation info. Ideal for rapid iteration during development.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@benchmark Expression&lt;/code&gt;&lt;/strong&gt;: Performs a more &lt;strong&gt;rigorous&lt;/strong&gt; analysis. It runs many more samples across different evaluation counts, collects detailed timing and memory data, and returns a &lt;code&gt;BenchmarkTools.Trial&lt;/code&gt; object containing rich statistical information (minimum, median, mean, standard deviation, quantiles, GC times, etc.).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;When to use &lt;code&gt;@benchmark&lt;/code&gt;:&lt;/strong&gt; Use it when you need a more statistically robust measurement, want to see the distribution of execution times (not just the minimum), or need to analyze GC behavior in detail. In scripts, you need to explicitly &lt;code&gt;display()&lt;/code&gt; or &lt;code&gt;println()&lt;/code&gt; the result object to see the full output.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;BenchmarkGroup&lt;/code&gt; and &lt;code&gt;@benchmarkable&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;BenchmarkGroup()&lt;/code&gt;:&lt;/strong&gt; Creates a container (like a &lt;code&gt;Dict&lt;/code&gt;) to organize multiple, related benchmarks. You assign names (strings) to different benchmark definitions within the group.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@benchmarkable Expression&lt;/code&gt;:&lt;/strong&gt; This macro &lt;strong&gt;defines&lt;/strong&gt; a benchmark without running it immediately. It creates a &lt;code&gt;Benchmark&lt;/code&gt; object that can be stored (e.g., in a &lt;code&gt;BenchmarkGroup&lt;/code&gt;). This is useful for setting up a "suite" of tests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;run(suite, verbose=true)&lt;/code&gt;:&lt;/strong&gt; Executes all benchmarks defined within the &lt;code&gt;BenchmarkGroup&lt;/code&gt; (&lt;code&gt;suite&lt;/code&gt;). &lt;code&gt;verbose=true&lt;/code&gt; prints the results for each benchmark as it completes. The &lt;code&gt;run&lt;/code&gt; function returns a nested structure mirroring the &lt;code&gt;BenchmarkGroup&lt;/code&gt;, but containing the &lt;code&gt;Trial&lt;/code&gt; result objects instead of the definitions.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Accessing Results:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The &lt;code&gt;results&lt;/code&gt; object returned by &lt;code&gt;run&lt;/code&gt; contains the &lt;code&gt;Trial&lt;/code&gt; objects for each benchmark. &lt;code&gt;BenchmarkTools&lt;/code&gt; provides convenient functions like &lt;code&gt;minimum(trial)&lt;/code&gt; and &lt;code&gt;median(trial)&lt;/code&gt; which return a &lt;code&gt;TrialEstimate&lt;/code&gt; containing timing, allocation, and GC information. You access the specific time value using &lt;code&gt;.time&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Organizing Benchmarks:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;code&gt;BenchmarkGroup&lt;/code&gt; is essential for systematically comparing the performance of different implementations of the same function (like &lt;code&gt;MyMath.add_two&lt;/code&gt; vs. &lt;code&gt;add_two_alternative&lt;/code&gt;), different algorithms, or the same algorithm under varying conditions. It allows you to run a whole suite of performance tests with a single command and programmatically access or compare the results.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;BenchmarkTools.jl&lt;/code&gt; Documentation:&lt;/strong&gt; Covers &lt;code&gt;@benchmark&lt;/code&gt;, &lt;code&gt;BenchmarkGroup&lt;/code&gt;, &lt;code&gt;@benchmarkable&lt;/code&gt;, &lt;code&gt;run&lt;/code&gt;, and result analysis in detail.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;To run the script:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Requires &lt;code&gt;BenchmarkTools.jl&lt;/code&gt; installed and &lt;code&gt;my_math.jl&lt;/code&gt; from lesson 0080)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0085_benchmark_suite.jl
&lt;span class="nt"&gt;---&lt;/span&gt; @benchmark &lt;span class="k"&gt;for &lt;/span&gt;detailed analysis &lt;span class="nt"&gt;---&lt;/span&gt;
Detailed @benchmark result &lt;span class="k"&gt;for &lt;/span&gt;MyMath.add_two:
BenchmarkTools.Trial: 10000 samples with 1000 evaluations per sample.
 Range &lt;span class="o"&gt;(&lt;/span&gt;min … max&lt;span class="o"&gt;)&lt;/span&gt;:  1.300 ns … 6.093 ns  ┊ GC &lt;span class="o"&gt;(&lt;/span&gt;min … max&lt;span class="o"&gt;)&lt;/span&gt;: 0.00% … 0.00%
 Time  &lt;span class="o"&gt;(&lt;/span&gt;median&lt;span class="o"&gt;)&lt;/span&gt;:     1.310 ns             ┊ GC &lt;span class="o"&gt;(&lt;/span&gt;median&lt;span class="o"&gt;)&lt;/span&gt;:    0.00%
 Time  &lt;span class="o"&gt;(&lt;/span&gt;mean ± σ&lt;span class="o"&gt;)&lt;/span&gt;:   1.315 ns ± 0.088 ns  ┊ GC &lt;span class="o"&gt;(&lt;/span&gt;mean ± σ&lt;span class="o"&gt;)&lt;/span&gt;:  0.00% ± 0.00%

    █▁                                                       
  ▂▅██▄▄▂▂▂▂▁▂▂▂▂▂▁▁▁▁▁▁▁▂▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▂▁▁▁▁▁▁▁▁▁▁▁▁▁▂▂ ▂
  1.3 ns         Histogram: frequency by &lt;span class="nb"&gt;time       &lt;/span&gt;1.49 ns &amp;lt;

 Memory estimate: 0 bytes, allocs estimate: 0.

&lt;span class="nt"&gt;---&lt;/span&gt; Comparing functions with BenchmarkGroup &lt;span class="nt"&gt;---&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;1/2&lt;span class="o"&gt;)&lt;/span&gt; benchmarking &lt;span class="s2"&gt;"original"&lt;/span&gt;...
&lt;span class="k"&gt;done&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;took 0.241308971 seconds&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;2/2&lt;span class="o"&gt;)&lt;/span&gt; benchmarking &lt;span class="s2"&gt;"alternative"&lt;/span&gt;...
&lt;span class="k"&gt;done&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;took 0.231766299 seconds&lt;span class="o"&gt;)&lt;/span&gt;

Accessing results programmatically:
Minimum &lt;span class="nb"&gt;time &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="s1"&gt;'original'&lt;/span&gt;: 9.0 ns  &lt;span class="c"&gt;# investigate discrepancy&lt;/span&gt;
Median &lt;span class="nb"&gt;time &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="s1"&gt;'alternative'&lt;/span&gt;: 11.0 ns &lt;span class="c"&gt;# investigate discrepancy&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Module 9: Memory, Data Layout and Unsafe Operations
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Memory Layout And Isbits
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;0086_module_intro.md&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This module marks a significant shift. We move from the high-level, mostly "safe" world of Julia programming into the low-level, C-style memory model that underpins its remarkable performance. Here, we'll learn to think about Julia objects not just by their type, but as &lt;strong&gt;raw blocks of bytes&lt;/strong&gt; in memory.&lt;/p&gt;




&lt;h2&gt;
  
  
  Breaking the Contract
&lt;/h2&gt;

&lt;p&gt;In previous modules, we operated under Julia's implicit "social contract": write clear, type-stable code, and the compiler will reward you with performance comparable to C or Fortran. This module deliberately steps outside that contract.&lt;/p&gt;

&lt;p&gt;We will dive &lt;em&gt;beneath&lt;/em&gt; the compiler's safety net to understand the &lt;strong&gt;physical memory layout&lt;/strong&gt; of Julia objects. This isn't just academic; it's the foundation for:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Ultimate Performance:&lt;/strong&gt; Writing code that ensures optimal data locality and allows the compiler to generate the most efficient machine instructions possible.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;C Interoperability:&lt;/strong&gt; Seamlessly passing data to C, C++, or Fortran libraries &lt;strong&gt;without copying&lt;/strong&gt;, by ensuring Julia's data structures are represented identically in memory to their native counterparts.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Advanced Techniques:&lt;/strong&gt; Building zero-copy views directly from memory buffers, implementing custom data structures with specific layouts, and performing bit-level manipulation on raw data representations.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Power and Responsibility
&lt;/h2&gt;

&lt;p&gt;The functions and concepts introduced here often have names prefixed with &lt;code&gt;unsafe_&lt;/code&gt;. This is a deliberate and serious warning. These tools bypass Julia's extensive safety checks (like bounds checking and type checking). They grant you C-level power over memory, which comes with C-level risks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reading uninitialized memory.&lt;/li&gt;
&lt;li&gt;Writing past the allocated bounds of an object.&lt;/li&gt;
&lt;li&gt;Corrupting Julia's internal data structures or the garbage collector state.&lt;/li&gt;
&lt;li&gt;Causing immediate segmentation faults and process crashes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the domain of &lt;strong&gt;systems programming&lt;/strong&gt;: you gain maximum control, but you bear maximum responsibility for correctness and safety. Mastering these concepts allows you to push Julia to its absolute performance limits and integrate it deeply with other systems. &lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Julia Official Documentation, Manual, "Calling C and Fortran Code": Introduces the concepts needed for interoperability, many of which rely on understanding memory layout.&lt;/li&gt;
&lt;li&gt;Julia Official Documentation, Base Documentation, &lt;code&gt;unsafe_*&lt;/code&gt; functions (e.g., &lt;code&gt;unsafe_load&lt;/code&gt;, &lt;code&gt;unsafe_wrap&lt;/code&gt;): Explicitly document the dangers and responsibilities of using these low-level operations.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;0087_isbits_and_memory_layout.md&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Before we can analyze the size (&lt;code&gt;sizeof&lt;/code&gt;) or layout (&lt;code&gt;fieldoffset&lt;/code&gt;) of a Julia &lt;code&gt;struct&lt;/code&gt;, we must understand a fundamental distinction in Julia's type system: &lt;strong&gt;&lt;code&gt;isbits&lt;/code&gt;&lt;/strong&gt; versus &lt;strong&gt;non-&lt;code&gt;isbits&lt;/code&gt;&lt;/strong&gt; types. This distinction dictates whether the data for an object is stored directly ("inline") or accessed indirectly via a pointer ("referenced").&lt;/p&gt;




&lt;h2&gt;
  
  
  The Core Question: Where is the Data?
&lt;/h2&gt;

&lt;p&gt;Julia's type system classifies types based on how their data is represented in memory.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;isbits&lt;/code&gt; Types (Data is "In-Place")
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Definition:&lt;/strong&gt; These are types whose in-memory representation consists &lt;strong&gt;solely of the data itself&lt;/strong&gt;. They are self-contained, fixed-size blocks of bits with no pointers to other memory locations. The official documentation refers to them as "plain data" types.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Characteristics:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Immutable:&lt;/strong&gt; All &lt;code&gt;isbits&lt;/code&gt; types must be immutable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No References:&lt;/strong&gt; They cannot contain fields that are pointers or references to other objects (like &lt;code&gt;String&lt;/code&gt;, &lt;code&gt;Vector&lt;/code&gt;, &lt;code&gt;mutable struct&lt;/code&gt; instances).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Examples:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Primitives:&lt;/strong&gt; &lt;code&gt;Int64&lt;/code&gt;, &lt;code&gt;Float64&lt;/code&gt;, &lt;code&gt;Bool&lt;/code&gt;, &lt;code&gt;Char&lt;/code&gt;, &lt;code&gt;UInt8&lt;/code&gt;, etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Immutable Composites:&lt;/strong&gt; An &lt;strong&gt;immutable &lt;code&gt;struct&lt;/code&gt;&lt;/strong&gt; or &lt;code&gt;NTuple&lt;/code&gt; (fixed-size tuple) is also &lt;code&gt;isbits&lt;/code&gt; &lt;strong&gt;if and only if&lt;/strong&gt; all of its fields are themselves &lt;code&gt;isbits&lt;/code&gt; types.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Analogy (C &lt;code&gt;struct&lt;/code&gt;):&lt;/strong&gt; Think of an &lt;code&gt;isbits struct&lt;/code&gt; as directly equivalent to a C &lt;code&gt;struct&lt;/code&gt;. A Julia &lt;code&gt;struct Point { x::Float64; y::Float64 }&lt;/code&gt; has the exact same 16-byte memory layout as its C counterpart. This block of data can be efficiently copied, stack-allocated by the compiler, passed in CPU registers, or stored contiguously ("inlined") within an array without any indirection.&lt;/li&gt;

&lt;/ul&gt;




&lt;h3&gt;
  
  
  Non-&lt;code&gt;isbits&lt;/code&gt; Types (Data is "Referenced")
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Definition:&lt;/strong&gt; These are types whose instances contain &lt;strong&gt;references (pointers)&lt;/strong&gt; to data stored elsewhere, typically on the heap. The object itself might be small (just a pointer or a header with pointers), but it points to potentially large amounts of data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Characteristics:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;May Contain Pointers:&lt;/strong&gt; They have fields whose types are non-&lt;code&gt;isbits&lt;/code&gt; (like &lt;code&gt;String&lt;/code&gt;, &lt;code&gt;Array&lt;/code&gt;, &lt;code&gt;Dict&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Includes All Mutables:&lt;/strong&gt; All &lt;code&gt;mutable struct&lt;/code&gt;s are &lt;strong&gt;always non-&lt;code&gt;isbits&lt;/code&gt;&lt;/strong&gt;, even if they only contain &lt;code&gt;isbits&lt;/code&gt; fields (e.g., &lt;code&gt;mutable struct MutablePoint { x::Float64; y::Float64 }&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Why Mutables are Non-&lt;code&gt;isbits&lt;/code&gt;:&lt;/strong&gt; A mutable object must have a stable, unique identity (memory address) so that modifications made through one reference are visible to all other references. This requires heap allocation and access via pointers.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Examples:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;String&lt;/code&gt; (contains a pointer to its UTF-8 byte data on the heap).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Vector{T}&lt;/code&gt; (contains a pointer to its element buffer on the heap).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Dict{K,V}&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Any &lt;code&gt;mutable struct&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Any immutable &lt;code&gt;struct&lt;/code&gt; that contains a non-&lt;code&gt;isbits&lt;/code&gt; field (e.g., &lt;code&gt;struct LabeledPoint { p::Point; label::String }&lt;/code&gt; is non-&lt;code&gt;isbits&lt;/code&gt; because &lt;code&gt;String&lt;/code&gt; is non-&lt;code&gt;isbits&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Analogy (Array Layout):&lt;/strong&gt; A &lt;code&gt;Vector{Point}&lt;/code&gt; (where &lt;code&gt;Point&lt;/code&gt; is &lt;code&gt;isbits&lt;/code&gt;) is stored as a single, contiguous block of &lt;code&gt;Float64&lt;/code&gt; data: &lt;code&gt;[x1, y1, x2, y2, ...]&lt;/code&gt;. This is an &lt;strong&gt;Array of Structs (AoS)&lt;/strong&gt;. In contrast, a &lt;code&gt;Vector{String}&lt;/code&gt; is stored as a contiguous block of &lt;strong&gt;pointers&lt;/strong&gt;: &lt;code&gt;[ptr1, ptr2, ptr3, ...]&lt;/code&gt;, where each &lt;code&gt;ptr&lt;/code&gt; points to a separate &lt;code&gt;String&lt;/code&gt; object on the heap. This is an &lt;strong&gt;Array of Pointers&lt;/strong&gt;. Understanding this difference is paramount for achieving cache efficiency and enabling SIMD optimizations.&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;The &lt;code&gt;isbits&lt;/code&gt; property is the key determinant of an object's memory layout and performance characteristics in Julia. We can check this property using the &lt;code&gt;isbitstype&lt;/code&gt; function, as shown in the next lesson.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, &lt;code&gt;isbitstype&lt;/code&gt;:&lt;/strong&gt; "Return &lt;code&gt;true&lt;/code&gt; if type &lt;code&gt;T&lt;/code&gt; is a 'plain data' type..."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, Types:&lt;/strong&gt; Describes the properties of immutable and mutable composite types and their memory implications.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, &lt;code&gt;devdocs&lt;/code&gt;, "Memory layout of Julia Objects":&lt;/strong&gt; (Internal documentation) Provides deeper details on object representation.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;0088_isbits_examples.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0088_isbits_examples.jl&lt;/span&gt;
&lt;span class="c"&gt;# Demonstrates the 'isbitstype' check.&lt;/span&gt;

&lt;span class="c"&gt;# 1. Primitives are isbits types.&lt;/span&gt;
&lt;span class="c"&gt;# They are immutable and contain only data bits.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Primitives ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"isbitstype(Int64):   "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isbitstype&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Int64&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;   &lt;span class="c"&gt;# true&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"isbitstype(Float64): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isbitstype&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# true&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"isbitstype(Bool):    "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isbitstype&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Bool&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;    &lt;span class="c"&gt;# true&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"isbitstype(Char):    "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isbitstype&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Char&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;    &lt;span class="c"&gt;# true&lt;/span&gt;

&lt;span class="c"&gt;# --- Immutable Composites ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Immutable Composites ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 2. Immutable struct with ONLY isbits fields IS an isbits type.&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="nc"&gt; Point&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;
    &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"isbitstype(Point):   "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isbitstype&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Point&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;   &lt;span class="c"&gt;# true&lt;/span&gt;

&lt;span class="c"&gt;# 3. NTuple (fixed-size tuple) of isbits types IS an isbits type.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"isbitstype(NTuple{3, Int}): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isbitstype&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;NTuple&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="x"&gt;}))&lt;/span&gt; &lt;span class="c"&gt;# true&lt;/span&gt;

&lt;span class="c"&gt;# 4. Immutable struct containing a non-isbits field is NOT isbits.&lt;/span&gt;
&lt;span class="c"&gt;#    'String' holds a pointer to heap data, making it non-isbits.&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="nc"&gt; LabeledPoint&lt;/span&gt;
    &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Point&lt;/span&gt;      &lt;span class="c"&gt;# Point is isbits&lt;/span&gt;
    &lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="c"&gt;# String is NOT isbits&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"isbitstype(LabeledPoint): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isbitstype&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LabeledPoint&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# false&lt;/span&gt;

&lt;span class="c"&gt;# --- Mutables and References ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Mutables and References ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 5. Mutable struct is NEVER an isbits type, even with isbits fields.&lt;/span&gt;
&lt;span class="c"&gt;#    It must be heap-allocated to have a stable identity.&lt;/span&gt;
&lt;span class="k"&gt;mutable struct&lt;/span&gt;&lt;span class="nc"&gt; MutablePoint&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;
    &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"isbitstype(MutablePoint): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isbitstype&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MutablePoint&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# false&lt;/span&gt;

&lt;span class="c"&gt;# 6. Types that inherently involve pointers/references are NOT isbits types.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"isbitstype(String):       "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isbitstype&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;       &lt;span class="c"&gt;# false&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"isbitstype(Vector{Int}):  "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isbitstype&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Vector&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="x"&gt;}))&lt;/span&gt;  &lt;span class="c"&gt;# false&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"isbitstype(Dict{Int, Int}): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isbitstype&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Dict&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="x"&gt;}))&lt;/span&gt; &lt;span class="c"&gt;# false&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"isbitstype(Channel{Int}): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isbitstype&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Channel&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="x"&gt;}))&lt;/span&gt; &lt;span class="c"&gt;# false&lt;/span&gt;

&lt;span class="c"&gt;# 7. Abstract types are NOT isbits types.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"isbitstype(Number):       "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isbitstype&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Number&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;       &lt;span class="c"&gt;# false&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"isbitstype(AbstractArray):"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isbitstype&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;AbstractArray&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script uses the built-in &lt;code&gt;isbitstype(T)&lt;/code&gt; function to concretely demonstrate the rules outlined in the previous lesson for determining if a type is an &lt;strong&gt;&lt;code&gt;isbits&lt;/code&gt; type&lt;/strong&gt; (a plain-data type). Understanding this classification is crucial for predicting memory layout and performance.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Core Concept: &lt;code&gt;isbitstype(T::Type)&lt;/code&gt;&lt;/strong&gt;
This function takes a &lt;strong&gt;Type&lt;/strong&gt; object (like &lt;code&gt;Int64&lt;/code&gt;, &lt;code&gt;Point&lt;/code&gt;, &lt;code&gt;String&lt;/code&gt;) as input and returns &lt;code&gt;true&lt;/code&gt; if that type meets the criteria for being an &lt;code&gt;isbits&lt;/code&gt; type, and &lt;code&gt;false&lt;/code&gt; otherwise. Recall, the criteria are:&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1.  The type must be **immutable**.
2.  The type must **contain no references** (pointers) to other memory locations; all its data must be stored directly within its own memory footprint.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Verification of Rules:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Primitives (&lt;code&gt;Int64&lt;/code&gt;, &lt;code&gt;Float64&lt;/code&gt;, etc.):&lt;/strong&gt; As expected, these fundamental types are &lt;code&gt;isbits&lt;/code&gt; (&lt;code&gt;true&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Immutable &lt;code&gt;struct&lt;/code&gt; (&lt;code&gt;Point&lt;/code&gt;):&lt;/strong&gt; Because &lt;code&gt;Point&lt;/code&gt; is immutable and contains only &lt;code&gt;Float64&lt;/code&gt; fields (which are &lt;code&gt;isbits&lt;/code&gt;), &lt;code&gt;isbitstype(Point)&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt;. This confirms it has a C-like, contiguous memory layout.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;NTuple&lt;/code&gt;:&lt;/strong&gt; Similarly, &lt;code&gt;NTuple{3, Int}&lt;/code&gt; is a fixed-size, immutable collection of &lt;code&gt;isbits&lt;/code&gt; types, making it &lt;code&gt;isbits&lt;/code&gt; (&lt;code&gt;true&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Immutable &lt;code&gt;struct&lt;/code&gt; with Non-&lt;code&gt;isbits&lt;/code&gt; Field (&lt;code&gt;LabeledPoint&lt;/code&gt;):&lt;/strong&gt; &lt;code&gt;LabeledPoint&lt;/code&gt; contains a &lt;code&gt;String&lt;/code&gt;. Since &lt;code&gt;String&lt;/code&gt; itself is not &lt;code&gt;isbits&lt;/code&gt; (it holds a pointer to heap data), the entire &lt;code&gt;LabeledPoint&lt;/code&gt; struct becomes non-&lt;code&gt;isbits&lt;/code&gt; (&lt;code&gt;false&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;mutable struct&lt;/code&gt; (&lt;code&gt;MutablePoint&lt;/code&gt;):&lt;/strong&gt; &lt;code&gt;isbitstype(MutablePoint)&lt;/code&gt; is &lt;code&gt;false&lt;/code&gt;. This confirms the rule: &lt;strong&gt;all &lt;code&gt;mutable struct&lt;/code&gt;s are non-&lt;code&gt;isbits&lt;/code&gt;&lt;/strong&gt;, regardless of their fields, because they require heap allocation for a stable identity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reference Types (&lt;code&gt;String&lt;/code&gt;, &lt;code&gt;Vector&lt;/code&gt;, &lt;code&gt;Dict&lt;/code&gt;):&lt;/strong&gt; These types inherently involve pointers to heap-allocated data, so they are non-&lt;code&gt;isbits&lt;/code&gt; (&lt;code&gt;false&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Abstract Types (&lt;code&gt;Number&lt;/code&gt;, &lt;code&gt;AbstractArray&lt;/code&gt;):&lt;/strong&gt; Abstract types do not have a single, fixed memory layout; they represent a &lt;em&gt;set&lt;/em&gt; of possible concrete types. Therefore, they cannot be &lt;code&gt;isbits&lt;/code&gt; (&lt;code&gt;false&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Performance Implication Summary:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Types for which &lt;code&gt;isbitstype&lt;/code&gt; returns &lt;code&gt;true&lt;/code&gt; (like &lt;code&gt;Point&lt;/code&gt;) are candidates for &lt;strong&gt;stack allocation&lt;/strong&gt;, &lt;strong&gt;register passing&lt;/strong&gt;, and &lt;strong&gt;inlined storage&lt;/strong&gt; in arrays (&lt;code&gt;Vector{Point}&lt;/code&gt; is contiguous).&lt;/li&gt;
&lt;li&gt;Types for which &lt;code&gt;isbitstype&lt;/code&gt; returns &lt;code&gt;false&lt;/code&gt; (like &lt;code&gt;MutablePoint&lt;/code&gt; or &lt;code&gt;LabeledPoint&lt;/code&gt;) are generally &lt;strong&gt;heap-allocated&lt;/strong&gt;, passed &lt;strong&gt;by reference (pointer)&lt;/strong&gt;, and stored as &lt;strong&gt;pointers&lt;/strong&gt; in arrays (&lt;code&gt;Vector{MutablePoint}&lt;/code&gt; is an array of pointers).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Knowing how to check &lt;code&gt;isbitstype&lt;/code&gt; allows you to verify your assumptions about how your custom types will be handled by the compiler and predict their performance characteristics.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Base Documentation, &lt;code&gt;isbitstype&lt;/code&gt;:&lt;/strong&gt; "Return &lt;code&gt;true&lt;/code&gt; if type &lt;code&gt;T&lt;/code&gt; is a 'plain data' type..."&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0088_isbits_examples.jl
&lt;span class="nt"&gt;---&lt;/span&gt; Primitives &lt;span class="nt"&gt;---&lt;/span&gt;
isbitstype&lt;span class="o"&gt;(&lt;/span&gt;Int64&lt;span class="o"&gt;)&lt;/span&gt;:   &lt;span class="nb"&gt;true
&lt;/span&gt;isbitstype&lt;span class="o"&gt;(&lt;/span&gt;Float64&lt;span class="o"&gt;)&lt;/span&gt;: &lt;span class="nb"&gt;true
&lt;/span&gt;isbitstype&lt;span class="o"&gt;(&lt;/span&gt;Bool&lt;span class="o"&gt;)&lt;/span&gt;:    &lt;span class="nb"&gt;true
&lt;/span&gt;isbitstype&lt;span class="o"&gt;(&lt;/span&gt;Char&lt;span class="o"&gt;)&lt;/span&gt;:    &lt;span class="nb"&gt;true&lt;/span&gt;

&lt;span class="nt"&gt;---&lt;/span&gt; Immutable Composites &lt;span class="nt"&gt;---&lt;/span&gt;
isbitstype&lt;span class="o"&gt;(&lt;/span&gt;Point&lt;span class="o"&gt;)&lt;/span&gt;:   &lt;span class="nb"&gt;true
&lt;/span&gt;isbitstype&lt;span class="o"&gt;(&lt;/span&gt;NTuple&lt;span class="o"&gt;{&lt;/span&gt;3, Int&lt;span class="o"&gt;})&lt;/span&gt;: &lt;span class="nb"&gt;true
&lt;/span&gt;isbitstype&lt;span class="o"&gt;(&lt;/span&gt;LabeledPoint&lt;span class="o"&gt;)&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;

&lt;span class="nt"&gt;---&lt;/span&gt; Mutables and References &lt;span class="nt"&gt;---&lt;/span&gt;
isbitstype&lt;span class="o"&gt;(&lt;/span&gt;MutablePoint&lt;span class="o"&gt;)&lt;/span&gt;: &lt;span class="nb"&gt;false
&lt;/span&gt;isbitstype&lt;span class="o"&gt;(&lt;/span&gt;String&lt;span class="o"&gt;)&lt;/span&gt;:       &lt;span class="nb"&gt;false
&lt;/span&gt;isbitstype&lt;span class="o"&gt;(&lt;/span&gt;Vector&lt;span class="o"&gt;{&lt;/span&gt;Int&lt;span class="o"&gt;})&lt;/span&gt;:  &lt;span class="nb"&gt;false
&lt;/span&gt;isbitstype&lt;span class="o"&gt;(&lt;/span&gt;Dict&lt;span class="o"&gt;{&lt;/span&gt;Int, Int&lt;span class="o"&gt;})&lt;/span&gt;: &lt;span class="nb"&gt;false
&lt;/span&gt;isbitstype&lt;span class="o"&gt;(&lt;/span&gt;Channel&lt;span class="o"&gt;{&lt;/span&gt;Int&lt;span class="o"&gt;})&lt;/span&gt;: &lt;span class="nb"&gt;false
&lt;/span&gt;isbitstype&lt;span class="o"&gt;(&lt;/span&gt;Number&lt;span class="o"&gt;)&lt;/span&gt;:       &lt;span class="nb"&gt;false
&lt;/span&gt;isbitstype&lt;span class="o"&gt;(&lt;/span&gt;AbstractArray&lt;span class="o"&gt;)&lt;/span&gt;:false
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;0089_sizeof.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0089_sizeof.jl&lt;/span&gt;
&lt;span class="c"&gt;# Demonstrates 'sizeof()' and introduces data alignment/padding.&lt;/span&gt;

&lt;span class="c"&gt;# 1. 'sizeof()' on primitive (isbits) types.&lt;/span&gt;
&lt;span class="c"&gt;#    Returns the number of bytes occupied by the type in memory.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Primitive Types ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sizeof(Int8):   "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sizeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Int8&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;   &lt;span class="c"&gt;# 1 byte&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sizeof(Int16):  "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sizeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Int16&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;  &lt;span class="c"&gt;# 2 bytes&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sizeof(Int32):  "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sizeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Int32&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;  &lt;span class="c"&gt;# 4 bytes&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sizeof(Int64):  "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sizeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Int64&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;  &lt;span class="c"&gt;# 8 bytes&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sizeof(Float64):"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sizeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# 8 bytes&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sizeof(Bool):   "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sizeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Bool&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;   &lt;span class="c"&gt;# 1 byte&lt;/span&gt;

&lt;span class="c"&gt;# Size of a pointer (depends on architecture, typically 8 on 64-bit)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sizeof(Ptr{Nothing}): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sizeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Ptr&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;Nothing&lt;/span&gt;&lt;span class="x"&gt;}))&lt;/span&gt;

&lt;span class="c"&gt;# --- isbits Structs ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- isbits Structs ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 2. 'sizeof()' on a simple isbits struct.&lt;/span&gt;
&lt;span class="c"&gt;#    Size is the sum of the sizes of its fields (plus padding).&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="nc"&gt; Point&lt;/span&gt; &lt;span class="c"&gt;# isbits&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt; &lt;span class="c"&gt;# 8 bytes&lt;/span&gt;
    &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt; &lt;span class="c"&gt;# 8 bytes&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sizeof(Point):  "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sizeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Point&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;  &lt;span class="c"&gt;# 8 + 8 = 16 bytes&lt;/span&gt;

&lt;span class="c"&gt;# 3. 'sizeof()' on an isbits struct requiring padding.&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="nc"&gt; PaddedData&lt;/span&gt; &lt;span class="c"&gt;# isbits&lt;/span&gt;
    &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int8&lt;/span&gt;    &lt;span class="c"&gt;# 1 byte&lt;/span&gt;
    &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int64&lt;/span&gt;   &lt;span class="c"&gt;# 8 bytes&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="c"&gt;# Expected size might seem like 1 + 8 = 9 bytes. Due to alignment&lt;/span&gt;
&lt;span class="c"&gt;# padding is added, resulting in 16 bytes.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sizeof(PaddedData): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sizeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PaddedData&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# Usually 16 bytes!&lt;/span&gt;

&lt;span class="c"&gt;# --- Non-isbits Types (Instances) ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Non-isbits Types (Instances) ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 4. 'sizeof(T)' errors for non-isbits *types* like String or Vector{Int}.&lt;/span&gt;
&lt;span class="c"&gt;#    However, 'sizeof(instance)' has specific definitions for some types:&lt;/span&gt;
&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"hello"&lt;/span&gt; &lt;span class="c"&gt;# 5 characters (5 bytes in UTF-8)&lt;/span&gt;
&lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt; &lt;span class="c"&gt;# 3 Int64 elements (3 * 8 = 24 bytes of data)&lt;/span&gt;

&lt;span class="c"&gt;# sizeof(s::String) returns the number of code units (bytes for UTF-8).&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sizeof(instance s): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sizeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# 5 bytes&lt;/span&gt;

&lt;span class="c"&gt;# sizeof(v::Array) returns the size of the data buffer in bytes.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sizeof(instance v): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sizeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# 24 bytes (length * element size)&lt;/span&gt;

&lt;span class="c"&gt;# NOTE: Neither of these returns the size of the object *header* itself.&lt;/span&gt;

&lt;span class="c"&gt;# --- Total Memory Usage ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Total Memory (Base.summarysize) ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 5. 'Base.summarysize()' calculates the total memory used by an object,&lt;/span&gt;
&lt;span class="c"&gt;#    including the object header/metadata AND any heap-allocated data it points to.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Base.summarysize(s): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;summarysize&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# Size of String object + size of "hello" bytes + overhead&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Base.summarysize(v): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;summarysize&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# Size of Vector object + size of [1, 2, 3] data + overhead&lt;/span&gt;

&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Point&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# isbits struct&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Base.summarysize(p): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;summarysize&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# Same as sizeof(Point)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces the &lt;code&gt;sizeof()&lt;/code&gt; function, which reports the memory size occupied by a type or value, and reveals the important concept of &lt;strong&gt;data alignment&lt;/strong&gt; and &lt;strong&gt;padding&lt;/strong&gt; in &lt;code&gt;struct&lt;/code&gt; layouts.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Core Concept: &lt;code&gt;sizeof(T)&lt;/code&gt; and &lt;code&gt;sizeof(x)&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
The &lt;code&gt;sizeof()&lt;/code&gt; function returns the number of bytes required to store a value of type &lt;code&gt;T&lt;/code&gt; or the specific value &lt;code&gt;x&lt;/code&gt;. Its behavior depends on the type:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For &lt;strong&gt;&lt;code&gt;isbits&lt;/code&gt; types&lt;/strong&gt; (primitives, immutable structs with &lt;code&gt;isbits&lt;/code&gt; fields), &lt;code&gt;sizeof(T)&lt;/code&gt; gives the total size of the actual data representation, including any padding needed for alignment. For &lt;code&gt;Point&lt;/code&gt;, it's &lt;code&gt;16&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;For &lt;strong&gt;non-&lt;code&gt;isbits&lt;/code&gt; *types&lt;/strong&gt;* (like &lt;code&gt;String&lt;/code&gt; or &lt;code&gt;Vector{Int}&lt;/code&gt;), &lt;code&gt;sizeof(T)&lt;/code&gt; &lt;strong&gt;throws an error&lt;/strong&gt; because these types don't have a single, fixed-size binary representation.&lt;/li&gt;
&lt;li&gt;For &lt;strong&gt;instances&lt;/strong&gt; of &lt;em&gt;some&lt;/em&gt; non-&lt;code&gt;isbits&lt;/code&gt; types, &lt;code&gt;sizeof(x)&lt;/code&gt; has specific definitions:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;sizeof(s::String)&lt;/code&gt; returns the number of bytes in the string's data (&lt;code&gt;ncodeunits(s)&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sizeof(v::Array)&lt;/code&gt; returns the size in bytes of the array's &lt;strong&gt;data buffer&lt;/strong&gt; (&lt;code&gt;length(v) * sizeof(eltype(v))&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Important:&lt;/strong&gt; For non-&lt;code&gt;isbits&lt;/code&gt; instances like &lt;code&gt;s&lt;/code&gt; and &lt;code&gt;v&lt;/code&gt;, &lt;code&gt;sizeof(instance)&lt;/code&gt; &lt;strong&gt;does not&lt;/strong&gt; report the size of the object's header or reference part; it reports the size of the referenced &lt;em&gt;data&lt;/em&gt;.&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Data Alignment and Padding&lt;/strong&gt;&lt;br&gt;
The output for &lt;code&gt;sizeof(PaddedData)&lt;/code&gt; (16 bytes, not 9) is crucial. It demonstrates &lt;strong&gt;data alignment&lt;/strong&gt;. CPUs access memory most efficiently when data is aligned (e.g., an 8-byte &lt;code&gt;Int64&lt;/code&gt; starts at an address multiple of 8). To ensure &lt;code&gt;b::Int64&lt;/code&gt; is aligned, the compiler inserts &lt;strong&gt;7 bytes of unused padding&lt;/strong&gt; after &lt;code&gt;a::Int8&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Memory Layout:&lt;/strong&gt; &lt;code&gt;[ a (1 byte) | padding (7 bytes) | b (8 bytes) ]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;This padding is added automatically for performance. The next lesson (&lt;code&gt;fieldoffset&lt;/code&gt;) will show this explicitly.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;Base.summarysize(obj)&lt;/code&gt; vs. &lt;code&gt;sizeof(obj)&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;sizeof(obj)&lt;/code&gt; gives the size of the inline data (&lt;code&gt;isbits&lt;/code&gt;) or the referenced data (&lt;code&gt;String&lt;/code&gt;, &lt;code&gt;Array&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Base.summarysize(obj)&lt;/code&gt; is the function for the &lt;strong&gt;total memory footprint&lt;/strong&gt;, including the object's header/reference itself &lt;strong&gt;and&lt;/strong&gt; any out-of-line (heap-allocated) data it references, plus potential GC overhead.&lt;/li&gt;
&lt;li&gt;For &lt;code&gt;isbits&lt;/code&gt; types like &lt;code&gt;p&lt;/code&gt;, &lt;code&gt;summarysize(p) == sizeof(p)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;For non-&lt;code&gt;isbits&lt;/code&gt; types like &lt;code&gt;s&lt;/code&gt; and &lt;code&gt;v&lt;/code&gt;, &lt;code&gt;summarysize(obj)&lt;/code&gt; is generally larger than &lt;code&gt;sizeof(obj)&lt;/code&gt; because it includes the header size and overhead. The results (&lt;code&gt;summarysize(s)=13&lt;/code&gt;, &lt;code&gt;summarysize(v)=64&lt;/code&gt;) reflect this accurately (e.g., &lt;code&gt;13 = 5 bytes data + 8 bytes header? + overhead?&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Understanding &lt;code&gt;sizeof&lt;/code&gt; (especially its specific behavior for &lt;code&gt;String&lt;/code&gt; and &lt;code&gt;Array&lt;/code&gt; instances) and &lt;code&gt;Base.summarysize&lt;/code&gt; is vital for analyzing memory usage. Understanding alignment is key for optimizing data structures and C interop.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Base Documentation, &lt;code&gt;sizeof&lt;/code&gt;:&lt;/strong&gt; "Return the size, in bytes, of the canonical binary representation..." Also notes the specific method &lt;code&gt;sizeof(s::String) = ncodeunits(s)&lt;/code&gt;. Behavior for &lt;code&gt;Array&lt;/code&gt; instances seems less explicitly documented but empirically matches data buffer size.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Base Documentation, &lt;code&gt;Base.summarysize&lt;/code&gt;:&lt;/strong&gt; "Compute the total size, in bytes, of an object and all its fields and elements."&lt;/li&gt;
&lt;li&gt;(Data alignment is a general computer architecture concept).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0089_sizeof.jl
&lt;span class="nt"&gt;---&lt;/span&gt; Primitive Types &lt;span class="nt"&gt;---&lt;/span&gt;
sizeof&lt;span class="o"&gt;(&lt;/span&gt;Int8&lt;span class="o"&gt;)&lt;/span&gt;:   1
sizeof&lt;span class="o"&gt;(&lt;/span&gt;Int16&lt;span class="o"&gt;)&lt;/span&gt;:  2
sizeof&lt;span class="o"&gt;(&lt;/span&gt;Int32&lt;span class="o"&gt;)&lt;/span&gt;:  4
sizeof&lt;span class="o"&gt;(&lt;/span&gt;Int64&lt;span class="o"&gt;)&lt;/span&gt;:  8
sizeof&lt;span class="o"&gt;(&lt;/span&gt;Float64&lt;span class="o"&gt;)&lt;/span&gt;:8
sizeof&lt;span class="o"&gt;(&lt;/span&gt;Bool&lt;span class="o"&gt;)&lt;/span&gt;:   1
sizeof&lt;span class="o"&gt;(&lt;/span&gt;Ptr&lt;span class="o"&gt;{&lt;/span&gt;Nothing&lt;span class="o"&gt;})&lt;/span&gt;: 8

&lt;span class="nt"&gt;---&lt;/span&gt; isbits Structs &lt;span class="nt"&gt;---&lt;/span&gt;
sizeof&lt;span class="o"&gt;(&lt;/span&gt;Point&lt;span class="o"&gt;)&lt;/span&gt;:  16
sizeof&lt;span class="o"&gt;(&lt;/span&gt;PaddedData&lt;span class="o"&gt;)&lt;/span&gt;: 16

&lt;span class="nt"&gt;---&lt;/span&gt; Non-isbits Types &lt;span class="o"&gt;(&lt;/span&gt;Instances&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
sizeof&lt;span class="o"&gt;(&lt;/span&gt;instance s&lt;span class="o"&gt;)&lt;/span&gt;: 5
sizeof&lt;span class="o"&gt;(&lt;/span&gt;instance v&lt;span class="o"&gt;)&lt;/span&gt;: 24

&lt;span class="nt"&gt;---&lt;/span&gt; Total Memory &lt;span class="o"&gt;(&lt;/span&gt;Base.summarysize&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
Base.summarysize&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt;: 13
Base.summarysize&lt;span class="o"&gt;(&lt;/span&gt;v&lt;span class="o"&gt;)&lt;/span&gt;: 64
Base.summarysize&lt;span class="o"&gt;(&lt;/span&gt;p&lt;span class="o"&gt;)&lt;/span&gt;: 16
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;0090_fieldoffset_and_alignment.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0090_fieldoffset_and_alignment.jl&lt;/span&gt;
&lt;span class="c"&gt;# Demonstrates field offsets and alignment explicitly.&lt;/span&gt;

&lt;span class="c"&gt;# 1. Reuse the structs from the previous lesson.&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="nc"&gt; PaddedData&lt;/span&gt; &lt;span class="c"&gt;# isbits, sizeof = 16&lt;/span&gt;
    &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int8&lt;/span&gt;    &lt;span class="c"&gt;# 1 byte&lt;/span&gt;
    &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int64&lt;/span&gt;   &lt;span class="c"&gt;# 8 bytes&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="nc"&gt; OptimizedData&lt;/span&gt; &lt;span class="c"&gt;# isbits, sizeof = 16 (often)&lt;/span&gt;
    &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int64&lt;/span&gt;   &lt;span class="c"&gt;# 8 bytes&lt;/span&gt;
    &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int8&lt;/span&gt;    &lt;span class="c"&gt;# 1 byte&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="nc"&gt; CompactData&lt;/span&gt; &lt;span class="c"&gt;# isbits, sizeof = 16 (often)&lt;/span&gt;
    &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int64&lt;/span&gt; &lt;span class="c"&gt;# 8 bytes&lt;/span&gt;
    &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int32&lt;/span&gt; &lt;span class="c"&gt;# 4 bytes&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int16&lt;/span&gt; &lt;span class="c"&gt;# 2 bytes&lt;/span&gt;
    &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int8&lt;/span&gt;  &lt;span class="c"&gt;# 1 byte&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;


&lt;span class="c"&gt;# --- Alignment ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Data Alignment Requirements ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 2. Base.datatype_alignment(T)&lt;/span&gt;
&lt;span class="c"&gt;#    Returns the minimum required alignment boundary (in bytes) for type T.&lt;/span&gt;
&lt;span class="c"&gt;#    Usually determined by the size of the largest primitive field.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Alignment of Int8:  "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;datatype_alignment&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Int8&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;  &lt;span class="c"&gt;# 1&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Alignment of Int64: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;datatype_alignment&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Int64&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# 8 (on 64-bit)&lt;/span&gt;

&lt;span class="c"&gt;# Alignment of a struct is usually the maximum alignment of its fields.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Alignment of PaddedData: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;datatype_alignment&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PaddedData&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# 8&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Alignment of OptimizedData: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;datatype_alignment&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OptimizedData&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# 8&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Alignment of CompactData: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;datatype_alignment&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CompactData&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# 8&lt;/span&gt;


&lt;span class="c"&gt;# --- Field Offsets ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Field Offsets (Proof of Padding) ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 3. fieldoffset(Type, field_index)&lt;/span&gt;
&lt;span class="c"&gt;#    Returns the byte offset of a field from the beginning of the struct.&lt;/span&gt;
&lt;span class="c"&gt;#    Field indices are 1-based.&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- PaddedData (size &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;(sizeof(PaddedData))) ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# Field 'a' (index 1) starts at byte 0.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Offset of a (field 1): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fieldoffset&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PaddedData&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# 0&lt;/span&gt;
&lt;span class="c"&gt;# Field 'b' (index 2) requires 8-byte alignment.&lt;/span&gt;
&lt;span class="c"&gt;# Compiler inserts 7 bytes padding after 'a'.&lt;/span&gt;
&lt;span class="c"&gt;# 'b' starts at byte 8.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Offset of b (field 2): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fieldoffset&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PaddedData&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# 8 (NOT 1!)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- OptimizedData (size &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;(sizeof(OptimizedData))) ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# Field 'b' (index 1) starts at byte 0.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Offset of b (field 1): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fieldoffset&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OptimizedData&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# 0&lt;/span&gt;
&lt;span class="c"&gt;# Field 'a' (index 2) starts immediately after 'b' at byte 8.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Offset of a (field 2): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fieldoffset&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OptimizedData&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# 8&lt;/span&gt;
&lt;span class="c"&gt;# Note: The total size might still be 16 due to struct-level alignment&lt;/span&gt;
&lt;span class="c"&gt;# requirements (struct size often padded to match its alignment).&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- CompactData (size &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;(sizeof(CompactData))) ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Offset of a (field 1): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fieldoffset&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CompactData&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# 0  (Int64, size 8)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Offset of b (field 2): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fieldoffset&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CompactData&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# 8  (Int32, size 4)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Offset of c (field 3): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fieldoffset&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CompactData&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# 12 (Int16, size 2)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Offset of d (field 4): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fieldoffset&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CompactData&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# 14 (Int8, size 1)&lt;/span&gt;
&lt;span class="c"&gt;# Total size used by fields: 8+4+2+1 = 15 bytes.&lt;/span&gt;
&lt;span class="c"&gt;# Struct size is 16 bytes due to struct-level padding to meet alignment of 8.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script delves deeper into the memory layout concepts introduced with &lt;code&gt;sizeof&lt;/code&gt;, specifically demonstrating &lt;strong&gt;data alignment requirements&lt;/strong&gt; and using &lt;code&gt;fieldoffset&lt;/code&gt; to explicitly &lt;strong&gt;reveal the padding&lt;/strong&gt; inserted by the compiler.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Core Concept: Alignment&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Base.datatype_alignment(T)&lt;/code&gt;:&lt;/strong&gt; This function reports the &lt;strong&gt;alignment requirement&lt;/strong&gt; (in bytes) for a type &lt;code&gt;T&lt;/code&gt;. For optimal performance, the starting memory address of a value of type &lt;code&gt;T&lt;/code&gt; should be a multiple of its alignment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Primitives:&lt;/strong&gt; The alignment of a primitive type (like &lt;code&gt;Int8&lt;/code&gt;, &lt;code&gt;Int64&lt;/code&gt;) is usually equal to its size (up to a maximum, often 8 or 16 bytes, depending on the architecture). &lt;code&gt;Int64&lt;/code&gt; requires 8-byte alignment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structs:&lt;/strong&gt; The alignment requirement of a &lt;code&gt;struct&lt;/code&gt; is typically the &lt;strong&gt;maximum&lt;/strong&gt; alignment requirement of any of its fields. Since &lt;code&gt;PaddedData&lt;/code&gt;, &lt;code&gt;OptimizedData&lt;/code&gt;, and &lt;code&gt;CompactData&lt;/code&gt; all contain an &lt;code&gt;Int64&lt;/code&gt;, their alignment requirement is 8 bytes.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Core Concept: &lt;code&gt;fieldoffset(Type, field_index)&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This function is the Julia equivalent of C's &lt;code&gt;offsetof&lt;/code&gt; macro. It takes a &lt;code&gt;struct&lt;/code&gt; type and the &lt;strong&gt;1-based index&lt;/strong&gt; of a field and returns the &lt;strong&gt;byte offset&lt;/strong&gt; of that field from the start of the &lt;code&gt;struct&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;This allows us to precisely see where each field is placed in memory.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Proof of Padding (&lt;code&gt;PaddedData&lt;/code&gt;)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;struct PaddedData { a::Int8; b::Int64 }&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fieldoffset(PaddedData, 1)&lt;/code&gt; (for &lt;code&gt;a&lt;/code&gt;) is &lt;code&gt;0&lt;/code&gt;. The first field starts at the beginning.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fieldoffset(PaddedData, 2)&lt;/code&gt; (for &lt;code&gt;b&lt;/code&gt;) is &lt;code&gt;8&lt;/code&gt;, &lt;strong&gt;not&lt;/strong&gt; &lt;code&gt;1&lt;/code&gt;. This provides concrete proof of padding. &lt;code&gt;a&lt;/code&gt; occupies byte 0. &lt;code&gt;b&lt;/code&gt; requires 8-byte alignment, so it cannot start at byte 1. The compiler inserts &lt;strong&gt;7 bytes of padding&lt;/strong&gt; (bytes 1 through 7) so that &lt;code&gt;b&lt;/code&gt; can start at the correctly aligned byte 8.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory Layout:&lt;/strong&gt; &lt;code&gt;[ a (byte 0) | padding (bytes 1-7) | b (bytes 8-15) ]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The total size becomes 16 bytes.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Field Order (&lt;code&gt;OptimizedData&lt;/code&gt;)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;struct OptimizedData { b::Int64; a::Int8 }&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fieldoffset(OptimizedData, 1)&lt;/code&gt; (for &lt;code&gt;b&lt;/code&gt;) is &lt;code&gt;0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fieldoffset(OptimizedData, 2)&lt;/code&gt; (for &lt;code&gt;a&lt;/code&gt;) is &lt;code&gt;8&lt;/code&gt;. It starts immediately after &lt;code&gt;b&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Packing:&lt;/strong&gt; No padding is needed &lt;em&gt;between&lt;/em&gt; &lt;code&gt;b&lt;/code&gt; and &lt;code&gt;a&lt;/code&gt;. However, the total &lt;code&gt;sizeof(OptimizedData)&lt;/code&gt; is often still 16. This is because the struct &lt;em&gt;itself&lt;/em&gt; must meet its alignment requirement (8 bytes). To ensure that in an array &lt;code&gt;Vector{OptimizedData}&lt;/code&gt; each element starts on an 8-byte boundary, the compiler may add padding &lt;em&gt;at the end&lt;/em&gt; of the struct, bringing the total size from 9 (8+1) up to the next multiple of 8, which is 16.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Performance Guideline (&lt;code&gt;CompactData&lt;/code&gt;)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;struct CompactData { a::Int64; b::Int32; c::Int16; d::Int8 }&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Offsets: 0, 8, 12, 14.&lt;/li&gt;
&lt;li&gt;By ordering fields from &lt;strong&gt;largest alignment to smallest alignment&lt;/strong&gt;, we minimize the padding &lt;em&gt;between&lt;/em&gt; fields. In this case, no padding is needed between fields.&lt;/li&gt;
&lt;li&gt;The total size occupied by data is &lt;code&gt;8+4+2+1 = 15&lt;/code&gt; bytes.&lt;/li&gt;
&lt;li&gt;The final &lt;code&gt;sizeof(CompactData)&lt;/code&gt; is 16 bytes because of the struct-level padding added at the end to satisfy the overall 8-byte alignment requirement.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Practice:&lt;/strong&gt; While Julia's compiler handles this automatically, manually ordering struct fields from largest to smallest is a standard C/C++ practice that guarantees the most compact memory layout and is good habit for performance-conscious code.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Understanding alignment and offsets is essential for writing highly optimized code (minimizing wasted memory and ensuring cache efficiency) and for correctly interfacing with C/C++ libraries that rely on specific struct layouts.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Base Documentation, &lt;code&gt;fieldoffset&lt;/code&gt;:&lt;/strong&gt; "Get the byte offset of a field relative to the start of the composite type."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Base Documentation, &lt;code&gt;Base.datatype_alignment&lt;/code&gt;:&lt;/strong&gt; "Get the default alignment for a type."&lt;/li&gt;
&lt;li&gt;(CPU architecture manuals and C language standards define alignment rules, which Julia generally follows.)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0090_fieldoffset_and_alignment.jl
&lt;span class="nt"&gt;---&lt;/span&gt; Data Alignment Requirements &lt;span class="nt"&gt;---&lt;/span&gt;
Alignment of Int8:  1
Alignment of Int64: 8
Alignment of PaddedData: 8
Alignment of OptimizedData: 8
Alignment of CompactData: 8

&lt;span class="nt"&gt;---&lt;/span&gt; Field Offsets &lt;span class="o"&gt;(&lt;/span&gt;Proof of Padding&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
&lt;span class="nt"&gt;---&lt;/span&gt; PaddedData &lt;span class="o"&gt;(&lt;/span&gt;size 16&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
Offset of a &lt;span class="o"&gt;(&lt;/span&gt;field 1&lt;span class="o"&gt;)&lt;/span&gt;: 0
Offset of b &lt;span class="o"&gt;(&lt;/span&gt;field 2&lt;span class="o"&gt;)&lt;/span&gt;: 8

&lt;span class="nt"&gt;---&lt;/span&gt; OptimizedData &lt;span class="o"&gt;(&lt;/span&gt;size 16&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
Offset of b &lt;span class="o"&gt;(&lt;/span&gt;field 1&lt;span class="o"&gt;)&lt;/span&gt;: 0
Offset of a &lt;span class="o"&gt;(&lt;/span&gt;field 2&lt;span class="o"&gt;)&lt;/span&gt;: 8

&lt;span class="nt"&gt;---&lt;/span&gt; CompactData &lt;span class="o"&gt;(&lt;/span&gt;size 16&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
Offset of a &lt;span class="o"&gt;(&lt;/span&gt;field 1&lt;span class="o"&gt;)&lt;/span&gt;: 0
Offset of b &lt;span class="o"&gt;(&lt;/span&gt;field 2&lt;span class="o"&gt;)&lt;/span&gt;: 8
Offset of c &lt;span class="o"&gt;(&lt;/span&gt;field 3&lt;span class="o"&gt;)&lt;/span&gt;: 12
Offset of d &lt;span class="o"&gt;(&lt;/span&gt;field 4&lt;span class="o"&gt;)&lt;/span&gt;: 14
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Pointers And Unsafe Memory Access
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;0091_pointer_from_objref.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0091_pointer_from_objref.jl&lt;/span&gt;
&lt;span class="c"&gt;# Getting raw pointers to Julia objects.&lt;/span&gt;

&lt;span class="c"&gt;# --- Case 1: Mutable Struct (Heap-Allocated Object) ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Mutable Struct ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# A mutable struct instance 'd' lives on the heap.&lt;/span&gt;
&lt;span class="k"&gt;mutable struct&lt;/span&gt;&lt;span class="nc"&gt; MyData&lt;/span&gt;
    &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int64&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MyData&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 'pointer_from_objref(obj)' returns a raw Ptr{Nothing} (like void*)&lt;/span&gt;
&lt;span class="c"&gt;# pointing to the beginning of the object's memory block on the heap.&lt;/span&gt;
&lt;span class="c"&gt;# The GC knows about 'd' and won't collect it while 'd' is reachable.&lt;/span&gt;
&lt;span class="n"&gt;ptr_d_obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pointer_from_objref&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Object d: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Pointer to d object (Ptr{Nothing}): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ptr_d_obj&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# --- Case 2: Array (Special Handling) ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Array ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;A&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt; &lt;span class="c"&gt;# Vector{Int64}&lt;/span&gt;

&lt;span class="c"&gt;# 'pointer(A)' is the *safe and standard* way to get a pointer for arrays.&lt;/span&gt;
&lt;span class="c"&gt;# It returns a *typed* pointer (Ptr{Int64}) pointing directly to the&lt;/span&gt;
&lt;span class="c"&gt;# *first data element* (A[1]).&lt;/span&gt;
&lt;span class="c"&gt;# This is the pointer you pass to C functions expecting 'int*'.&lt;/span&gt;
&lt;span class="c"&gt;# The GC guarantees the array's data won't move while this pointer is live&lt;/span&gt;
&lt;span class="c"&gt;# (e.g., during a ccall).&lt;/span&gt;
&lt;span class="n"&gt;ptr_A_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pointer&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Array A: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Pointer to A's data (Ptr{Int64}): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ptr_A_data&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 'pointer_from_objref(A)' points to the *Array object header* itself,&lt;/span&gt;
&lt;span class="c"&gt;# NOT the data buffer. This header contains metadata like dimensions and length.&lt;/span&gt;
&lt;span class="c"&gt;# This is generally less useful than pointer(A).&lt;/span&gt;
&lt;span class="n"&gt;ptr_A_header&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pointer_from_objref&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Pointer to A's *header* (Ptr{Nothing}): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ptr_A_header&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# --- Case 3: Immutable `isbits` Struct (Requires Boxing) ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Immutable isbits Struct ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="nc"&gt; Point&lt;/span&gt; &lt;span class="c"&gt;# isbits&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;
    &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Point&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Point p: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# !! DANGER !! Attempting pointer_from_objref directly on an isbits value 'p' is UNSAFE.&lt;/span&gt;

&lt;span class="c"&gt;# The SAFE way to get a stable pointer to an isbits value is to "box" it&lt;/span&gt;
&lt;span class="c"&gt;# using a 'Ref'. A 'Ref' is a tiny mutable container designed for this.&lt;/span&gt;
&lt;span class="n"&gt;p_boxed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Ref&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Creates a Ref{Point} object on the heap, holding 'p'.&lt;/span&gt;

&lt;span class="c"&gt;# Now we get a pointer to the *Ref object* on the heap.&lt;/span&gt;
&lt;span class="n"&gt;ptr_p_ref_obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pointer_from_objref&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p_boxed&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Boxed Point (Ref): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p_boxed&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Pointer to Ref object: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ptr_p_ref_obj&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Use 'Base.unsafe_convert' to get a pointer to the *data inside* the Ref.&lt;/span&gt;
&lt;span class="c"&gt;# This is the low-level function that ccall uses for Ref arguments.&lt;/span&gt;
&lt;span class="n"&gt;ptr_p_data_in_ref&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unsafe_convert&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Ptr&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Point&lt;/span&gt;&lt;span class="x"&gt;},&lt;/span&gt; &lt;span class="n"&gt;p_boxed&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Returns Ptr{Point}&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Pointer to Point data inside Ref: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ptr_p_data_in_ref&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# This 'ptr_p_data_in_ref' is what you'd pass to a C function expecting 'Point*'.&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script explores how to obtain raw memory pointers (&lt;code&gt;Ptr{T}&lt;/code&gt;) to Julia objects, highlighting the crucial differences between &lt;code&gt;pointer()&lt;/code&gt; for arrays and the lower-level &lt;code&gt;pointer_from_objref()&lt;/code&gt;. Understanding these is essential for unsafe memory operations and C interoperability.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;pointer(A::Array)&lt;/code&gt; - The Safe Pointer to Data
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Purpose:&lt;/strong&gt; &lt;code&gt;pointer(A)&lt;/code&gt; is the &lt;strong&gt;standard, safe, and recommended&lt;/strong&gt; way to get a pointer associated with an &lt;code&gt;Array&lt;/code&gt; (or &lt;code&gt;String&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Return Type:&lt;/strong&gt; It returns a &lt;strong&gt;typed pointer&lt;/strong&gt; (e.g., &lt;code&gt;Ptr{Int64}&lt;/code&gt; for &lt;code&gt;Vector{Int64}&lt;/code&gt;) that points directly to the &lt;strong&gt;first data element&lt;/strong&gt; (&lt;code&gt;A[1]&lt;/code&gt;) in the array's contiguous memory buffer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use Case:&lt;/strong&gt; This is the pointer you pass to C functions that expect a C-style array pointer (like &lt;code&gt;double*&lt;/code&gt; or &lt;code&gt;int*&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GC Safety:&lt;/strong&gt; Julia's Garbage Collector (GC) is aware of pointers created via &lt;code&gt;pointer(A)&lt;/code&gt;. When such a pointer is passed to &lt;code&gt;ccall&lt;/code&gt;, the GC &lt;strong&gt;guarantees&lt;/strong&gt; that the underlying array &lt;code&gt;A&lt;/code&gt; will not be moved or garbage collected while the C function is executing ("pinning"). This prevents memory corruption.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;pointer_from_objref(obj)&lt;/code&gt; - The Unsafe Pointer to Object
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Purpose:&lt;/strong&gt; &lt;code&gt;pointer_from_objref(obj)&lt;/code&gt; is a lower-level, generally &lt;strong&gt;unsafe&lt;/strong&gt; function. It provides a raw pointer to the &lt;strong&gt;beginning of the Julia object &lt;code&gt;obj&lt;/code&gt; itself&lt;/strong&gt; in memory.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Return Type:&lt;/strong&gt; It returns an &lt;strong&gt;untyped pointer&lt;/strong&gt;, &lt;code&gt;Ptr{Nothing}&lt;/code&gt; (equivalent to C's &lt;code&gt;void*&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Behavior:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;For &lt;strong&gt;heap-allocated objects&lt;/strong&gt; (like &lt;code&gt;mutable struct&lt;/code&gt; &lt;code&gt;d&lt;/code&gt;), it returns the address of the object's block on the heap.&lt;/li&gt;
&lt;li&gt;For &lt;strong&gt;Arrays&lt;/strong&gt; (like &lt;code&gt;A&lt;/code&gt;), it returns the address of the &lt;strong&gt;array header object&lt;/strong&gt;, which contains metadata like dimensions and length, &lt;strong&gt;not&lt;/strong&gt; the address of the data buffer returned by &lt;code&gt;pointer(A)&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;GC Safety Warning:&lt;/strong&gt; The GC &lt;em&gt;does&lt;/em&gt; know about the object &lt;code&gt;obj&lt;/code&gt; itself, but it provides &lt;strong&gt;no guarantees&lt;/strong&gt; about the object's location &lt;em&gt;unless&lt;/em&gt; you are careful. If you simply store &lt;code&gt;ptr = pointer_from_objref(obj)&lt;/code&gt; in a variable, the GC might later move the object &lt;code&gt;obj&lt;/code&gt; during compaction, leaving &lt;code&gt;ptr&lt;/code&gt; dangling (pointing to invalid memory). It's generally only safe to use this pointer immediately, for example, within a &lt;code&gt;ccall&lt;/code&gt; where the object reference itself keeps the object rooted.&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Handling &lt;code&gt;isbits&lt;/code&gt; Values (Boxing with &lt;code&gt;Ref&lt;/code&gt;)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Danger:&lt;/strong&gt; You &lt;strong&gt;cannot&lt;/strong&gt; safely use &lt;code&gt;pointer_from_objref&lt;/code&gt; directly on an &lt;strong&gt;&lt;code&gt;isbits&lt;/code&gt; value&lt;/strong&gt; (like an &lt;code&gt;Int&lt;/code&gt;, &lt;code&gt;Float64&lt;/code&gt;, or an immutable &lt;code&gt;isbits struct&lt;/code&gt; like &lt;code&gt;Point&lt;/code&gt;). These values often live on the &lt;strong&gt;stack&lt;/strong&gt; or even just in &lt;strong&gt;CPU registers&lt;/strong&gt;. They don't necessarily have a stable memory address tracked by the GC.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Solution: Boxing with &lt;code&gt;Ref&lt;/code&gt;:&lt;/strong&gt; To get a stable, GC-tracked pointer to an &lt;code&gt;isbits&lt;/code&gt; value (e.g., to pass its address to a C function expecting &lt;code&gt;Point*&lt;/code&gt;), you must &lt;strong&gt;"box"&lt;/strong&gt; it using &lt;code&gt;Ref(value)&lt;/code&gt;.

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Ref(p)&lt;/code&gt; creates a small, &lt;strong&gt;mutable&lt;/strong&gt;, &lt;strong&gt;heap-allocated&lt;/strong&gt; container object (&lt;code&gt;Ref{Point}&lt;/code&gt;) that holds the &lt;code&gt;isbits&lt;/code&gt; value &lt;code&gt;p&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Base.unsafe_convert(Ptr{T}, ref)&lt;/code&gt;:&lt;/strong&gt; This is the low-level function (used internally by &lt;code&gt;ccall&lt;/code&gt;) to get a &lt;strong&gt;typed pointer&lt;/strong&gt; (&lt;code&gt;Ptr{Point}&lt;/code&gt; in this case) to the data &lt;em&gt;stored inside&lt;/em&gt; the &lt;code&gt;Ref&lt;/code&gt; object. This pointer &lt;em&gt;is&lt;/em&gt; GC-safe while the &lt;code&gt;Ref&lt;/code&gt; object exists and is suitable for passing to C functions expecting a pointer to the struct.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pointer_from_objref(p_boxed)&lt;/code&gt; still gives you the pointer to the &lt;code&gt;Ref&lt;/code&gt; object itself, which is usually less useful for C interop than the pointer to the contained data.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Understanding when and how to obtain pointers safely is paramount when working at the boundary between Julia's managed memory and raw memory access.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Base Documentation, &lt;code&gt;pointer&lt;/code&gt;:&lt;/strong&gt; "Get the native address of an array or string element." Mentions GC safety during &lt;code&gt;ccall&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Base Documentation, &lt;code&gt;pointer_from_objref&lt;/code&gt;:&lt;/strong&gt; "Get the memory address of a Julia object as a &lt;code&gt;Ptr&lt;/code&gt;." Explicitly warns about GC interaction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Base Documentation, &lt;code&gt;Ref&lt;/code&gt;:&lt;/strong&gt; Describes &lt;code&gt;Ref&lt;/code&gt; as a container often used for C interop involving pointers to values.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Base Documentation, &lt;code&gt;Base.unsafe_convert&lt;/code&gt;:&lt;/strong&gt; "Convert &lt;code&gt;x&lt;/code&gt; to a value of type &lt;code&gt;T&lt;/code&gt;... In cases where &lt;code&gt;x&lt;/code&gt; is already of type &lt;code&gt;T&lt;/code&gt;, should return &lt;code&gt;x&lt;/code&gt;." Crucially used for converting &lt;code&gt;Ref{T}&lt;/code&gt; to &lt;code&gt;Ptr{T}&lt;/code&gt; for &lt;code&gt;ccall&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0091_pointer_from_objref.jl
&lt;span class="nt"&gt;---&lt;/span&gt; Mutable Struct &lt;span class="nt"&gt;---&lt;/span&gt;
Object d: MyData&lt;span class="o"&gt;(&lt;/span&gt;100&lt;span class="o"&gt;)&lt;/span&gt;
Pointer to d object &lt;span class="o"&gt;(&lt;/span&gt;Ptr&lt;span class="o"&gt;{&lt;/span&gt;Nothing&lt;span class="o"&gt;})&lt;/span&gt;: Ptr&lt;span class="o"&gt;{&lt;/span&gt;Nothing&lt;span class="o"&gt;}(&lt;/span&gt;0x...&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nt"&gt;---&lt;/span&gt; Array &lt;span class="nt"&gt;---&lt;/span&gt;
Array A: &lt;span class="o"&gt;[&lt;/span&gt;10, 20, 30]
Pointer to A&lt;span class="s1"&gt;'s data (Ptr{Int64}): Ptr{Int64}(0x...)
Pointer to A'&lt;/span&gt;s &lt;span class="k"&gt;*&lt;/span&gt;header&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;Ptr&lt;span class="o"&gt;{&lt;/span&gt;Nothing&lt;span class="o"&gt;})&lt;/span&gt;: Ptr&lt;span class="o"&gt;{&lt;/span&gt;Nothing&lt;span class="o"&gt;}(&lt;/span&gt;0x...&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nt"&gt;---&lt;/span&gt; Immutable isbits Struct &lt;span class="nt"&gt;---&lt;/span&gt;
Point p: Point&lt;span class="o"&gt;(&lt;/span&gt;1.0, 2.0&lt;span class="o"&gt;)&lt;/span&gt;
Boxed Point &lt;span class="o"&gt;(&lt;/span&gt;Ref&lt;span class="o"&gt;)&lt;/span&gt;: Base.RefValue&lt;span class="o"&gt;{&lt;/span&gt;Point&lt;span class="o"&gt;}(&lt;/span&gt;Point&lt;span class="o"&gt;(&lt;/span&gt;1.0, 2.0&lt;span class="o"&gt;))&lt;/span&gt;
Pointer to Ref object: Ptr&lt;span class="o"&gt;{&lt;/span&gt;Nothing&lt;span class="o"&gt;}(&lt;/span&gt;0x...&lt;span class="o"&gt;)&lt;/span&gt;
Pointer to Point data inside Ref: Ptr&lt;span class="o"&gt;{&lt;/span&gt;Point&lt;span class="o"&gt;}(&lt;/span&gt;0x...&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(Memory addresses (&lt;code&gt;0x...&lt;/code&gt;) will vary.)&lt;/em&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;0092_unsafe_load_store.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0092_unsafe_load_store.jl&lt;/span&gt;
&lt;span class="c"&gt;# Demonstrates reading from and writing to raw pointers.&lt;/span&gt;

&lt;span class="c"&gt;# 1. Get a pointer to array data (our raw memory block)&lt;/span&gt;
&lt;span class="n"&gt;A&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt; &lt;span class="c"&gt;# Vector{Int64}&lt;/span&gt;
&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pointer&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;       &lt;span class="c"&gt;# p::Ptr{Int64}, points to A[1]&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Original array: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Pointer p (points to A[1]): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Element size: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sizeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;eltype&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="x"&gt;)),&lt;/span&gt; &lt;span class="s"&gt;" bytes"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# 8 bytes for Int64&lt;/span&gt;

&lt;span class="c"&gt;# --- Reading from Pointers: unsafe_load ---&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Reading using unsafe_load ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 2. unsafe_load(pointer, [index=1])&lt;/span&gt;
&lt;span class="c"&gt;#    Reads the value of the pointer's element type from memory.&lt;/span&gt;
&lt;span class="c"&gt;#    The index is 1-based and refers to *elements*, not bytes.&lt;/span&gt;
&lt;span class="n"&gt;val1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;unsafe_load&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;    &lt;span class="c"&gt;# Reads the 1st Int64 (at byte offset 0)&lt;/span&gt;
&lt;span class="n"&gt;val2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;unsafe_load&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Reads the 2nd Int64 (at byte offset 8)&lt;/span&gt;
&lt;span class="n"&gt;val3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;unsafe_load&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Reads the 3rd Int64 (at byte offset 16)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Value at index 1 (offset 0): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;val1&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# 10&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Value at index 2 (offset 8): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;val2&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# 20&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Value at index 3 (offset 16):"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;val3&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# 30&lt;/span&gt;

&lt;span class="c"&gt;# --- Writing to Pointers: unsafe_store! ---&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Writing using unsafe_store! ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 3. unsafe_store!(pointer, value, [index=1])&lt;/span&gt;
&lt;span class="c"&gt;#    Writes 'value' to the memory location for the specified element index.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Storing 999 at index 4 (offset 24)..."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;unsafe_store!&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;999&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Writes 999 to A[4]'s location&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Array after unsafe_store!: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# [10, 20, 30, 999]&lt;/span&gt;

&lt;span class="c"&gt;# --- Pointer Arithmetic (Alternative Access) ---&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Pointer Arithmetic (C-style) ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 4. Manually add byte offsets to the pointer.&lt;/span&gt;
&lt;span class="c"&gt;#    'p + N' adds N *bytes* to the address.&lt;/span&gt;
&lt;span class="n"&gt;p_plus_8_bytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;sizeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Int64&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;   &lt;span class="c"&gt;# Pointer to the 2nd element&lt;/span&gt;
&lt;span class="n"&gt;p_plus_16_bytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;sizeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Int64&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Pointer to the 3rd element&lt;/span&gt;

&lt;span class="c"&gt;# Load using the offset pointer (index defaults to 1 for the *new* pointer)&lt;/span&gt;
&lt;span class="n"&gt;val2_arith&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;unsafe_load&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p_plus_8_bytes&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;val3_arith&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;unsafe_load&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p_plus_16_bytes&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Value at p + 8 bytes:  "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;val2_arith&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# 20&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Value at p + 16 bytes: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;val3_arith&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# 30&lt;/span&gt;

&lt;span class="c"&gt;# --- DANGER: No Bounds Checking ---&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- DANGER: No Bounds Checking ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 5. Unsafe operations DO NOT check array bounds.&lt;/span&gt;
&lt;span class="c"&gt;#    Writing past the end corrupts memory.&lt;/span&gt;
&lt;span class="n"&gt;out_of_bounds_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Attempting unsafe_store! at index &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;out_of_bounds_index (out of bounds)..."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;unsafe_store!&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;out_of_bounds_index&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"...Memory potentially corrupted (no crash this time)."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# Reading might read garbage or crash&lt;/span&gt;
    &lt;span class="c"&gt;# garbage = unsafe_load(p, out_of_bounds_index)&lt;/span&gt;
    &lt;span class="c"&gt;# println("Read garbage: ", garbage)&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="c"&gt;# A crash (segfault) might happen here, or later, or never.&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Caught error (lucky if it happens immediately): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# Reset the value we overwrote if no crash&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;999&lt;/span&gt;
    &lt;span class="n"&gt;unsafe_store!&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Restore original value for consistency if needed&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Array after potential out-of-bounds write attempt: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script demonstrates the fundamental &lt;strong&gt;unsafe&lt;/strong&gt; operations for reading (&lt;code&gt;unsafe_load&lt;/code&gt;) and writing (&lt;code&gt;unsafe_store!&lt;/code&gt;) directly to memory addresses specified by pointers (&lt;code&gt;Ptr{T}&lt;/code&gt;). These functions are the Julia equivalents of C's pointer dereferencing (&lt;code&gt;*ptr&lt;/code&gt;) and assignment (&lt;code&gt;*ptr = value&lt;/code&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Core Concepts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;unsafe_load(pointer::Ptr{T}, [index::Integer=1])&lt;/code&gt;:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Reads the binary data from the memory address &lt;code&gt;pointer + (index-1)*sizeof(T)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Interprets those bytes as a value of type &lt;code&gt;T&lt;/code&gt; (the element type of the pointer).&lt;/li&gt;
&lt;li&gt;Returns the value of type &lt;code&gt;T&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;1-Based Indexing:&lt;/strong&gt; The optional &lt;code&gt;index&lt;/code&gt; argument is &lt;strong&gt;1-based&lt;/strong&gt; and refers to the &lt;em&gt;element number&lt;/em&gt;, not the byte offset. &lt;code&gt;unsafe_load(p, 2)&lt;/code&gt; automatically calculates the correct byte offset to read the second &lt;code&gt;Int64&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;&lt;code&gt;unsafe_store!(pointer::Ptr{T}, value, [index::Integer=1])&lt;/code&gt;:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Writes the binary representation of &lt;code&gt;value&lt;/code&gt; to the memory address &lt;code&gt;pointer + (index-1)*sizeof(T)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;value&lt;/code&gt; should typically be convertible to type &lt;code&gt;T&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;!&lt;/code&gt; suffix indicates that this function modifies memory (the location pointed to).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Pointer Arithmetic:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;You can manually perform C-style pointer arithmetic by adding &lt;strong&gt;byte offsets&lt;/strong&gt; to a pointer. &lt;code&gt;p + sizeof(Int64)&lt;/code&gt; creates a &lt;em&gt;new&lt;/em&gt; pointer address that is 8 bytes after &lt;code&gt;p&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;When calling &lt;code&gt;unsafe_load&lt;/code&gt; or &lt;code&gt;unsafe_store!&lt;/code&gt; on such an offset pointer, the default index &lt;code&gt;1&lt;/code&gt; refers to the &lt;em&gt;start&lt;/em&gt; of that new address. &lt;code&gt;unsafe_load(p + sizeof(Int64))&lt;/code&gt; is equivalent to &lt;code&gt;unsafe_load(p, 2)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;While possible, using the 1-based index argument is generally less error-prone than manual byte arithmetic.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  The &lt;code&gt;unsafe_&lt;/code&gt; Warning: No Safety Net
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No Bounds Checking:&lt;/strong&gt; This is the most critical danger. &lt;code&gt;unsafe_load&lt;/code&gt; and &lt;code&gt;unsafe_store!&lt;/code&gt; perform &lt;strong&gt;zero bounds checking&lt;/strong&gt;. They operate directly on memory addresses. If you provide an index (or calculate a byte offset) that points outside the allocated memory block for your object (like &lt;code&gt;A&lt;/code&gt;), these functions will still attempt to read or write there.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Undefined Behavior:&lt;/strong&gt; Accessing memory out of bounds leads to &lt;strong&gt;undefined behavior&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;It might crash immediately with a segmentation fault.&lt;/li&gt;
&lt;li&gt;It might silently read garbage data.&lt;/li&gt;
&lt;li&gt;It might silently &lt;strong&gt;corrupt&lt;/strong&gt; unrelated data or program state, leading to bizarre errors much later in execution.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Responsibility:&lt;/strong&gt; When using &lt;code&gt;unsafe_&lt;/code&gt; functions, &lt;strong&gt;you&lt;/strong&gt;, the programmer, are solely responsible for ensuring that all memory accesses are within the valid bounds of the object being pointed to.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;These functions are essential building blocks for performance-critical code that interacts directly with memory buffers (e.g., from network I/O, C libraries, or custom data structures), but they must be used with extreme caution and careful bounds management.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Base Documentation, &lt;code&gt;unsafe_load&lt;/code&gt;:&lt;/strong&gt; "Load a value of type &lt;code&gt;T&lt;/code&gt; from the address indicated by pointer &lt;code&gt;p&lt;/code&gt;..."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Base Documentation, &lt;code&gt;unsafe_store!&lt;/code&gt;:&lt;/strong&gt; "Store a value of type &lt;code&gt;T&lt;/code&gt; to the address indicated by pointer &lt;code&gt;p&lt;/code&gt;..."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Metaprogramming" (Pointer Arithmetic):&lt;/strong&gt; Briefly mentions pointer arithmetic with byte offsets.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0092_unsafe_load_store.jl
Original array: &lt;span class="o"&gt;[&lt;/span&gt;10, 20, 30, 40]
Pointer p &lt;span class="o"&gt;(&lt;/span&gt;points to A[1]&lt;span class="o"&gt;)&lt;/span&gt;: Ptr&lt;span class="o"&gt;{&lt;/span&gt;Int64&lt;span class="o"&gt;}(&lt;/span&gt;0x...&lt;span class="o"&gt;)&lt;/span&gt;
Element size: 8 bytes

&lt;span class="nt"&gt;---&lt;/span&gt; Reading using unsafe_load &lt;span class="nt"&gt;---&lt;/span&gt;
Value at index 1 &lt;span class="o"&gt;(&lt;/span&gt;offset 0&lt;span class="o"&gt;)&lt;/span&gt;: 10
Value at index 2 &lt;span class="o"&gt;(&lt;/span&gt;offset 8&lt;span class="o"&gt;)&lt;/span&gt;: 20
Value at index 3 &lt;span class="o"&gt;(&lt;/span&gt;offset 16&lt;span class="o"&gt;)&lt;/span&gt;: 30

&lt;span class="nt"&gt;---&lt;/span&gt; Writing using unsafe_store! &lt;span class="nt"&gt;---&lt;/span&gt;
Storing 999 at index 4 &lt;span class="o"&gt;(&lt;/span&gt;offset 24&lt;span class="o"&gt;)&lt;/span&gt;...
Array after unsafe_store!: &lt;span class="o"&gt;[&lt;/span&gt;10, 20, 30, 999]

&lt;span class="nt"&gt;---&lt;/span&gt; Pointer Arithmetic &lt;span class="o"&gt;(&lt;/span&gt;C-style&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
Value at p + 8 bytes: 20
Value at p + 16 bytes: 30
Attempting unsafe_store! at index 100 &lt;span class="o"&gt;(&lt;/span&gt;out of bounds&lt;span class="o"&gt;)&lt;/span&gt;...
...Memory potentially corrupted &lt;span class="o"&gt;(&lt;/span&gt;no crash this &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Read garbage: &lt;span class="nt"&gt;-1&lt;/span&gt;
Array after potential out-of-bounds write attempt: &lt;span class="o"&gt;[&lt;/span&gt;10, 20, 30, 40]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(Memory addresses will vary. Whether the out-of-bounds write actually crashes is system-dependent.)&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Zero Copy Views And Conversions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;0093_unsafe_wrap.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0093_unsafe_wrap.jl&lt;/span&gt;
&lt;span class="c"&gt;# Creates a Julia Array view over a raw pointer (zero-copy).&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Libc&lt;/span&gt; &lt;span class="c"&gt;# For malloc/free examples&lt;/span&gt;

&lt;span class="c"&gt;# --- Case 1: Wrapping Memory Managed by Julia ---&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Wrapping a Julia Array's Pointer (Borrowing) ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 1. Get a pointer to existing, GC-managed memory.&lt;/span&gt;
&lt;span class="n"&gt;julia_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Float64&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;1.1&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;2.2&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;3.3&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;4.4&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;5.5&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;ptr_julia&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pointer&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;julia_data&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;num_elements&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;julia_data&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 2. Use 'unsafe_wrap' to create an Array VIEW.&lt;/span&gt;
&lt;span class="c"&gt;#    Syntax: unsafe_wrap(Array, pointer::Ptr{T}, dims; own = false)&lt;/span&gt;
&lt;span class="c"&gt;#    'dims' can be an integer (for Vector) or a tuple (for multi-dim).&lt;/span&gt;
&lt;span class="c"&gt;#    'own = false' (default) means Julia does NOT own/manage this memory.&lt;/span&gt;
&lt;span class="n"&gt;wrapped_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;unsafe_wrap&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Array&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ptr_julia&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;num_elements&lt;/span&gt;&lt;span class="x"&gt;;&lt;/span&gt; &lt;span class="n"&gt;own&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Original Julia data: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;julia_data&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Wrapped array view:  "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;wrapped_array&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Type of wrapped array: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wrapped_array&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# Vector{Float64}&lt;/span&gt;

&lt;span class="c"&gt;# 3. Modifications through the view AFFECT the original data.&lt;/span&gt;
&lt;span class="c"&gt;#    They share the same underlying memory. No copy was made.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Modifying wrapped_array[1] = 99.9"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;wrapped_array&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;99.9&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Wrapped array view is now: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;wrapped_array&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Original Julia data is now: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;julia_data&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Also changed!&lt;/span&gt;

&lt;span class="c"&gt;# --- Case 2: Wrapping Memory Allocated Outside Julia (e.g., C) ---&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Wrapping Externally Allocated Memory (Taking Ownership) ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 4. Allocate memory using C's malloc (via Libc).&lt;/span&gt;
&lt;span class="c"&gt;#    This memory is NOT tracked by Julia's GC initially.&lt;/span&gt;
&lt;span class="n"&gt;bytes_to_alloc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;sizeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Int64&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ptr_malloc_void&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Libc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;malloc&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes_to_alloc&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ptr_malloc_void&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;C_NULL&lt;/span&gt;
    &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"malloc failed"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="c"&gt;# Cast the void* to a typed pointer&lt;/span&gt;
&lt;span class="n"&gt;ptr_malloc_int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;convert&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Ptr&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;Int64&lt;/span&gt;&lt;span class="x"&gt;},&lt;/span&gt; &lt;span class="n"&gt;ptr_malloc_void&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Allocated external memory at: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ptr_malloc_int&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 5. Wrap the C memory, passing 'own = true'.&lt;/span&gt;
&lt;span class="c"&gt;#    'own = true' tells Julia's GC to take ownership of this pointer&lt;/span&gt;
&lt;span class="c"&gt;#    and call 'Libc.free()' on it when the wrapped array is finalized.&lt;/span&gt;
&lt;span class="n"&gt;owned_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;unsafe_wrap&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Array&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ptr_malloc_int&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="x"&gt;;&lt;/span&gt; &lt;span class="n"&gt;own&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 6. Initialize and use the array.&lt;/span&gt;
&lt;span class="n"&gt;owned_array&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;
&lt;span class="n"&gt;owned_array&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;
&lt;span class="n"&gt;owned_array&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Owned wrapped array: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;owned_array&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 7. IMPORTANT: We do NOT manually call Libc.free(ptr_malloc_void).&lt;/span&gt;
&lt;span class="c"&gt;#    The GC will handle it because we passed 'own = true'.&lt;/span&gt;
&lt;span class="c"&gt;#    Manually freeing would cause a double-free crash later.&lt;/span&gt;

&lt;span class="c"&gt;# --- DANGER: Using 'own=true' on Julia Memory ---&lt;/span&gt;

&lt;span class="c"&gt;# 8. NEVER use 'own=true' when wrapping memory from another Julia object.&lt;/span&gt;
&lt;span class="c"&gt;# ptr_julia_bad = pointer(julia_data)&lt;/span&gt;
&lt;span class="c"&gt;# WRONG: owned_bad = unsafe_wrap(Array, ptr_julia_bad, num_elements; own = true)&lt;/span&gt;
&lt;span class="c"&gt;# This would tell the GC to 'free()' the memory managed by 'julia_data',&lt;/span&gt;
&lt;span class="c"&gt;# leading to heap corruption and likely crashes.&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Finished unsafe_wrap examples."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces &lt;code&gt;unsafe_wrap(Array, ...)&lt;/code&gt;, a powerful function for creating a Julia &lt;code&gt;Array&lt;/code&gt; object that acts as a &lt;strong&gt;zero-copy view&lt;/strong&gt; onto a raw block of memory specified by a pointer. This is fundamental for high-performance interoperability with C libraries or for working directly with memory buffers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Core Concept: Zero-Copy View
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;unsafe_wrap(Array, pointer::Ptr{T}, dims; own=false)&lt;/code&gt; constructs a standard Julia &lt;code&gt;Array&lt;/code&gt; (e.g., &lt;code&gt;Vector{T}&lt;/code&gt; or &lt;code&gt;Matrix{T}&lt;/code&gt;) whose underlying data &lt;em&gt;is&lt;/em&gt; the memory block starting at &lt;code&gt;pointer&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No Data Copy:&lt;/strong&gt; Absolutely no data is copied during this operation. The created array directly uses the memory pointed to by &lt;code&gt;pointer&lt;/code&gt;. This makes it extremely fast.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shared Memory:&lt;/strong&gt; As demonstrated in Case 1, modifications made through the &lt;code&gt;wrapped_array&lt;/code&gt; are instantly reflected in the original &lt;code&gt;julia_data&lt;/code&gt; because they operate on the exact same memory locations.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The &lt;code&gt;own&lt;/code&gt; Parameter: Managing Memory Ownership
&lt;/h2&gt;

&lt;p&gt;This boolean keyword argument is &lt;strong&gt;critically important&lt;/strong&gt; for memory safety:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;own = false&lt;/code&gt; (Default - "Borrowing"):&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Use this when the memory pointed to by &lt;code&gt;pointer&lt;/code&gt; is &lt;strong&gt;managed elsewhere&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Examples:

&lt;ul&gt;
&lt;li&gt;Wrapping a pointer obtained from another Julia object (like &lt;code&gt;pointer(julia_data)&lt;/code&gt;). The Julia GC owns &lt;code&gt;julia_data&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Wrapping a pointer returned by a C library where the C library &lt;em&gt;retains ownership&lt;/em&gt; and will free the memory later.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;You are telling Julia's GC: "Do &lt;strong&gt;not&lt;/strong&gt; try to &lt;code&gt;free&lt;/code&gt; this memory when the wrapped array goes out of scope."&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;&lt;code&gt;own = true&lt;/code&gt; (Taking Ownership):&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Use this &lt;strong&gt;only&lt;/strong&gt; when the memory pointed to by &lt;code&gt;pointer&lt;/code&gt; was allocated using a mechanism like C's &lt;code&gt;malloc&lt;/code&gt; (or &lt;code&gt;Libc.malloc&lt;/code&gt;), and you want to &lt;strong&gt;transfer ownership&lt;/strong&gt; of that memory block to the Julia GC.&lt;/li&gt;
&lt;li&gt;You are telling Julia's GC: "When this wrapped array object is finalized (no longer reachable), you &lt;strong&gt;must call &lt;code&gt;Libc.free()&lt;/code&gt;&lt;/strong&gt; on the original &lt;code&gt;pointer&lt;/code&gt; to release the memory."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CRITICAL DANGER:&lt;/strong&gt; Never use &lt;code&gt;own = true&lt;/code&gt; on a pointer obtained from another Julia object (like &lt;code&gt;pointer(A)&lt;/code&gt;). This will cause the GC to incorrectly &lt;code&gt;free&lt;/code&gt; memory it doesn't own, leading to &lt;strong&gt;heap corruption&lt;/strong&gt; and crashes (double-free).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Use Cases
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;C Interoperability (HFT):&lt;/strong&gt; When a C library (e.g., a market data feed handler) gives you a &lt;code&gt;Ptr{OrderUpdate}&lt;/code&gt; pointing to a large buffer of updates, you use &lt;code&gt;unsafe_wrap(Array, ptr, num_updates; own=false)&lt;/code&gt; to instantly get a &lt;code&gt;Vector{OrderUpdate}&lt;/code&gt; (assuming &lt;code&gt;OrderUpdate&lt;/code&gt; is an &lt;code&gt;isbits struct&lt;/code&gt; with matching layout) without any copying. You can then process this vector using fast, idiomatic Julia code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory-Mapped Files:&lt;/strong&gt; Wrapping pointers obtained from memory-mapping large files allows processing huge datasets that don't fit in RAM as if they were regular Julia arrays.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shared Memory:&lt;/strong&gt; Working with pointers to shared memory segments used for inter-process communication.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;unsafe_wrap&lt;/code&gt; provides the crucial link between Julia's high-level array interface and low-level memory buffers, enabling maximum performance in data-intensive scenarios. However, misuse of the &lt;code&gt;own&lt;/code&gt; parameter is a common source of serious memory errors.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Base Documentation, &lt;code&gt;unsafe_wrap&lt;/code&gt;:&lt;/strong&gt; "Wrap a pointer &lt;code&gt;p&lt;/code&gt; to an array of element type &lt;code&gt;T&lt;/code&gt;..." Explains arguments including &lt;code&gt;own&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Base Documentation, &lt;code&gt;Libc.malloc&lt;/code&gt;, &lt;code&gt;Libc.free&lt;/code&gt;:&lt;/strong&gt; Functions for interacting with the C standard library's memory allocation.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0093_unsafe_wrap.jl
&lt;span class="nt"&gt;---&lt;/span&gt; Wrapping a Julia Array&lt;span class="s1"&gt;'s Pointer (Borrowing) ---
Original Julia data: [1.1, 2.2, 3.3, 4.4, 5.5]
Wrapped array view:  [1.1, 2.2, 3.3, 4.4, 5.5]
Type of wrapped array: Vector{Float64} (alias for Array{Float64, 1})

Modifying wrapped_array[1] = 99.9
Wrapped array view is now: [99.9, 2.2, 3.3, 4.4, 5.5]
Original Julia data is now: [99.9, 2.2, 3.3, 4.4, 5.5]

--- Wrapping Externally Allocated Memory (Taking Ownership) ---
Allocated external memory at: Ptr{Int64}(0x...)
Owned wrapped array: [1000, 2000, 3000]

Finished unsafe_wrap examples.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(Memory addresses will vary.)&lt;/em&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;0094_unsafe_string.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0094_unsafe_string.jl&lt;/span&gt;
&lt;span class="c"&gt;# Creates a Julia String by COPYING data from a raw pointer.&lt;/span&gt;

&lt;span class="c"&gt;# --- Case 1: Null-Terminated C String ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Creating String from Null-Terminated Pointer ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 1. Simulate a C string: Vector{UInt8} ending with 0x00.&lt;/span&gt;
&lt;span class="c"&gt;#    This data is managed by Julia's GC.&lt;/span&gt;
&lt;span class="n"&gt;c_string_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;UInt8&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="sc"&gt;'H'&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="sc"&gt;'e'&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="sc"&gt;'l'&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="sc"&gt;'l'&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="sc"&gt;'o'&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="sc"&gt;'\0'&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt; &lt;span class="c"&gt;# '\0' is the null terminator&lt;/span&gt;
&lt;span class="n"&gt;ptr_null&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pointer&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c_string_data&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Gets a Ptr{UInt8}&lt;/span&gt;

&lt;span class="c"&gt;# 2. Use 'unsafe_string(pointer)'&lt;/span&gt;
&lt;span class="c"&gt;#    This function reads bytes starting at 'ptr_null' and *copies* them&lt;/span&gt;
&lt;span class="c"&gt;#    into a NEW, heap-allocated Julia String.&lt;/span&gt;
&lt;span class="c"&gt;#    It stops copying when it encounters the first null byte (0x00).&lt;/span&gt;
&lt;span class="c"&gt;#    The null byte itself is NOT included in the Julia String.&lt;/span&gt;
&lt;span class="n"&gt;julia_string_from_null&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;unsafe_string&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ptr_null&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Original C data (bytes): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c_string_data&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Julia string (from null): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;repr&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;julia_string_from_null&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# Use repr to see quotes&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Type: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;julia_string_from_null&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Length: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;julia_string_from_null&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# Length is 5, excludes null&lt;/span&gt;

&lt;span class="c"&gt;# --- Case 2: Pointer to Data with Known Length ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Creating String from Pointer + Length ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 3. Simulate a buffer without a null terminator (e.g., from network).&lt;/span&gt;
&lt;span class="n"&gt;c_buffer_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;UInt8&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="sc"&gt;'W'&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="sc"&gt;'o'&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="sc"&gt;'r'&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="sc"&gt;'l'&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="sc"&gt;'d'&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;ptr_len&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pointer&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c_buffer_data&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;buffer_length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c_buffer_data&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# 5&lt;/span&gt;

&lt;span class="c"&gt;# 4. Use 'unsafe_string(pointer, length)'&lt;/span&gt;
&lt;span class="c"&gt;#    This function reads *exactly* 'length' bytes starting at 'ptr_len'&lt;/span&gt;
&lt;span class="c"&gt;#    and *copies* them into a NEW Julia String.&lt;/span&gt;
&lt;span class="c"&gt;#    It does NOT look for a null terminator.&lt;/span&gt;
&lt;span class="n"&gt;julia_string_from_len&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;unsafe_string&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ptr_len&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;buffer_length&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Original C buffer (bytes): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c_buffer_data&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Julia string (from length): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;repr&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;julia_string_from_len&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Type: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;julia_string_from_len&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Length: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;julia_string_from_len&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# Length is 5&lt;/span&gt;

&lt;span class="c"&gt;# --- Demonstrating the Copy ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Demonstrating the Copy (vs. unsafe_wrap) ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 5. Modify the original C data *after* creating the Julia string.&lt;/span&gt;
&lt;span class="n"&gt;c_string_data&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;UInt8&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;'J'&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Change 'H' to 'J'&lt;/span&gt;

&lt;span class="c"&gt;# 6. The Julia string remains UNCHANGED because it's a copy.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Original C data modified: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c_string_data&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Julia string (from null) is unchanged: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;repr&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;julia_string_from_null&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# Still "Hello"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces &lt;code&gt;unsafe_string()&lt;/code&gt;, the standard function for creating a Julia &lt;code&gt;String&lt;/code&gt; object from a raw pointer (&lt;code&gt;Ptr{UInt8}&lt;/code&gt;), typically obtained from C code. Crucially, unlike &lt;code&gt;unsafe_wrap&lt;/code&gt; for arrays, &lt;code&gt;unsafe_string&lt;/code&gt; &lt;strong&gt;always copies&lt;/strong&gt; the data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Core Concept: Copying Bytes into a &lt;code&gt;String&lt;/code&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;unsafe_string(pointer::Ptr{UInt8})&lt;/code&gt;:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Purpose:&lt;/strong&gt; Converts a &lt;strong&gt;null-terminated&lt;/strong&gt; C-style string (&lt;code&gt;char*&lt;/code&gt;) into a Julia &lt;code&gt;String&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Behavior:&lt;/strong&gt; It starts reading bytes from the memory address &lt;code&gt;pointer&lt;/code&gt;. It &lt;strong&gt;copies&lt;/strong&gt; each byte into a newly allocated Julia &lt;code&gt;String&lt;/code&gt; object until it encounters the &lt;strong&gt;first null byte (&lt;code&gt;0x00&lt;/code&gt;)&lt;/strong&gt;. The null byte itself is &lt;strong&gt;not included&lt;/strong&gt; in the resulting &lt;code&gt;String&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use Case:&lt;/strong&gt; This is the primary function for handling strings returned by C functions that follow the null-termination convention.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;&lt;code&gt;unsafe_string(pointer::Ptr{UInt8}, length::Integer)&lt;/code&gt;:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Purpose:&lt;/strong&gt; Converts a sequence of bytes of a &lt;strong&gt;known length&lt;/strong&gt; (which might &lt;strong&gt;not&lt;/strong&gt; be null-terminated) into a Julia &lt;code&gt;String&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Behavior:&lt;/strong&gt; It reads exactly &lt;code&gt;length&lt;/code&gt; bytes starting from &lt;code&gt;pointer&lt;/code&gt; and &lt;strong&gt;copies&lt;/strong&gt; them into a newly allocated Julia &lt;code&gt;String&lt;/code&gt;. It does &lt;strong&gt;not&lt;/strong&gt; look for, require, or stop at null bytes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use Case:&lt;/strong&gt; Essential for handling data from sources where the length is provided separately, such as network packets, fixed-width fields in binary files, or C APIs that return a &lt;code&gt;char*&lt;/code&gt; and a &lt;code&gt;size_t&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why &lt;code&gt;unsafe_string&lt;/code&gt; &lt;em&gt;Copies&lt;/em&gt; (Unlike &lt;code&gt;unsafe_wrap&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;This copying behavior is deliberate and important for safety and correctness, distinguishing it fundamentally from &lt;code&gt;unsafe_wrap(Array, ...)&lt;/code&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Immutability:&lt;/strong&gt; Julia &lt;code&gt;String&lt;/code&gt;s are &lt;strong&gt;immutable&lt;/strong&gt;. Once created, their content cannot be changed. If &lt;code&gt;unsafe_string&lt;/code&gt; created a &lt;em&gt;view&lt;/em&gt; (like &lt;code&gt;unsafe_wrap&lt;/code&gt;), modifying the original C buffer later would violate the Julia &lt;code&gt;String&lt;/code&gt;'s immutability guarantee. By copying, the Julia &lt;code&gt;String&lt;/code&gt; becomes independent of the original C memory. (The script demonstrates this: changing &lt;code&gt;c_string_data&lt;/code&gt; does &lt;em&gt;not&lt;/em&gt; affect &lt;code&gt;julia_string_from_null&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Ownership &amp;amp; GC:&lt;/strong&gt; The copied data is stored in a new &lt;code&gt;String&lt;/code&gt; object managed by Julia's Garbage Collector (GC). The GC knows how to track and eventually free this memory. The original C pointer might point to memory managed by C (e.g., &lt;code&gt;malloc&lt;/code&gt;/&lt;code&gt;free&lt;/code&gt;) or temporary stack memory; Julia cannot safely manage that memory directly through a &lt;code&gt;String&lt;/code&gt; view.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;UTF-8 Validation (Implicit):&lt;/strong&gt; While &lt;code&gt;unsafe_string&lt;/code&gt; itself might not strictly validate during the copy for performance, the resulting &lt;code&gt;String&lt;/code&gt; object is expected to hold valid UTF-8 data. Copying provides an opportunity (even if sometimes deferred) to ensure this, whereas a direct view would expose Julia code to potentially invalid byte sequences from C.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;While the copy introduces a small performance cost compared to a zero-copy view, it's necessary to maintain the guarantees and safety of Julia's immutable &lt;code&gt;String&lt;/code&gt; type when interfacing with potentially volatile C memory.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Base Documentation, &lt;code&gt;unsafe_string&lt;/code&gt;:&lt;/strong&gt; "Copy data from a &lt;code&gt;Ptr{UInt8}&lt;/code&gt; into a &lt;code&gt;String&lt;/code&gt;." Describes both the null-terminated and length-based versions.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0094_unsafe_string.jl
&lt;span class="nt"&gt;---&lt;/span&gt; Creating String from Null-Terminated Pointer &lt;span class="nt"&gt;---&lt;/span&gt;
Original C data &lt;span class="o"&gt;(&lt;/span&gt;bytes&lt;span class="o"&gt;)&lt;/span&gt;: UInt8[0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x00]
Julia string &lt;span class="o"&gt;(&lt;/span&gt;from null&lt;span class="o"&gt;)&lt;/span&gt;: &lt;span class="s2"&gt;"Hello"&lt;/span&gt;
Type: String
Length: 5

&lt;span class="nt"&gt;---&lt;/span&gt; Creating String from Pointer + Length &lt;span class="nt"&gt;---&lt;/span&gt;
Original C buffer &lt;span class="o"&gt;(&lt;/span&gt;bytes&lt;span class="o"&gt;)&lt;/span&gt;: UInt8[0x57, 0x6f, 0x72, 0x6c, 0x64]
Julia string &lt;span class="o"&gt;(&lt;/span&gt;from length&lt;span class="o"&gt;)&lt;/span&gt;: &lt;span class="s2"&gt;"World"&lt;/span&gt;
Type: String
Length: 5

&lt;span class="nt"&gt;---&lt;/span&gt; Demonstrating the Copy &lt;span class="o"&gt;(&lt;/span&gt;vs. unsafe_wrap&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
Original C data modified: UInt8[0x4a, 0x65, 0x6c, 0x6c, 0x6f, 0x00]
Julia string &lt;span class="o"&gt;(&lt;/span&gt;from null&lt;span class="o"&gt;)&lt;/span&gt; is unchanged: &lt;span class="s2"&gt;"Hello"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;0095_reinterpret.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0095_reinterpret.jl&lt;/span&gt;
&lt;span class="c"&gt;# Demonstrates 'reinterpret' for zero-copy type punning.&lt;/span&gt;

&lt;span class="c"&gt;# 1. Start with an array of one 'isbits' type.&lt;/span&gt;
&lt;span class="n"&gt;A_float&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Float64&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;π&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt; &lt;span class="c"&gt;# Vector{Float64}&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Original array (Float64): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;A_float&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Sizeof elements: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sizeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;eltype&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;A_float&lt;/span&gt;&lt;span class="x"&gt;)),&lt;/span&gt; &lt;span class="s"&gt;" bytes"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"First element bits:  "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bitstring&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;A_float&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;]))&lt;/span&gt;

&lt;span class="c"&gt;# --- Reinterpret to Same-Size Type ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Reinterpret: Float64 -&amp;gt; UInt64 ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 2. Use 'reinterpret(NewType, Array)'&lt;/span&gt;
&lt;span class="c"&gt;#    'NewType' must have the same size as 'eltype(Array)'.&lt;/span&gt;
&lt;span class="c"&gt;#    This creates a VIEW, not a copy. It interprets the *exact same bytes*&lt;/span&gt;
&lt;span class="c"&gt;#    as the new type.&lt;/span&gt;
&lt;span class="n"&gt;B_uint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reinterpret&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;UInt64&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;A_float&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Becomes Vector{UInt64}&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Reinterpreted array (UInt64): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;B_uint&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Sizeof elements: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sizeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;eltype&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;B_uint&lt;/span&gt;&lt;span class="x"&gt;)),&lt;/span&gt; &lt;span class="s"&gt;" bytes"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Still 8&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"First element bits:   "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bitstring&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;B_uint&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;]))&lt;/span&gt; &lt;span class="c"&gt;# Same bits as A_float[1]&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Type of reinterpreted array: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;B_uint&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;

&lt;span class="c"&gt;# 3. Modifications through the view AFFECT the original data.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Modifying view B_uint[4] = 0x0000_0000_0000_0000"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;B_uint&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mh"&gt;0x0000_0000_0000_0000&lt;/span&gt; &lt;span class="c"&gt;# Set the bits for 0.0 to all zeros&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"View B_uint is now: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;B_uint&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# A_float[4] was 0.0, which has a specific bit pattern (usually all zeros).&lt;/span&gt;
&lt;span class="c"&gt;# Let's check A_float[1] after changing B_uint[4] - it should be unchanged.&lt;/span&gt;
&lt;span class="c"&gt;# Re-check A_float[4] which should reflect the change if it was originally non-zero.&lt;/span&gt;
&lt;span class="c"&gt;# (Let's modify B_uint[1] instead for a clearer effect)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Modifying view B_uint[1] using bitwise XOR..."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;B_uint&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;B_uint&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt; &lt;span class="n"&gt;⊻&lt;/span&gt; &lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;UInt64&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;63&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Flip the sign bit&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"View B_uint[1] is now (bits): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bitstring&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;B_uint&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;]))&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Original A_float[1] is now: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;A_float&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;])&lt;/span&gt; &lt;span class="c"&gt;# Should now be -1.0&lt;/span&gt;

&lt;span class="c"&gt;# --- Reinterpret to Smaller Type ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Reinterpret: Float64 -&amp;gt; UInt8 ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 4. Reinterpret to a type with a smaller size.&lt;/span&gt;
&lt;span class="c"&gt;#    sizeof(UInt8) = 1 byte. sizeof(Float64) = 8 bytes.&lt;/span&gt;
&lt;span class="c"&gt;#    The resulting array will be larger.&lt;/span&gt;
&lt;span class="n"&gt;C_uint8&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reinterpret&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;UInt8&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;A_float&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Becomes Vector{UInt8}&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Reinterpreted array (UInt8): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;C_uint8&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Length of UInt8 array: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;C_uint8&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# length(A_float) * 8&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Type of reinterpreted array: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;C_uint8&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;

&lt;span class="c"&gt;# The first 8 bytes of C_uint8 correspond to the bytes of A_float[1]&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"First 8 bytes (UInt8): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;C_uint8&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="x"&gt;])&lt;/span&gt;

&lt;span class="c"&gt;# --- Reinterpret Single Values ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Reinterpret Single Values ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 5. Reinterpret can also work on single isbits values.&lt;/span&gt;
&lt;span class="n"&gt;f_val&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;
&lt;span class="n"&gt;u_val&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;UInt64&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reinterpret&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;UInt64&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f_val&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Value -1.0 (Float64): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f_val&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Value -1.0 reinterpreted as UInt64 (hex): 0x"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u_val&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Value -1.0 reinterpreted as UInt64 (bits): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bitstring&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u_val&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces &lt;code&gt;reinterpret(NewType, A)&lt;/code&gt;, a powerful &lt;strong&gt;zero-copy&lt;/strong&gt; operation that allows you to view the raw memory bytes of an array &lt;code&gt;A&lt;/code&gt; as if they represented elements of &lt;code&gt;NewType&lt;/code&gt;. This is often called "type punning."&lt;/p&gt;

&lt;h2&gt;
  
  
  Core Concept: Viewing Bits Differently
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;reinterpret(NewType, A)&lt;/code&gt; creates a &lt;strong&gt;new array view&lt;/strong&gt; that shares the &lt;strong&gt;exact same underlying memory&lt;/strong&gt; as the original array &lt;code&gt;A&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;It does &lt;strong&gt;not&lt;/strong&gt; copy any data.&lt;/li&gt;
&lt;li&gt;It does &lt;strong&gt;not&lt;/strong&gt; convert values (like &lt;code&gt;Float64(1)&lt;/code&gt; converts an &lt;code&gt;Int&lt;/code&gt; to a &lt;code&gt;Float&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Instead, it simply changes how Julia &lt;strong&gt;interprets the bits&lt;/strong&gt; stored in memory. It tells the compiler: "Look at this block of memory that you thought was an array of &lt;code&gt;Float64&lt;/code&gt;s; now, interpret those same bits as an array of &lt;code&gt;UInt64&lt;/code&gt;s (or &lt;code&gt;UInt8&lt;/code&gt;s, etc.)." &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Size Requirements and Resulting Dimensions
&lt;/h2&gt;

&lt;p&gt;The relationship between the size of the original element type (&lt;code&gt;eltype(A)&lt;/code&gt;) and &lt;code&gt;NewType&lt;/code&gt; determines the dimensions of the resulting view:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;sizeof(NewType) == sizeof(eltype(A))&lt;/code&gt;&lt;/strong&gt; (e.g., &lt;code&gt;Float64&lt;/code&gt; -&amp;gt; &lt;code&gt;UInt64&lt;/code&gt;, both 8 bytes):

&lt;ul&gt;
&lt;li&gt;The resulting array view has the &lt;strong&gt;same dimensions&lt;/strong&gt; as the original array &lt;code&gt;A&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;reinterpret(UInt64, A_float)&lt;/code&gt; produces a &lt;code&gt;Vector{UInt64}&lt;/code&gt; with the same length as &lt;code&gt;A_float&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;sizeof(NewType) &amp;lt; sizeof(eltype(A))&lt;/code&gt;&lt;/strong&gt; (e.g., &lt;code&gt;Float64&lt;/code&gt; -&amp;gt; &lt;code&gt;UInt8&lt;/code&gt;, 8 bytes -&amp;gt; 1 byte):

&lt;ul&gt;
&lt;li&gt;The resulting array view will have an &lt;strong&gt;additional first dimension&lt;/strong&gt; whose size is &lt;code&gt;sizeof(eltype(A)) ÷ sizeof(NewType)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;reinterpret(UInt8, A_float)&lt;/code&gt; treats each &lt;code&gt;Float64&lt;/code&gt; as 8 consecutive &lt;code&gt;UInt8&lt;/code&gt;s. The result is a &lt;code&gt;Vector{UInt8}&lt;/code&gt; whose length is &lt;code&gt;length(A_float) * 8&lt;/code&gt;. If &lt;code&gt;A_float&lt;/code&gt; were a matrix, the result would effectively add a dimension of size 8.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;sizeof(NewType) &amp;gt; sizeof(eltype(A))&lt;/code&gt;&lt;/strong&gt; (e.g., &lt;code&gt;UInt8&lt;/code&gt; -&amp;gt; &lt;code&gt;UInt64&lt;/code&gt;):

&lt;ul&gt;
&lt;li&gt;This requires the first dimension of &lt;code&gt;A&lt;/code&gt; to be appropriately sized (&lt;code&gt;sizeof(NewType) ÷ sizeof(eltype(A))&lt;/code&gt;). This dimension is then removed in the resulting view. This case is less common.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Performance and Use Cases (HFT Context)
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;reinterpret&lt;/code&gt; is a critical tool for low-level performance optimization and data manipulation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero-Copy:&lt;/strong&gt; It avoids memory allocation and copying, making it extremely fast.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bitwise Operations:&lt;/strong&gt; Floating-point types (&lt;code&gt;Float32&lt;/code&gt;, &lt;code&gt;Float64&lt;/code&gt;) don't support bitwise operations (&lt;code&gt;&amp;amp;&lt;/code&gt;, &lt;code&gt;|&lt;/code&gt;, &lt;code&gt;⊻&lt;/code&gt;, shifts). To perform bit-level checks or manipulations on the IEEE 754 representation of a float (e.g., quickly checking the sign bit, extracting exponent/mantissa bits), you &lt;code&gt;reinterpret&lt;/code&gt; it as an unsigned integer (&lt;code&gt;UInt32&lt;/code&gt;, &lt;code&gt;UInt64&lt;/code&gt;) of the same size. We demonstrate flipping the sign bit (&lt;code&gt;UInt64(1) &amp;lt;&amp;lt; 63&lt;/code&gt;) of &lt;code&gt;A_float[1]&lt;/code&gt; via the &lt;code&gt;B_uint&lt;/code&gt; view.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Serialization/Network I/O:&lt;/strong&gt; When sending an array of &lt;code&gt;Float64&lt;/code&gt;s over the network or saving to a binary file, you often need a raw byte stream (&lt;code&gt;Vector{UInt8}&lt;/code&gt;). &lt;code&gt;reinterpret(UInt8, A_float)&lt;/code&gt; provides this &lt;strong&gt;zero-copy view&lt;/strong&gt; of the underlying bytes, which can then be written directly to an &lt;code&gt;IO&lt;/code&gt; stream.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hashing:&lt;/strong&gt; Calculating a hash over raw bytes (&lt;code&gt;Vector{UInt8}&lt;/code&gt;) can sometimes be faster or provide different properties than hashing structured data (&lt;code&gt;Vector{Float64}&lt;/code&gt;). &lt;code&gt;reinterpret&lt;/code&gt; allows accessing those bytes directly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Shared Memory
&lt;/h2&gt;

&lt;p&gt;Because &lt;code&gt;reinterpret&lt;/code&gt; creates a view, modifying the reinterpreted array (&lt;code&gt;B_uint&lt;/code&gt;) directly modifies the bits in the memory shared with the original array (&lt;code&gt;A_float&lt;/code&gt;), changing its value, as demonstrated by flipping the sign bit.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Base Documentation, &lt;code&gt;reinterpret&lt;/code&gt;:&lt;/strong&gt; "Change the type-interpretation of a block of memory... without copying data." Explains the dimension changes based on type sizes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IEEE 754 Standard:&lt;/strong&gt; Defines the binary representation of floating-point numbers, which is what allows &lt;code&gt;reinterpret&lt;/code&gt; between floats and integers to be meaningful for bitwise manipulation.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0095_reinterpret.jl
Original array &lt;span class="o"&gt;(&lt;/span&gt;Float64&lt;span class="o"&gt;)&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;1.0, &lt;span class="nt"&gt;-2&lt;/span&gt;.0, 3.141592653589793, 0.0]
Sizeof elements: 8 bytes
First element bits:  0011111111110000000000000000000000000000000000000000000000000000

&lt;span class="nt"&gt;---&lt;/span&gt; Reinterpret: Float64 -&amp;gt; UInt64 &lt;span class="nt"&gt;---&lt;/span&gt;
Reinterpreted array &lt;span class="o"&gt;(&lt;/span&gt;UInt64&lt;span class="o"&gt;)&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;4607182418800017408, 13830554455654793216, 4614256656552045848, 0]
Sizeof elements: 8 bytes
First element bits:   0011111111110000000000000000000000000000000000000000000000000000
Type of reinterpreted array: Vector&lt;span class="o"&gt;{&lt;/span&gt;UInt64&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;Array&lt;span class="o"&gt;{&lt;/span&gt;UInt64, 1&lt;span class="o"&gt;})&lt;/span&gt;

Modifying view B_uint[1] using bitwise XOR...
View B_uint[1] is now &lt;span class="o"&gt;(&lt;/span&gt;bits&lt;span class="o"&gt;)&lt;/span&gt;: 1011111111110000000000000000000000000000000000000000000000000000
Original A_float[1] is now: &lt;span class="nt"&gt;-1&lt;/span&gt;.0

&lt;span class="nt"&gt;---&lt;/span&gt; Reinterpret: Float64 -&amp;gt; UInt8 &lt;span class="nt"&gt;---&lt;/span&gt;
Reinterpreted array &lt;span class="o"&gt;(&lt;/span&gt;UInt8&lt;span class="o"&gt;)&lt;/span&gt;: UInt8[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x18, 0x2d, 0x44, 0x54, 0xfb, 0x21, 0x09, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
Length of UInt8 array: 32
Type of reinterpreted array: Vector&lt;span class="o"&gt;{&lt;/span&gt;UInt8&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;Array&lt;span class="o"&gt;{&lt;/span&gt;UInt8, 1&lt;span class="o"&gt;})&lt;/span&gt;
First 8 bytes &lt;span class="o"&gt;(&lt;/span&gt;UInt8&lt;span class="o"&gt;)&lt;/span&gt;: UInt8[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xbf]

&lt;span class="nt"&gt;---&lt;/span&gt; Reinterpret Single Values &lt;span class="nt"&gt;---&lt;/span&gt;
Value &lt;span class="nt"&gt;-1&lt;/span&gt;.0 &lt;span class="o"&gt;(&lt;/span&gt;Float64&lt;span class="o"&gt;)&lt;/span&gt;: &lt;span class="nt"&gt;-1&lt;/span&gt;.0
Value &lt;span class="nt"&gt;-1&lt;/span&gt;.0 reinterpreted as UInt64 &lt;span class="o"&gt;(&lt;/span&gt;hex&lt;span class="o"&gt;)&lt;/span&gt;: 0xbff0000000000000
Value &lt;span class="nt"&gt;-1&lt;/span&gt;.0 reinterpreted as UInt64 &lt;span class="o"&gt;(&lt;/span&gt;bits&lt;span class="o"&gt;)&lt;/span&gt;: 1011111111110000000000000000000000000000000000000000000000000000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(Byte order in the &lt;code&gt;UInt8&lt;/code&gt; array may vary depending on system endianness. Bit patterns and hex value for -1.0 are standard IEEE 754.)&lt;/em&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Module 10: Advanced Parallelism and Thread Safety
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Multi Threading
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;0096_module_intro.md&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This module tackles &lt;strong&gt;parallelism&lt;/strong&gt;, the technique of executing computations simultaneously to leverage modern multi-core processors. We will distinguish this sharply from the &lt;strong&gt;concurrency&lt;/strong&gt; explored in Module 7 and introduce Julia's powerful tools for both shared-memory (multi-threading) and distributed-memory (multi-processing) parallelism.&lt;/p&gt;




&lt;h2&gt;
  
  
  Concurrency vs. Parallelism Revisited
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Concurrency (Module 7):&lt;/strong&gt; Primarily managed with &lt;code&gt;Task&lt;/code&gt;s (&lt;code&gt;@async&lt;/code&gt;). Focuses on &lt;strong&gt;managing many tasks over time&lt;/strong&gt;, often interleaving their execution on a &lt;strong&gt;single OS thread&lt;/strong&gt;. Tasks yield control during blocking operations (like I/O or &lt;code&gt;sleep&lt;/code&gt;), preventing one slow operation from halting progress on others. Excellent for I/O-bound workloads (like handling many network clients).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parallelism (This Module):&lt;/strong&gt; Focuses on &lt;strong&gt;executing multiple tasks truly simultaneously&lt;/strong&gt; to speed up CPU-bound work. This requires utilizing multiple CPU cores via:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Multi-Threading:&lt;/strong&gt; Multiple OS threads operating within a &lt;strong&gt;single process&lt;/strong&gt;, sharing the same memory space.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-Processing:&lt;/strong&gt; Multiple independent OS &lt;strong&gt;processes&lt;/strong&gt;, each with its own separate memory space.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  Julia's Parallelism Advantage: No GIL
&lt;/h2&gt;

&lt;p&gt;A defining feature, especially compared to languages like CPython, is Julia's &lt;strong&gt;lack of a Global Interpreter Lock (GIL)&lt;/strong&gt;. This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;True Shared-Memory Parallelism:&lt;/strong&gt; Julia code running on Thread 1 can execute &lt;em&gt;at the exact same physical time&lt;/em&gt; as Julia code running on Thread 2, provided they are scheduled on different CPU cores. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;C++/Rust Level Capability:&lt;/strong&gt; This enables genuine in-process, shared-memory parallelism, matching the capabilities of compiled languages like C++ and Rust, which is crucial for maximizing performance on modern hardware.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Responsibility: Thread Safety
&lt;/h2&gt;

&lt;p&gt;With the power of shared-memory parallelism comes the absolute requirement of &lt;strong&gt;thread safety&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Data Races:&lt;/strong&gt; When multiple threads access shared, mutable data without proper synchronization, and at least one access is a write, you have a &lt;strong&gt;data race&lt;/strong&gt;. This leads to unpredictable results, memory corruption, and non-deterministic crashes that are notoriously difficult to debug.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Synchronization:&lt;/strong&gt; Protecting shared data requires &lt;strong&gt;synchronization mechanisms&lt;/strong&gt; like locks or atomic operations to ensure that critical sections of code are executed by only one thread at a time or that updates happen indivisibly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Non-Negotiable:&lt;/strong&gt; Failure to ensure thread safety &lt;em&gt;will&lt;/em&gt; break your program in subtle and catastrophic ways. Understanding and correctly applying synchronization primitives is not optional; it's a fundamental requirement of multi-threaded programming.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Relevance to High-Performance Computing (HFT)
&lt;/h2&gt;

&lt;p&gt;Parallelism is essential for low-latency systems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Processing data from multiple market feeds simultaneously.&lt;/li&gt;
&lt;li&gt;Running computationally intensive calculations (e.g., signal processing, model execution) for different instruments or strategies in parallel.&lt;/li&gt;
&lt;li&gt;Reacting to incoming events with minimal delay by dedicating threads or processes to specific tasks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The tools covered in this module—&lt;code&gt;Threads&lt;/code&gt;, &lt;code&gt;Distributed&lt;/code&gt;, atomics, and SIMD—are the building blocks for constructing such high-performance, parallel systems in Julia.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Parallel Computing":&lt;/strong&gt; Provides a high-level overview of Julia's multi-threading and distributed computing capabilities.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Multi-Threading":&lt;/strong&gt; Details the specifics of Julia's threading model and associated tools.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;0097_launching_with_threads.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0097_launching_with_threads.jl&lt;/span&gt;
&lt;span class="c"&gt;# How to enable and check Julia's multi-threading capabilities.&lt;/span&gt;

&lt;span class="c"&gt;# 1. Access the 'Threads' module (part of Base Julia).&lt;/span&gt;
&lt;span class="c"&gt;#    No 'import' is needed for names directly in 'Base.Threads'.&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Threads&lt;/span&gt; &lt;span class="c"&gt;# Import the module itself to be explicit&lt;/span&gt;

&lt;span class="c"&gt;# 2. Get the number of threads Julia was started with.&lt;/span&gt;
&lt;span class="c"&gt;#    'Threads.nthreads()' returns the size of the thread pool.&lt;/span&gt;
&lt;span class="n"&gt;num_threads&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Threads&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nthreads&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Julia process launched with &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;num_threads thread(s)."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 3. Check if multi-threading is actually enabled.&lt;/span&gt;
&lt;span class="c"&gt;#    If nthreads() == 1, parallel execution is not possible.&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;num_threads&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"WARNING: Multi-threading is DISABLED."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Performance will be limited to a single core."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"To enable parallelism for subsequent lessons, restart Julia"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"using one of the following methods:"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  a) Command Line: julia -t N  (e.g., julia -t 4)"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  b) Command Line: julia -t auto (uses all available logical cores)"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  c) Environment Variable: export JULIA_NUM_THREADS=N (before starting Julia)"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SUCCESS: Multi-threading is ENABLED."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Parallel execution using up to &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;num_threads threads is possible."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 4. Get the ID of the *current* OS thread executing this code.&lt;/span&gt;
&lt;span class="c"&gt;#    Thread IDs range from 1 to nthreads().&lt;/span&gt;
&lt;span class="c"&gt;#    The main thread (that runs the script initially) is always ID 1.&lt;/span&gt;
&lt;span class="n"&gt;main_thread_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Threads&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;threadid&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"This main script is currently running on thread ID: &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;main_thread_id"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script explains how Julia's multi-threading capabilities are &lt;strong&gt;enabled at startup&lt;/strong&gt; and how to verify the configuration. Unlike some languages where threading is always available, Julia requires an explicit opt-in to create its pool of worker threads.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Core Concept: Startup Configuration&lt;/strong&gt;
Julia's parallel scheduler uses a pool of &lt;strong&gt;Operating System (OS) threads&lt;/strong&gt;. This pool is created &lt;strong&gt;only once&lt;/strong&gt; when the Julia process starts. You &lt;strong&gt;cannot&lt;/strong&gt; change the number of threads after Julia has started.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enabling Threads:&lt;/strong&gt;
To utilize multiple CPU cores for parallel execution, you &lt;em&gt;must&lt;/em&gt; tell Julia how many threads to create when you launch it. There are three primary methods:

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;-t N&lt;/code&gt; / &lt;code&gt;--threads N&lt;/code&gt; Command-Line Flag:&lt;/strong&gt; &lt;code&gt;julia -t 4 my_script.jl&lt;/code&gt; starts Julia with a main thread and 3 additional worker threads, for a total of 4 threads available via &lt;code&gt;Threads.nthreads()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;-t auto&lt;/code&gt; / &lt;code&gt;--threads auto&lt;/code&gt; Flag:&lt;/strong&gt; &lt;code&gt;julia -t auto my_script.jl&lt;/code&gt; automatically detects the number of logical CPU cores on your machine and sets &lt;code&gt;N&lt;/code&gt; to that value. This is often the most convenient option.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;JULIA_NUM_THREADS&lt;/code&gt; Environment Variable:&lt;/strong&gt; Setting this variable &lt;em&gt;before&lt;/em&gt; launching Julia (e.g., &lt;code&gt;export JULIA_NUM_THREADS=4&lt;/code&gt; in bash, then &lt;code&gt;julia my_script.jl&lt;/code&gt;) achieves the same result as the command-line flag.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Checking the Configuration:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Threads.nthreads()&lt;/code&gt;:&lt;/strong&gt; This function returns the &lt;strong&gt;total number of threads&lt;/strong&gt; in Julia's pool (main thread + worker threads). If this returns &lt;code&gt;1&lt;/code&gt;, multi-threading was not enabled at startup, and parallel execution macros like &lt;code&gt;Threads.@spawn&lt;/code&gt; or &lt;code&gt;Threads.@threads&lt;/code&gt; will effectively run sequentially on the single main thread.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Threads.threadid()&lt;/code&gt;:&lt;/strong&gt; This function returns the &lt;strong&gt;integer ID&lt;/strong&gt; (from &lt;code&gt;1&lt;/code&gt; to &lt;code&gt;nthreads()&lt;/code&gt;) of the specific OS thread that is currently executing the code. The thread that initially runs your script is always &lt;code&gt;1&lt;/code&gt;. When you launch parallel tasks (next lessons), you'll see them report different &lt;code&gt;threadid()&lt;/code&gt;s as they run on other threads in the pool.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Verification:&lt;/strong&gt;
Running this script normally (&lt;code&gt;julia 0097_launching_with_threads.jl&lt;/code&gt;) will likely show &lt;code&gt;1&lt;/code&gt; thread and print the warning. Running it with threading enabled (e.g., &lt;code&gt;julia -t 4 0097_launching_with_threads.jl&lt;/code&gt;) will show the number of threads requested and confirm that multi-threading is active. This check is essential before running any multi-threaded code to ensure parallelism is actually possible.&lt;/li&gt;

&lt;/ul&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Multi-Threading", "Starting Julia with multiple threads":&lt;/strong&gt; Details the command-line flags and environment variable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Base Documentation, &lt;code&gt;Threads.nthreads&lt;/code&gt;:&lt;/strong&gt; "Get the number of threads available to the Julia process."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Base Documentation, &lt;code&gt;Threads.threadid&lt;/code&gt;:&lt;/strong&gt; "Get the ID of the current thread."&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;To run the script:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Without Threads:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0097_launching_with_threads.jl
Julia process launched with 1 thread&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
WARNING: Multi-threading is DISABLED.
Performance will be limited to a single core.
To &lt;span class="nb"&gt;enable &lt;/span&gt;parallelism &lt;span class="k"&gt;for &lt;/span&gt;subsequent lessons, restart Julia
using one of the following methods:
  a&lt;span class="o"&gt;)&lt;/span&gt; Command Line: julia &lt;span class="nt"&gt;-t&lt;/span&gt; N  &lt;span class="o"&gt;(&lt;/span&gt;e.g., julia &lt;span class="nt"&gt;-t&lt;/span&gt; 4&lt;span class="o"&gt;)&lt;/span&gt;
  b&lt;span class="o"&gt;)&lt;/span&gt; Command Line: julia &lt;span class="nt"&gt;-t&lt;/span&gt; auto &lt;span class="o"&gt;(&lt;/span&gt;uses all available logical cores&lt;span class="o"&gt;)&lt;/span&gt;
  c&lt;span class="o"&gt;)&lt;/span&gt; Environment Variable: &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;JULIA_NUM_THREADS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;N &lt;span class="o"&gt;(&lt;/span&gt;before starting Julia&lt;span class="o"&gt;)&lt;/span&gt;
This main script is currently running on thread ID: 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;With Threads (e.g., 4):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia &lt;span class="nt"&gt;-t&lt;/span&gt; 4 0097_launching_with_threads.jl
Julia process launched with 4 thread&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
SUCCESS: Multi-threading is ENABLED.
Parallel execution using up to 4 threads is possible.
This main script is currently running on thread ID: 1
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;&lt;em&gt;(Replace &lt;code&gt;4&lt;/code&gt; with the number of threads you requested or &lt;code&gt;auto&lt;/code&gt; detected.)&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;0098_threads_spawn.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0098_threads_spawn.jl&lt;/span&gt;
&lt;span class="c"&gt;# Introduces Threads.@spawn for dynamic parallel task execution.&lt;/span&gt;
&lt;span class="c"&gt;# Requires running Julia with multiple threads (e.g., 'julia -t 4')&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Threads&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nd"&gt;@spawn&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;threadid&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;fetch&lt;/span&gt; &lt;span class="c"&gt;# fetch is needed to get results&lt;/span&gt;

&lt;span class="c"&gt;# 1. Define a function simulating CPU-intensive work.&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; cpu_intensive_work&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;iterations&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# Report which thread is starting the work for this ID&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Task &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;id: Starting on thread "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;threadid&lt;/span&gt;&lt;span class="x"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;sum_val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;
    &lt;span class="c"&gt;# Perform a non-trivial computation&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;iterations&lt;/span&gt;
        &lt;span class="n"&gt;sum_val&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;sin&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sqrt&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;float&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="x"&gt;)))&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="c"&gt;# Report which thread finished the work&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Task &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;id: Finished on thread "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;threadid&lt;/span&gt;&lt;span class="x"&gt;(),&lt;/span&gt; &lt;span class="s"&gt;" | Result: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sum_val&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sum_val&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Return a tuple with the ID and result&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# --- Execution ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Main script running on thread: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;threadid&lt;/span&gt;&lt;span class="x"&gt;())&lt;/span&gt;
&lt;span class="n"&gt;num_tasks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="n"&gt;iterations_per_task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;50_000_000&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Spawning &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;num_tasks parallel tasks using Threads.@spawn..."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 2. Create storage for the Task objects returned by @spawn.&lt;/span&gt;
&lt;span class="n"&gt;tasks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Vector&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;Task&lt;/span&gt;&lt;span class="x"&gt;}(&lt;/span&gt;&lt;span class="nb"&gt;undef&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;num_tasks&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 3. Launch tasks using Threads.@spawn.&lt;/span&gt;
&lt;span class="c"&gt;#    '@spawn' creates a Task and schedules it to run on any available thread&lt;/span&gt;
&lt;span class="c"&gt;#    from Julia's thread pool. It returns the Task object immediately.&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;num_tasks&lt;/span&gt;
    &lt;span class="c"&gt;# Schedule the function call to run in parallel&lt;/span&gt;
    &lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;@spawn&lt;/span&gt; &lt;span class="n"&gt;cpu_intensive_work&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;iterations_per_task&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"All tasks spawned. Main thread continues while tasks run in parallel."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Waiting for tasks to complete by calling fetch()..."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 4. Wait for each task and retrieve its result using 'fetch()'.&lt;/span&gt;
&lt;span class="c"&gt;#    'fetch(t)' blocks the *current* thread (Thread 1 here) until 't' finishes.&lt;/span&gt;
&lt;span class="c"&gt;#    We collect results in an array.&lt;/span&gt;
&lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Vector&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;Any&lt;/span&gt;&lt;span class="x"&gt;}(&lt;/span&gt;&lt;span class="nb"&gt;undef&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;num_tasks&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Use Any for tuples, or be more specific&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;num_tasks&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Main: Waiting for Task "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"..."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# fetch() blocks here if tasks[i] is not yet complete.&lt;/span&gt;
    &lt;span class="n"&gt;task_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fetch&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="x"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;task_result&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Main: Fetched result from Task "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;All tasks complete."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Collected results:"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces &lt;strong&gt;&lt;code&gt;Threads.@spawn&lt;/code&gt;&lt;/strong&gt;, the primary macro for launching &lt;strong&gt;parallel tasks&lt;/strong&gt; in Julia's modern multi-threading system. It enables dynamic task creation and leverages an efficient work-stealing scheduler.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Core Concept: Parallel Task Execution&lt;/strong&gt;
&lt;code&gt;Threads.@spawn expression&lt;/code&gt; takes a Julia expression (typically a function call), wraps it in a &lt;code&gt;Task&lt;/code&gt;, and submits it to Julia's &lt;strong&gt;multi-threaded scheduler&lt;/strong&gt;. This scheduler then assigns the task to run on one of the available &lt;strong&gt;worker threads&lt;/strong&gt; (threads with ID &amp;gt; 1) in Julia's thread pool, allowing it to execute in parallel with the main thread and other spawned tasks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@spawn&lt;/code&gt; vs. &lt;code&gt;@async&lt;/code&gt;:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@async&lt;/code&gt; (Module 7): Designed for &lt;strong&gt;concurrency&lt;/strong&gt; on a &lt;em&gt;single&lt;/em&gt; thread. Tasks yield cooperatively during I/O or explicit yields.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@spawn&lt;/code&gt;: Designed for &lt;strong&gt;parallelism&lt;/strong&gt; across &lt;em&gt;multiple&lt;/em&gt; threads/cores. Ideal for CPU-bound computations.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Return Value: &lt;code&gt;Task&lt;/code&gt; Object&lt;/strong&gt;
Like &lt;code&gt;@async&lt;/code&gt;, &lt;code&gt;@spawn&lt;/code&gt; returns &lt;strong&gt;immediately&lt;/strong&gt;, without waiting for the task to start or finish. It returns a &lt;code&gt;Task&lt;/code&gt; object, which serves as a handle to the asynchronously executing computation.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Work-Stealing Scheduler:&lt;/strong&gt;
&lt;code&gt;@spawn&lt;/code&gt; uses a sophisticated &lt;strong&gt;work-stealing scheduler&lt;/strong&gt;. Each worker thread maintains a queue of tasks. If a thread finishes its own tasks and another thread still has tasks waiting in its queue, the idle thread can "steal" work from the busy thread. This provides excellent load balancing and CPU utilization, especially when tasks have varying durations.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Synchronization and Results: &lt;code&gt;fetch(t::Task)&lt;/code&gt;&lt;/strong&gt;
To get the result of a task launched with &lt;code&gt;@spawn&lt;/code&gt; and ensure it has completed, you use &lt;code&gt;fetch(t)&lt;/code&gt;.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Blocking:&lt;/strong&gt; &lt;code&gt;fetch(t)&lt;/code&gt; &lt;strong&gt;blocks&lt;/strong&gt; the calling thread until task &lt;code&gt;t&lt;/code&gt; finishes execution.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Return Value:&lt;/strong&gt; It returns the value returned by the expression executed within the task (e.g., the tuple &lt;code&gt;(id, sum_val)&lt;/code&gt; from &lt;code&gt;cpu_intensive_work&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error Propagation:&lt;/strong&gt; If the spawned task throws an exception, &lt;code&gt;fetch(t)&lt;/code&gt; will re-throw that same exception on the calling thread.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Workflow:&lt;/strong&gt;

&lt;ol&gt;
&lt;li&gt; Launch multiple parallel computations using &lt;code&gt;@spawn&lt;/code&gt;, storing the returned &lt;code&gt;Task&lt;/code&gt; objects.&lt;/li&gt;
&lt;li&gt; Perform any other work that can be done concurrently on the main thread (optional).&lt;/li&gt;
&lt;li&gt; Call &lt;code&gt;fetch()&lt;/code&gt; on each &lt;code&gt;Task&lt;/code&gt; object to wait for its completion and collect its result. This loop effectively acts as a "join" point, ensuring all parallel work is done before proceeding.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;Threads.@spawn&lt;/code&gt; is the recommended, flexible way to achieve parallelism for complex or dynamic workloads in Julia.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Multi-Threading":&lt;/strong&gt; Explains &lt;code&gt;@spawn&lt;/code&gt; and the task-based parallelism model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Base Documentation, &lt;code&gt;Threads.@spawn&lt;/code&gt;:&lt;/strong&gt; Details the macro's behavior.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Base Documentation, &lt;code&gt;fetch&lt;/code&gt;:&lt;/strong&gt; Explains how to wait for and retrieve task results.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;To run the script:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(You MUST start Julia with multiple threads, e.g., &lt;code&gt;julia -t 4 0098_threads_spawn.jl&lt;/code&gt;)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia &lt;span class="nt"&gt;-t&lt;/span&gt; 4 0098_threads_spawn.jl
Main script running on thread: 1
Spawning 4 parallel tasks using Threads.@spawn...
All tasks spawned. Main thread continues &lt;span class="k"&gt;while &lt;/span&gt;tasks run &lt;span class="k"&gt;in &lt;/span&gt;parallel.
Waiting &lt;span class="k"&gt;for &lt;/span&gt;tasks to &lt;span class="nb"&gt;complete &lt;/span&gt;by calling fetch&lt;span class="o"&gt;()&lt;/span&gt;...
Main: Waiting &lt;span class="k"&gt;for &lt;/span&gt;Task 1...
Task 1: Starting on thread 1  &lt;span class="c"&gt;# May start on any thread&lt;/span&gt;
Task 2: Starting on thread 2
Task 3: Starting on thread 3
Task 4: Starting on thread 4
Task 2: Finished on thread 2 | Result: &lt;span class="c"&gt;###&lt;/span&gt;
Task 4: Finished on thread 4 | Result: &lt;span class="c"&gt;###&lt;/span&gt;
Task 3: Finished on thread 3 | Result: &lt;span class="c"&gt;###&lt;/span&gt;
Task 1: Finished on thread 1 | Result: &lt;span class="c"&gt;###&lt;/span&gt;
Main: Fetched result from Task 1
Main: Waiting &lt;span class="k"&gt;for &lt;/span&gt;Task 2...
Main: Fetched result from Task 2
Main: Waiting &lt;span class="k"&gt;for &lt;/span&gt;Task 3...
Main: Fetched result from Task 3
Main: Waiting &lt;span class="k"&gt;for &lt;/span&gt;Task 4...
Main: Fetched result from Task 4

All tasks complete.
Collected results:
  &lt;span class="o"&gt;(&lt;/span&gt;1, &lt;span class="c"&gt;###)&lt;/span&gt;
  &lt;span class="o"&gt;(&lt;/span&gt;2, &lt;span class="c"&gt;###)&lt;/span&gt;
  &lt;span class="o"&gt;(&lt;/span&gt;3, &lt;span class="c"&gt;###)&lt;/span&gt;
  &lt;span class="o"&gt;(&lt;/span&gt;4, &lt;span class="c"&gt;###)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(The exact order of "Starting" and "Finished" messages will vary due to parallel execution and scheduling. Results &lt;code&gt;###&lt;/code&gt; will be floating-point numbers.)&lt;/em&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;0099_threads_macro.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0099_threads_macro.jl&lt;/span&gt;
&lt;span class="c"&gt;# Introduces Threads.@threads for static parallelization of for loops.&lt;/span&gt;
&lt;span class="c"&gt;# Requires running Julia with multiple threads (e.g., 'julia -t 4')&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Threads&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nd"&gt;@threads&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;threadid&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nthreads&lt;/span&gt;

&lt;span class="c"&gt;# --- Example 1: Safe Parallel Loop (Writing to unique indices) ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Example 1: Safe Parallel Loop ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;N&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="c"&gt;# Number of iterations&lt;/span&gt;
&lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;zeros&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Array to store results&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Main script on thread: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;threadid&lt;/span&gt;&lt;span class="x"&gt;())&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Looping &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;N times using &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;(nthreads()) threads..."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 1. Use 'Threads.@threads' before a 'for' loop.&lt;/span&gt;
&lt;span class="c"&gt;#    Julia divides the loop iterations (1:N) into chunks,&lt;/span&gt;
&lt;span class="c"&gt;#    one chunk per available thread. Each thread executes its chunk.&lt;/span&gt;
&lt;span class="n"&gt;Threads&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nd"&gt;@threads&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;
    &lt;span class="c"&gt;# Simulate work for each iteration&lt;/span&gt;
    &lt;span class="n"&gt;work_val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;20_000_000&lt;/span&gt; &lt;span class="c"&gt;# Shorter loop for quicker demo&lt;/span&gt;
        &lt;span class="n"&gt;work_val&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="c"&gt;# Report which thread handled which iteration&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Iteration &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;i running on thread "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;threadid&lt;/span&gt;&lt;span class="x"&gt;())&lt;/span&gt;

    &lt;span class="c"&gt;# CRITICAL: This is safe *only* because each thread writes&lt;/span&gt;
    &lt;span class="c"&gt;# to a unique, non-overlapping index results[i].&lt;/span&gt;
    &lt;span class="c"&gt;# There is no shared mutable state being modified concurrently.&lt;/span&gt;
    &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;work_val&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="c"&gt;# The main thread waits here until *all* threads finish their chunks.&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Loop finished."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Results: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;


&lt;span class="c"&gt;# --- Example 2: Data Race (Incorrectly modifying shared state) ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Example 2: Data Race ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;total_sum_incorrect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt; &lt;span class="c"&gt;# Shared mutable variable&lt;/span&gt;
&lt;span class="n"&gt;iterations_race&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1_000_000&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Calculating sum incorrectly (data race)..."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 2. INCORRECT use of @threads with shared mutable state.&lt;/span&gt;
&lt;span class="n"&gt;Threads&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nd"&gt;@threads&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;iterations_race&lt;/span&gt;
    &lt;span class="c"&gt;# !! DATA RACE !!&lt;/span&gt;
    &lt;span class="c"&gt;# Multiple threads read 'total_sum_incorrect', add 1.0,&lt;/span&gt;
    &lt;span class="c"&gt;# and try to write back simultaneously. Updates will be lost.&lt;/span&gt;
    &lt;span class="kd"&gt;global&lt;/span&gt; &lt;span class="n"&gt;total_sum_incorrect&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Loop finished."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# The result will be significantly LESS than iterations_race.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Incorrect Total Sum (will be &amp;lt; &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;iterations_race): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total_sum_incorrect&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"This demonstrates a read-modify-write data race."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces &lt;strong&gt;&lt;code&gt;Threads.@threads&lt;/code&gt;&lt;/strong&gt;, a macro designed for simple &lt;strong&gt;parallelization of &lt;code&gt;for&lt;/code&gt; loops&lt;/strong&gt;. It offers a straightforward way to distribute loop iterations across multiple threads but requires careful consideration of &lt;strong&gt;data safety&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Core Concept: Static Loop Scheduling&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;Threads.@threads for i in iterable ... end&lt;/code&gt; tells Julia to divide the work of the loop iterations among the available threads.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Static Schedule:&lt;/strong&gt; Unlike &lt;code&gt;@spawn&lt;/code&gt;'s dynamic work-stealing, &lt;code&gt;@threads&lt;/code&gt; typically performs a &lt;strong&gt;static schedule&lt;/strong&gt;. It divides the iteration space (e.g., &lt;code&gt;1:N&lt;/code&gt;) into roughly equal chunks (approximately &lt;code&gt;N / nthreads()&lt;/code&gt; iterations per chunk) and assigns one chunk to each thread (&lt;code&gt;1&lt;/code&gt; to &lt;code&gt;nthreads()&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implicit Wait:&lt;/strong&gt; The code &lt;em&gt;after&lt;/em&gt; the &lt;code&gt;@threads for&lt;/code&gt; loop only executes &lt;strong&gt;after all threads&lt;/strong&gt; have completed their assigned chunks. The main thread implicitly waits.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;When to Use &lt;code&gt;@threads&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
It's best suited for &lt;strong&gt;"embarrassingly parallel"&lt;/strong&gt; loops where:&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1.  Each iteration is **independent** of the others (the calculation for `i` doesn't depend on the result for `i-1`).
2.  The amount of work per iteration is **roughly equal**.
3.  You are primarily performing **CPU-bound work**.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The Critical Danger: Data Races&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Example 1 (Safe):&lt;/strong&gt; This loop is safe because each thread writes to a &lt;strong&gt;separate, unique location&lt;/strong&gt; in the &lt;code&gt;results&lt;/code&gt; array (&lt;code&gt;results[i]&lt;/code&gt;). There's no possibility of two threads trying to modify the &lt;em&gt;same&lt;/em&gt; memory location simultaneously.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Example 2 (Unsafe - Data Race):&lt;/strong&gt; This loop demonstrates a &lt;strong&gt;classic data race&lt;/strong&gt;. &lt;code&gt;total_sum_incorrect&lt;/code&gt; is a single variable shared by all threads. The operation &lt;code&gt;total_sum_incorrect += 1.0&lt;/code&gt; is &lt;strong&gt;not atomic&lt;/strong&gt; (indivisible). It involves three steps:

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Read&lt;/strong&gt; the current value of &lt;code&gt;total_sum_incorrect&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Modify&lt;/strong&gt; the value (add 1.0).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Write&lt;/strong&gt; the new value back.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;If Thread 2 reads the value (&lt;code&gt;100&lt;/code&gt;), then Thread 3 reads the value (&lt;code&gt;100&lt;/code&gt;) &lt;em&gt;before&lt;/em&gt; Thread 2 writes its result (&lt;code&gt;101&lt;/code&gt;), both threads will eventually write &lt;code&gt;101&lt;/code&gt;. One increment is lost. This happens thousands of times, leading to a final sum far less than the number of iterations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;global&lt;/code&gt; Keyword:&lt;/strong&gt; Note the use of &lt;code&gt;global total_sum_incorrect += 1.0&lt;/code&gt;. Just like in single-threaded loops (Module 2), modifying a global variable from within the loop's scope requires the &lt;code&gt;global&lt;/code&gt; keyword.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;@threads&lt;/code&gt; vs. &lt;code&gt;@spawn&lt;/code&gt;:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@threads&lt;/code&gt;:&lt;/strong&gt; Simpler syntax for basic &lt;code&gt;for&lt;/code&gt; loops. Static scheduling (can be inefficient if work per iteration varies greatly). &lt;strong&gt;Requires manual care&lt;/strong&gt; regarding data races if shared mutable state is involved.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@spawn&lt;/code&gt;:&lt;/strong&gt; More flexible (can parallelize any code block, not just loops). Dynamic work-stealing scheduler (better load balancing for uneven tasks). Still requires manual synchronization (&lt;code&gt;fetch&lt;/code&gt;) and care with shared mutable state.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Guideline:&lt;/strong&gt; Prefer &lt;code&gt;@threads&lt;/code&gt; for simple, independent, balanced &lt;code&gt;for&lt;/code&gt; loops where writes go to unique locations. For more complex scenarios or when modifying shared state safely, use &lt;code&gt;@spawn&lt;/code&gt; combined with locks or atomics (covered next). Always be extremely vigilant about data races when using &lt;code&gt;@threads&lt;/code&gt; with shared mutable variables.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Multi-Threading", &lt;code&gt;Threads.@threads&lt;/code&gt;:&lt;/strong&gt; Explains the macro and its use cases. Explicitly warns about data races.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;To run the script:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(You MUST start Julia with multiple threads, e.g., &lt;code&gt;julia -t 4 0099_threads_macro.jl&lt;/code&gt;)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia &lt;span class="nt"&gt;-t&lt;/span&gt; 4 0099_threads_macro.jl
&lt;span class="nt"&gt;---&lt;/span&gt; Example 1: Safe Parallel Loop &lt;span class="nt"&gt;---&lt;/span&gt;
Main script on thread: 1
Looping 10 &lt;span class="nb"&gt;times &lt;/span&gt;using 4 threads...
  Iteration 1 running on thread 1
  Iteration 4 running on thread 2
  Iteration 7 running on thread 3
  Iteration 10 running on thread 4
  Iteration 2 running on thread 1
  Iteration 5 running on thread 2
  Iteration 8 running on thread 3
  Iteration 3 running on thread 1
  Iteration 6 running on thread 2
  Iteration 9 running on thread 3
Loop finished.
Results: &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="c"&gt;###, ###, ###, ###, ###, ###, ###, ###, ###, ###]&lt;/span&gt;

&lt;span class="nt"&gt;---&lt;/span&gt; Example 2: Data Race &lt;span class="nt"&gt;---&lt;/span&gt;
Calculating &lt;span class="nb"&gt;sum &lt;/span&gt;incorrectly &lt;span class="o"&gt;(&lt;/span&gt;data race&lt;span class="o"&gt;)&lt;/span&gt;...
Loop finished.
Incorrect Total Sum &lt;span class="o"&gt;(&lt;/span&gt;will be &amp;lt; 1000000&lt;span class="o"&gt;)&lt;/span&gt;: &lt;span class="c"&gt;###.0 # A value significantly less than 1,000,000&lt;/span&gt;
This demonstrates a read-modify-write data race.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(The exact order of iterations per thread may vary. Results &lt;code&gt;###&lt;/code&gt; will be floats. The incorrect total sum will vary between runs but will be less than 1,000,000.)&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Thread Safety Mechanisms
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;0100_thread_safety_locks.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0100_thread_safety_locks.jl&lt;/span&gt;
&lt;span class="c"&gt;# Demonstrates using locks to prevent data races.&lt;/span&gt;
&lt;span class="c"&gt;# Requires running Julia with multiple threads (e.g., 'julia -t 4')&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Threads&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nd"&gt;@spawn&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;ReentrantLock&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unlock&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nthreads&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;fetch&lt;/span&gt;

&lt;span class="c"&gt;# 1. Initialize a lock.&lt;/span&gt;
&lt;span class="c"&gt;#    A lock is a synchronization primitive ensuring mutual exclusion.&lt;/span&gt;
&lt;span class="c"&gt;#    'ReentrantLock' allows the *same* thread to acquire the lock multiple times&lt;/span&gt;
&lt;span class="c"&gt;#    without deadlocking (it must unlock it the same number of times).&lt;/span&gt;
&lt;span class="c"&gt;#    'SpinLock' is lower-level, busy-waiting lock (CPU intensive) for very short critical sections.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;counter_lock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;ReentrantLock&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;

&lt;span class="c"&gt;# Shared mutable state that needs protection&lt;/span&gt;
&lt;span class="n"&gt;total_sum_correct&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;
&lt;span class="n"&gt;num_increments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1_000_000&lt;/span&gt; &lt;span class="c"&gt;# Use a larger number to make races likely&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Correctly calculating sum using lock ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Using &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;(nthreads()) threads for &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;num_increments increments..."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Array to hold Task objects&lt;/span&gt;
&lt;span class="n"&gt;tasks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Vector&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;Task&lt;/span&gt;&lt;span class="x"&gt;}(&lt;/span&gt;&lt;span class="nb"&gt;undef&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;num_increments&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# --- Method 1: Manual lock/unlock with try...finally (Less Preferred) ---&lt;/span&gt;
&lt;span class="c"&gt;# Launch tasks that increment the shared counter safely&lt;/span&gt;
&lt;span class="c"&gt;# for i in 1:num_increments&lt;/span&gt;
&lt;span class="c"&gt;#     tasks[i] = @spawn begin&lt;/span&gt;
&lt;span class="c"&gt;#         # 2. Acquire the lock *before* accessing shared data.&lt;/span&gt;
&lt;span class="c"&gt;#         # If another thread holds the lock, this call blocks.&lt;/span&gt;
&lt;span class="c"&gt;#         lock(counter_lock)&lt;/span&gt;
&lt;span class="c"&gt;#         try&lt;/span&gt;
&lt;span class="c"&gt;#             # --- CRITICAL SECTION START ---&lt;/span&gt;
&lt;span class="c"&gt;#             # Only one thread can execute this code block at a time.&lt;/span&gt;
&lt;span class="c"&gt;#             global total_sum_correct += 1.0&lt;/span&gt;
&lt;span class="c"&gt;#             # --- CRITICAL SECTION END ---&lt;/span&gt;
&lt;span class="c"&gt;#         finally&lt;/span&gt;
&lt;span class="c"&gt;#             # 3. CRITICAL: Release the lock *always*.&lt;/span&gt;
&lt;span class="c"&gt;#             # 'finally' ensures unlock happens even if an error&lt;/span&gt;
&lt;span class="c"&gt;#             # occurs inside the 'try' block, preventing deadlock.&lt;/span&gt;
&lt;span class="c"&gt;#             unlock(counter_lock)&lt;/span&gt;
&lt;span class="c"&gt;#         end&lt;/span&gt;
&lt;span class="c"&gt;#     end&lt;/span&gt;
&lt;span class="c"&gt;# end&lt;/span&gt;

&lt;span class="c"&gt;# --- Method 2: Idiomatic lock(...) do ... end (Recommended) ---&lt;/span&gt;
&lt;span class="c"&gt;# This is syntactic sugar for the try...finally block above.&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;num_increments&lt;/span&gt;
    &lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;@spawn&lt;/span&gt; &lt;span class="k"&gt;begin&lt;/span&gt;
        &lt;span class="c"&gt;# 4. Acquire lock, execute block, guarantee unlock.&lt;/span&gt;
        &lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;counter_lock&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
            &lt;span class="c"&gt;# --- CRITICAL SECTION START ---&lt;/span&gt;
            &lt;span class="c"&gt;# Code here is automatically protected by the lock.&lt;/span&gt;
            &lt;span class="kd"&gt;global&lt;/span&gt; &lt;span class="n"&gt;total_sum_correct&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;
            &lt;span class="c"&gt;# --- CRITICAL SECTION END ---&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="c"&gt;# Lock is automatically released here&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;


&lt;span class="c"&gt;# 5. Wait for all tasks to complete.&lt;/span&gt;
&lt;span class="c"&gt;#    fetch() will block until each task is done.&lt;/span&gt;
&lt;span class="n"&gt;fetch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Using broadcasted fetch&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Loop finished."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# The result should now be exactly equal to num_increments.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Correct Total Sum (with lock): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total_sum_correct&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script demonstrates how to use &lt;strong&gt;locks&lt;/strong&gt; (specifically &lt;code&gt;ReentrantLock&lt;/code&gt;) to prevent the &lt;strong&gt;data race&lt;/strong&gt; identified in the previous lesson when multiple threads modify shared mutable state concurrently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Core Concept: Mutual Exclusion
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Data Race Cause:&lt;/strong&gt; The operation &lt;code&gt;total_sum_correct += 1.0&lt;/code&gt; is not atomic; it involves reading the current value, modifying it, and writing it back. Multiple threads executing these steps concurrently can interfere, leading to lost updates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Solution: Mutual Exclusion:&lt;/strong&gt; We need to ensure that only &lt;strong&gt;one thread at a time&lt;/strong&gt; can execute the code that modifies the shared variable (&lt;code&gt;total_sum_correct&lt;/code&gt;). This protected section of code is called a &lt;strong&gt;critical section&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Locks (Mutexes):&lt;/strong&gt; A lock (also known as a mutex, for MUTual EXclusion) is a synchronization primitive used to enforce mutual exclusion. It acts like a token; only the thread currently holding the token (the lock) is allowed to enter the critical section.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Using Locks in Julia (&lt;code&gt;Threads.ReentrantLock&lt;/code&gt;)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Initialization:&lt;/strong&gt; Create a lock object &lt;strong&gt;once&lt;/strong&gt; for the shared resource you need to protect: &lt;code&gt;const counter_lock = ReentrantLock()&lt;/code&gt;. Make it &lt;code&gt;const&lt;/code&gt; so the reference to the lock itself doesn't change.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Acquiring the Lock:&lt;/strong&gt; Before entering the critical section, a thread must &lt;strong&gt;acquire&lt;/strong&gt; the lock using &lt;code&gt;lock(counter_lock)&lt;/code&gt;.

&lt;ul&gt;
&lt;li&gt;If the lock is available, the thread acquires it and proceeds into the critical section.&lt;/li&gt;
&lt;li&gt;If another thread already holds the lock, the &lt;code&gt;lock()&lt;/code&gt; call &lt;strong&gt;blocks&lt;/strong&gt; the current thread (pauses its execution efficiently) until the lock is released.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Critical Section:&lt;/strong&gt; The code that accesses or modifies the shared mutable state (e.g., &lt;code&gt;global total_sum_correct += 1.0&lt;/code&gt;) is placed &lt;em&gt;after&lt;/em&gt; &lt;code&gt;lock()&lt;/code&gt; and &lt;em&gt;before&lt;/em&gt; &lt;code&gt;unlock()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Releasing the Lock:&lt;/strong&gt; After leaving the critical section, the thread &lt;strong&gt;must release&lt;/strong&gt; the lock using &lt;code&gt;unlock(counter_lock)&lt;/code&gt;. This allows one of the waiting (blocked) threads, if any, to acquire the lock and proceed.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Ensuring Unlock: &lt;code&gt;try...finally&lt;/code&gt; and &lt;code&gt;lock...do&lt;/code&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Danger of Deadlock:&lt;/strong&gt; If a thread acquires a lock and then encounters an error &lt;em&gt;before&lt;/em&gt; it releases the lock, the lock will remain held forever. Any other thread waiting for that lock will block indefinitely, causing a &lt;strong&gt;deadlock&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;try...finally...unlock&lt;/code&gt; (Manual but Safe):&lt;/strong&gt; The standard way to prevent deadlock is to put the critical section code inside a &lt;code&gt;try&lt;/code&gt; block and the &lt;code&gt;unlock()&lt;/code&gt; call inside a &lt;code&gt;finally&lt;/code&gt; block. The &lt;code&gt;finally&lt;/code&gt; block is &lt;strong&gt;guaranteed&lt;/strong&gt; to execute whether the &lt;code&gt;try&lt;/code&gt; block completes normally or throws an error.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;lock(l) do ... end&lt;/code&gt; (Idiomatic and Safest):&lt;/strong&gt; Julia provides syntactic sugar for the &lt;code&gt;try...finally&lt;/code&gt; pattern. The code inside the &lt;code&gt;do ... end&lt;/code&gt; block becomes the critical section. Julia automatically acquires the lock (&lt;code&gt;l&lt;/code&gt;) before executing the block and &lt;strong&gt;guarantees&lt;/strong&gt; that the lock is released when the block finishes, regardless of how it finishes (normal completion or error). &lt;strong&gt;This is the strongly recommended pattern&lt;/strong&gt; as it makes forgetting to unlock impossible.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Performance Impact
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Serialization:&lt;/strong&gt; Locks fundamentally &lt;strong&gt;serialize&lt;/strong&gt; execution through the critical section. Only one thread can be executing that code at any given time. If the critical section is large or frequently contended (many threads trying to acquire the lock often), the lock itself becomes a &lt;strong&gt;performance bottleneck&lt;/strong&gt;, limiting overall parallelism.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Overhead:&lt;/strong&gt; Acquiring and releasing locks involves atomic operations and potentially interaction with the OS scheduler (if blocking occurs), which has non-zero overhead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Guideline (HFT):&lt;/strong&gt; Use locks only when necessary to protect shared state. Keep critical sections &lt;strong&gt;as small and fast as possible&lt;/strong&gt;. Prefer lock-free alternatives (like atomics, covered next) for simple operations like counters if performance is paramount.&lt;/li&gt;
&lt;/ul&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Multi-Threading", "Data race freedom":&lt;/strong&gt; Discusses locks (&lt;code&gt;Mutex&lt;/code&gt;, &lt;code&gt;ReentrantLock&lt;/code&gt;, &lt;code&gt;SpinLock&lt;/code&gt;) as the primary mechanism for protecting shared mutable state.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Base Documentation, &lt;code&gt;ReentrantLock&lt;/code&gt;, &lt;code&gt;lock&lt;/code&gt;, &lt;code&gt;unlock&lt;/code&gt;, &lt;code&gt;lock(f::Function, lock)&lt;/code&gt;:&lt;/strong&gt; Details the lock types and functions, including the &lt;code&gt;do&lt;/code&gt; block syntax.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;To run the script:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(You MUST start Julia with multiple threads, e.g., &lt;code&gt;julia -t 4 0100_thread_safety_locks.jl&lt;/code&gt;)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia &lt;span class="nt"&gt;-t&lt;/span&gt; 4 0100_thread_safety_locks.jl
&lt;span class="nt"&gt;---&lt;/span&gt; Correctly calculating &lt;span class="nb"&gt;sum &lt;/span&gt;using lock &lt;span class="nt"&gt;---&lt;/span&gt;
Using 4 threads &lt;span class="k"&gt;for &lt;/span&gt;1000000 increments...
Loop finished.
Correct Total Sum &lt;span class="o"&gt;(&lt;/span&gt;with lock&lt;span class="o"&gt;)&lt;/span&gt;: 1000000.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(The result should now consistently be exactly 1,000,000.0, demonstrating that the lock correctly prevented the data race.)&lt;/em&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;0101_atomics_lock_free.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0101_atomics_lock_free.jl&lt;/span&gt;
&lt;span class="c"&gt;# Demonstrates Atomic types for lock-free thread safety.&lt;/span&gt;
&lt;span class="c"&gt;# Requires running Julia with multiple threads (e.g., 'julia -t 4')&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Threads&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nd"&gt;@spawn&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Atomic&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;atomic_add!&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;atomic_cas!&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nthreads&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;fetch&lt;/span&gt;

&lt;span class="c"&gt;# 1. Create an Atomic integer.&lt;/span&gt;
&lt;span class="c"&gt;#    'Atomic{T}' is a wrapper around a value of type 'T' (must be primitive bits type).&lt;/span&gt;
&lt;span class="c"&gt;#    It guarantees that operations performed via atomic functions are indivisible.&lt;/span&gt;
&lt;span class="c"&gt;#    Initialize it with 0.&lt;/span&gt;
&lt;span class="n"&gt;total_atomic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Atomic&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="x"&gt;}(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;num_increments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1_000_000&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Correctly calculating sum using Atomics (Lock-Free) ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Using &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;(nthreads()) threads for &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;num_increments increments..."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Array to hold Task objects&lt;/span&gt;
&lt;span class="n"&gt;tasks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Vector&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;Task&lt;/span&gt;&lt;span class="x"&gt;}(&lt;/span&gt;&lt;span class="nb"&gt;undef&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;num_increments&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Launch tasks that increment the atomic counter&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;num_increments&lt;/span&gt;
    &lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;@spawn&lt;/span&gt; &lt;span class="k"&gt;begin&lt;/span&gt;
        &lt;span class="c"&gt;# 2. Perform an atomic Read-Modify-Write operation.&lt;/span&gt;
        &lt;span class="c"&gt;#    'atomic_add!(ref, value)' adds 'value' to the current value&lt;/span&gt;
        &lt;span class="c"&gt;#    in 'ref' atomically. This compiles to a single, thread-safe&lt;/span&gt;
        &lt;span class="c"&gt;#    CPU instruction (like 'lock xadd' on x86).&lt;/span&gt;
        &lt;span class="c"&gt;#    There is NO lock, NO blocking. All threads proceed, and the&lt;/span&gt;
        &lt;span class="c"&gt;#    hardware ensures the additions are correct.&lt;/span&gt;
        &lt;span class="n"&gt;atomic_add!&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total_atomic&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 3. Wait for all tasks to complete.&lt;/span&gt;
&lt;span class="n"&gt;fetch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 4. Read the final value from the Atomic object.&lt;/span&gt;
&lt;span class="c"&gt;#    'atomic_ref[]' is the syntax for atomically reading the current value.&lt;/span&gt;
&lt;span class="n"&gt;final_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;total_atomic&lt;/span&gt;&lt;span class="x"&gt;[]&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Loop finished."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Correct Atomic Total Sum: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;final_value&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Should be exactly 1,000,000&lt;/span&gt;


&lt;span class="c"&gt;# --- Compare-And-Swap (CAS) ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Demonstrating Compare-And-Swap (CAS) ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 5. CAS is the fundamental atomic primitive.&lt;/span&gt;
&lt;span class="c"&gt;#    'atomic_cas!(ref, expected_old, new_value)' performs:&lt;/span&gt;
&lt;span class="c"&gt;#    Atomically:&lt;/span&gt;
&lt;span class="c"&gt;#      a) Read the current value in 'ref'.&lt;/span&gt;
&lt;span class="c"&gt;#      b) Compare it with 'expected_old'.&lt;/span&gt;
&lt;span class="c"&gt;#      c) If they match, write 'new_value' into 'ref' and return 'expected_old'.&lt;/span&gt;
&lt;span class="c"&gt;#      d) If they don't match (meaning another thread changed it), do nothing&lt;/span&gt;
&lt;span class="c"&gt;#         and return the value that was actually read.&lt;/span&gt;

&lt;span class="c"&gt;#    It allows building complex lock-free logic by retrying if a conflict occurs.&lt;/span&gt;

&lt;span class="n"&gt;current_val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;total_atomic&lt;/span&gt;&lt;span class="x"&gt;[]&lt;/span&gt; &lt;span class="c"&gt;# Read current value (1,000,000)&lt;/span&gt;
&lt;span class="n"&gt;expected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current_val&lt;/span&gt;
&lt;span class="n"&gt;desired_new&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current_val&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Current atomic value: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;current_val&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Attempting CAS: Expected=&lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;expected, New=&lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;desired_new"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Perform the CAS operation&lt;/span&gt;
&lt;span class="n"&gt;old_val_read&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;atomic_cas!&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total_atomic&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;desired_new&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Value returned by CAS: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;old_val_read&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 6. Check if CAS succeeded.&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;old_val_read&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"CAS successful!"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"New atomic value: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total_atomic&lt;/span&gt;&lt;span class="x"&gt;[])&lt;/span&gt; &lt;span class="c"&gt;# Should be 1,000,100&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"CAS failed! Another thread likely modified the value."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Current atomic value remains: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total_atomic&lt;/span&gt;&lt;span class="x"&gt;[])&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# Example of a failing CAS (if another thread hypothetically interfered)&lt;/span&gt;
&lt;span class="c"&gt;# Let's manually set 'expected' to something wrong&lt;/span&gt;
&lt;span class="n"&gt;expected_wrong&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current_val&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Attempting CAS with wrong expected value: Expected=&lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;expected_wrong, New=0"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;old_val_read_fail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;atomic_cas!&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total_atomic&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expected_wrong&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Value returned by failing CAS: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;old_val_read_fail&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Will be the actual value (1M or 1M+100)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;old_val_read_fail&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;expected_wrong&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"CAS successful (unexpected!)."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"CAS failed as expected."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Atomic value is unchanged: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total_atomic&lt;/span&gt;&lt;span class="x"&gt;[])&lt;/span&gt; &lt;span class="c"&gt;# Still 1M or 1M+100&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces &lt;strong&gt;Atomic types&lt;/strong&gt; (&lt;code&gt;Threads.Atomic{T}&lt;/code&gt;) and &lt;strong&gt;atomic operations&lt;/strong&gt;, which provide a &lt;strong&gt;lock-free&lt;/strong&gt; mechanism for ensuring thread safety for simple operations like counters and flags. They are generally much faster than locks for these specific use cases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Core Concept: Atomicity
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Problem with Locks:&lt;/strong&gt; Locks serialize access to critical sections, potentially causing threads to block and wait, creating performance bottlenecks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Atomic Operations:&lt;/strong&gt; These are special operations guaranteed by the CPU hardware to execute &lt;strong&gt;indivisibly&lt;/strong&gt; (atomically). When Thread A performs &lt;code&gt;atomic_add!&lt;/code&gt;, no other thread (Thread B) can interfere &lt;em&gt;during&lt;/em&gt; that add operation. Thread B might execute its own &lt;code&gt;atomic_add!&lt;/code&gt; immediately before or after Thread A's, but they cannot corrupt each other's read-modify-write sequence.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lock-Free:&lt;/strong&gt; Code using atomics is often "lock-free" because threads generally do not need to block and wait for a lock. They attempt the atomic operation directly. If there's contention, the hardware manages the conflict at the nanosecond level, which is vastly faster than OS-level thread blocking managed by locks.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Using Atomics in Julia (&lt;code&gt;Threads.Atomic&lt;/code&gt;)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Declaration:&lt;/strong&gt; Create an atomic variable using &lt;code&gt;Threads.Atomic{T}(initial_value)&lt;/code&gt;, where &lt;code&gt;T&lt;/code&gt; must be a primitive &lt;code&gt;isbits&lt;/code&gt; type (like &lt;code&gt;Int&lt;/code&gt;, &lt;code&gt;UInt64&lt;/code&gt;, &lt;code&gt;Bool&lt;/code&gt;, &lt;code&gt;Float32&lt;/code&gt;, &lt;code&gt;Float64&lt;/code&gt;). Example: &lt;code&gt;total_atomic = Atomic{Int}(0)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Atomic Read-Modify-Write:&lt;/strong&gt; Use specific atomic functions to modify the value safely:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;atomic_add!(ref::Atomic{T}, val::T)&lt;/code&gt;:&lt;/strong&gt; Atomically adds &lt;code&gt;val&lt;/code&gt; to the value in &lt;code&gt;ref&lt;/code&gt;. Returns the &lt;em&gt;old&lt;/em&gt; value.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;atomic_sub!(ref::Atomic{T}, val::T)&lt;/code&gt;:&lt;/strong&gt; Atomically subtracts &lt;code&gt;val&lt;/code&gt;. Returns the &lt;em&gt;old&lt;/em&gt; value.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;atomic_xchg!(ref::Atomic{T}, new::T)&lt;/code&gt;:&lt;/strong&gt; Atomically sets the value in &lt;code&gt;ref&lt;/code&gt; to &lt;code&gt;new&lt;/code&gt;. Returns the &lt;em&gt;old&lt;/em&gt; value.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;atomic_cas!(ref::Atomic{T}, expected::T, new::T)&lt;/code&gt;:&lt;/strong&gt; Compare-And-Swap (see below). Returns the &lt;em&gt;old&lt;/em&gt; value read from &lt;code&gt;ref&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;(Others exist: &lt;code&gt;atomic_and!&lt;/code&gt;, &lt;code&gt;atomic_or!&lt;/code&gt;, &lt;code&gt;atomic_xor!&lt;/code&gt;, &lt;code&gt;atomic_max!&lt;/code&gt;, &lt;code&gt;atomic_min!&lt;/code&gt;)&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Atomic Read:&lt;/strong&gt; To read the current value atomically, use array-like indexing: &lt;code&gt;current_val = atomic_ref[]&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Atomic Write:&lt;/strong&gt; To write a new value atomically (overwriting the old), use &lt;code&gt;atomic_xchg!&lt;/code&gt; or &lt;code&gt;atomic_ref[] = new_value&lt;/code&gt; (assignment is often overloaded for atomic write).&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Compare-And-Swap (CAS)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;atomic_cas!(ref, expected, new)&lt;/code&gt;:&lt;/strong&gt; This is the &lt;strong&gt;fundamental building block&lt;/strong&gt; of most complex lock-free algorithms (like queues, stacks, linked lists).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Operation:&lt;/strong&gt; It tries to atomically change the value in &lt;code&gt;ref&lt;/code&gt; from &lt;code&gt;expected&lt;/code&gt; to &lt;code&gt;new&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Success/Failure:&lt;/strong&gt; It &lt;strong&gt;succeeds only if&lt;/strong&gt; the value in &lt;code&gt;ref&lt;/code&gt; is &lt;em&gt;exactly&lt;/em&gt; equal to &lt;code&gt;expected&lt;/code&gt; at the moment of the operation. If another thread changed the value between when you read it (&lt;code&gt;expected = ref[]&lt;/code&gt;) and when you called &lt;code&gt;atomic_cas!&lt;/code&gt;, the CAS operation fails (doesn't write &lt;code&gt;new&lt;/code&gt;) and returns the &lt;em&gt;current, different&lt;/em&gt; value it found.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Retry Loops:&lt;/strong&gt; Lock-free algorithms often use CAS in a loop:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;atomic_ref&lt;/span&gt;&lt;span class="x"&gt;[]&lt;/span&gt;
&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
    &lt;span class="n"&gt;desired&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;calculate_new_value&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# Try to swap 'current' with 'desired'&lt;/span&gt;
    &lt;span class="n"&gt;read_val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;atomic_cas!&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;atomic_ref&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;desired&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;read_val&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;
        &lt;span class="c"&gt;# Success! Our change went through.&lt;/span&gt;
        &lt;span class="n"&gt;break&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="c"&gt;# Failure! Another thread interfered. Retry with the new value.&lt;/span&gt;
        &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;read_val&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Performance (vs. Locks)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;For simple updates like incrementing a counter (&lt;code&gt;atomic_add!&lt;/code&gt;), atomics are &lt;strong&gt;significantly faster&lt;/strong&gt; than using a lock (&lt;code&gt;lock(l) do ... end&lt;/code&gt;). They avoid the overhead of lock acquisition/release and potential thread blocking.&lt;/li&gt;
&lt;li&gt;For complex updates involving multiple variables, locks are often easier to reason about and implement correctly than complex CAS-based lock-free algorithms.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Guideline (HFT):&lt;/strong&gt; Use atomics for high-frequency counters, flags, sequence numbers, or simple state management where lock contention would be a bottleneck. Use locks for protecting more complex data structures or operations involving multiple steps.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Multi-Threading", "Atomic Operations":&lt;/strong&gt; Introduces &lt;code&gt;Atomic&lt;/code&gt; types and atomic functions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Base Documentation, &lt;code&gt;Threads.Atomic&lt;/code&gt;, &lt;code&gt;Threads.atomic_...&lt;/code&gt; functions:&lt;/strong&gt; Detailed API descriptions.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;To run the script:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(You MUST start Julia with multiple threads, e.g., &lt;code&gt;julia -t 4 0101_atomics_lock_free.jl&lt;/code&gt;)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia &lt;span class="nt"&gt;-t&lt;/span&gt; 4 0101_atomics_lock_free.jl
&lt;span class="nt"&gt;---&lt;/span&gt; Correctly calculating &lt;span class="nb"&gt;sum &lt;/span&gt;using Atomics &lt;span class="o"&gt;(&lt;/span&gt;Lock-Free&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
Using 4 threads &lt;span class="k"&gt;for &lt;/span&gt;1000000 increments...
Loop finished.
Correct Atomic Total Sum: 1000000

&lt;span class="nt"&gt;---&lt;/span&gt; Demonstrating Compare-And-Swap &lt;span class="o"&gt;(&lt;/span&gt;CAS&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
Current atomic value: 1000000
Attempting CAS: &lt;span class="nv"&gt;Expected&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1000000, &lt;span class="nv"&gt;New&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1000100
Value returned by CAS: 1000000
CAS successful!
New atomic value: 1000100

Attempting CAS with wrong expected value: &lt;span class="nv"&gt;Expected&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;999999, &lt;span class="nv"&gt;New&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
Value returned by failing CAS: 1000100
CAS failed as expected.
Atomic value is unchanged: 1000100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(The final result should consistently be 1,000,000, demonstrating lock-free correctness. CAS results should match the logic.)&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Appendix: Deeper Dive into Atomics
&lt;/h2&gt;

&lt;p&gt;The main script introduced &lt;code&gt;Atomic{T}&lt;/code&gt; types and basic operations like &lt;code&gt;atomic_add!&lt;/code&gt; and &lt;code&gt;atomic_cas!&lt;/code&gt;. This appendix explores some crucial details, patterns, and potential pitfalls for using atomics effectively in high-performance, multi-threaded code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Recap: Why Atomics Over Locks?
&lt;/h3&gt;

&lt;p&gt;Locks provide &lt;strong&gt;mutual exclusion&lt;/strong&gt; by forcing threads to wait, serializing access to critical sections. This is robust but can become a bottleneck if contention is high (many threads frequently trying to acquire the lock).&lt;/p&gt;

&lt;p&gt;Atomics leverage special &lt;strong&gt;CPU instructions&lt;/strong&gt; that perform simple operations (like read, write, add, swap) &lt;strong&gt;indivisibly&lt;/strong&gt;. They allow multiple threads to attempt operations concurrently, with the hardware managing conflicts at a very low level. For simple, highly contended updates (like incrementing a shared counter), atomics are often &lt;strong&gt;significantly faster&lt;/strong&gt; than locks because they avoid the overhead of lock management and thread blocking.&lt;/p&gt;

&lt;h3&gt;
  
  
  Memory Orderings: The Hidden Complexity
&lt;/h3&gt;

&lt;p&gt;Atomicity isn't just about indivisibility; it's also about &lt;strong&gt;memory ordering&lt;/strong&gt;. This refers to the guarantees an atomic operation provides about how its effects (reads and writes) become visible to other threads relative to other memory operations. Modern CPUs and compilers aggressively reorder memory operations for performance, and atomic operations act as "fences" to prevent undesirable reorderings.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sequential Consistency (&lt;code&gt;:sequentially_consistent&lt;/code&gt;):&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;This is the &lt;strong&gt;default memory order&lt;/strong&gt; for all atomic operations in Julia (&lt;code&gt;atomic_add!&lt;/code&gt;, &lt;code&gt;atomic_cas!&lt;/code&gt;, &lt;code&gt;atomic_store!&lt;/code&gt;, &lt;code&gt;atomic_load&lt;/code&gt;, etc., unless specified otherwise).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Guarantee:&lt;/strong&gt; It provides the strongest guarantees. All threads agree on a single, global sequential order of operations, consistent with the program's source code order. Operations cannot be reordered across a sequentially consistent atomic operation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Analogy:&lt;/strong&gt; Imagine a single, global logbook. Every atomic operation is written into this logbook in a definitive order visible to everyone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance:&lt;/strong&gt; This is the easiest to reason about but potentially the slowest, as it imposes the most constraints on the CPU and compiler, potentially requiring expensive memory fence instructions.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Relaxed Orderings (&lt;code&gt;:acquire&lt;/code&gt;, &lt;code&gt;:release&lt;/code&gt;, &lt;code&gt;:relaxed&lt;/code&gt;):&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Julia also allows specifying weaker memory orderings as optional arguments to atomic functions (e.g., &lt;code&gt;atomic_load(ref, :acquire)&lt;/code&gt;, &lt;code&gt;atomic_store!(ref, val, :release)&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;:acquire:&lt;/strong&gt; Ensures that memory reads/writes &lt;em&gt;after&lt;/em&gt; the atomic load are not reordered to happen &lt;em&gt;before&lt;/em&gt; it. Used when acquiring a "lock" or reading data dependent on a flag.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;:release:&lt;/strong&gt; Ensures that memory reads/writes &lt;em&gt;before&lt;/em&gt; the atomic store are not reordered to happen &lt;em&gt;after&lt;/em&gt; it. Used when releasing a "lock" or signaling that data is ready.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;☺️&lt;/strong&gt; Provides no ordering guarantees beyond the atomicity of the operation itself. Fastest, but extremely difficult to use correctly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Warning:&lt;/strong&gt; Using relaxed memory orderings is &lt;strong&gt;expert-level territory&lt;/strong&gt;. Incorrect use &lt;em&gt;will&lt;/em&gt; lead to subtle, non-deterministic data races that are nearly impossible to debug. &lt;strong&gt;Stick to the default sequential consistency unless profiling explicitly identifies atomic operations as a bottleneck AND you thoroughly understand the memory model of your target architecture.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Common Use Cases for Atomics
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;High-Performance Counters:&lt;/strong&gt; The canonical example (&lt;code&gt;atomic_add!&lt;/code&gt;, &lt;code&gt;atomic_sub!&lt;/code&gt;). Massively faster than a locked counter under high contention.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Flags and Status Indicators:&lt;/strong&gt; Signaling state changes between threads.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Atomic&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="x"&gt;}(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# 0=Idle, 1=Running, 2=Stopping&lt;/span&gt;
&lt;span class="c"&gt;# Worker task:&lt;/span&gt;
&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="x"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="c"&gt;# Atomic read&lt;/span&gt;
    &lt;span class="c"&gt;# wait&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="x"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="c"&gt;# do work&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="c"&gt;# Main task:&lt;/span&gt;
&lt;span class="n"&gt;atomic_store!&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Signal workers to start (or atomic_xchg!)&lt;/span&gt;
&lt;span class="c"&gt;# ... later ...&lt;/span&gt;
&lt;span class="n"&gt;atomic_store!&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Signal workers to stop&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Generating Unique Sequence Numbers/IDs:&lt;/strong&gt; A simple global counter incremented with &lt;code&gt;atomic_add!(counter, 1)&lt;/code&gt; can safely generate unique IDs across multiple threads.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Simple Statistics:&lt;/strong&gt; Accumulating sums or finding maximums/minimums across threads (&lt;code&gt;atomic_add!&lt;/code&gt;, &lt;code&gt;atomic_max!&lt;/code&gt;, &lt;code&gt;atomic_min!&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Building Blocks for Lock-Free Data Structures:&lt;/strong&gt; &lt;code&gt;atomic_cas!&lt;/code&gt; is the primitive used to implement complex lock-free algorithms like queues (e.g., Michael-Scott queue), stacks, and sets. &lt;strong&gt;Caution:&lt;/strong&gt; Implementing these correctly is extremely challenging. Prefer using existing, well-tested library implementations (often from external packages or potentially future standard library additions) unless absolutely necessary.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The ABA Problem: A Subtle CAS Pitfall
&lt;/h3&gt;

&lt;p&gt;Naive use of Compare-And-Swap in retry loops can suffer from the &lt;strong&gt;ABA problem&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Scenario:&lt;/strong&gt;

&lt;ol&gt;
&lt;li&gt; Thread 1 reads a value &lt;code&gt;A&lt;/code&gt; from an atomic reference &lt;code&gt;ref&lt;/code&gt;. (&lt;code&gt;expected = A&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt; Thread 1 gets preempted.&lt;/li&gt;
&lt;li&gt; Thread 2 acquires &lt;code&gt;ref&lt;/code&gt;, changes the value from &lt;code&gt;A&lt;/code&gt; to &lt;code&gt;B&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; Thread 2 performs more work, then changes the value in &lt;code&gt;ref&lt;/code&gt; &lt;em&gt;back&lt;/em&gt; to &lt;code&gt;A&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; Thread 1 resumes. It calculates its desired &lt;code&gt;new_value&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; Thread 1 executes &lt;code&gt;atomic_cas!(ref, expected, new_value)&lt;/code&gt;. Since &lt;code&gt;expected&lt;/code&gt; is &lt;code&gt;A&lt;/code&gt; and the current value in &lt;code&gt;ref&lt;/code&gt; &lt;em&gt;is&lt;/em&gt; &lt;code&gt;A&lt;/code&gt;, the CAS &lt;strong&gt;succeeds&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Problem:&lt;/strong&gt; Thread 1 assumes the state associated with &lt;code&gt;A&lt;/code&gt; hasn't changed because the value &lt;code&gt;A&lt;/code&gt; is the same. However, the underlying state &lt;em&gt;was&lt;/em&gt; modified (A -&amp;gt; B -&amp;gt; A). This can corrupt data structures where the value &lt;code&gt;A&lt;/code&gt; might be, for example, a pointer that was freed and reallocated, now pointing to something different but coincidentally having the same address bits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Solutions:&lt;/strong&gt; Often involve techniques like:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tagged Pointers:&lt;/strong&gt; Storing a "tag" or counter alongside the pointer within the same atomic word, so A -&amp;gt; B -&amp;gt; A becomes A1 -&amp;gt; B -&amp;gt; A2. The CAS on A1 fails.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sequence Locks/Counters:&lt;/strong&gt; Using separate atomic counters to track modifications.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Takeaway:&lt;/strong&gt; Be aware of this problem if implementing complex CAS-based logic. It's another reason to favor library implementations.&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Performance Considerations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Contention:&lt;/strong&gt; While faster than locks, atomics are not free. Under extreme contention (many cores constantly trying to modify the same atomic variable), the CPU's cache coherency protocols and atomic instructions themselves can become a bottleneck on the memory bus. Performance may not scale linearly with the number of cores.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;False Sharing:&lt;/strong&gt; This occurs when unrelated variables happen to reside on the &lt;strong&gt;same CPU cache line&lt;/strong&gt; (typically 64 bytes).

&lt;ul&gt;
&lt;li&gt;Thread A modifies &lt;code&gt;atomic_var_1&lt;/code&gt;. This forces the cache line containing &lt;code&gt;atomic_var_1&lt;/code&gt; to be invalidated in Thread B's cache.&lt;/li&gt;
&lt;li&gt;Thread B modifies &lt;code&gt;atomic_var_2&lt;/code&gt; (which is nearby in memory, on the same cache line). This forces the cache line to be invalidated in Thread A's cache.&lt;/li&gt;
&lt;li&gt;Even though the threads are accessing &lt;em&gt;different&lt;/em&gt; variables, they constantly invalidate each other's caches because the variables share a cache line. This causes significant performance degradation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Solution:&lt;/strong&gt; Ensure frequently accessed atomic variables used by different threads are sufficiently padded apart in memory (e.g., by placing them in different structs or adding unused padding fields) so they don't share a cache line. &lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Summary and Guidance
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Atomics offer a high-performance, lock-free way to manage simple shared state updates (counters, flags, etc.).&lt;/li&gt;
&lt;li&gt;They are significantly faster than locks under high contention for these specific use cases.&lt;/li&gt;
&lt;li&gt;Always use the default &lt;strong&gt;sequential consistency&lt;/strong&gt; memory ordering unless you have proven a need for relaxed orderings via profiling and fully understand the implications.&lt;/li&gt;
&lt;li&gt;Be aware of the &lt;strong&gt;ABA problem&lt;/strong&gt; if implementing complex logic with &lt;code&gt;atomic_cas!&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Consider &lt;strong&gt;false sharing&lt;/strong&gt; if benchmarking reveals unexpected scaling issues with multiple atomic variables.&lt;/li&gt;
&lt;li&gt;For complex data structures or operations involving multiple variables, &lt;strong&gt;locks are often simpler and safer&lt;/strong&gt; to implement correctly than intricate lock-free algorithms.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Choose the right tool for the job: atomics for simple, high-contention points; locks for broader or more complex critical sections. Always prioritize correctness.&lt;/p&gt;




&lt;h2&gt;
  
  
  Multi Processing
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;0102_distributed_processing.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0102_distributed_processing.jl&lt;/span&gt;
&lt;span class="c"&gt;# Introduces Distributed.jl for multi-processing.&lt;/span&gt;
&lt;span class="c"&gt;# MUST BE RUN WITH: julia -p N (e.g., julia -p 4)&lt;/span&gt;

&lt;span class="c"&gt;# 1. Import the Distributed standard library.&lt;/span&gt;
&lt;span class="c"&gt;#    '-p N' starts Julia with N additional "worker" processes.&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Distributed&lt;/span&gt;

&lt;span class="c"&gt;# --- Setup and Process IDs ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Distributed Processing Setup ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 2. Check the number of available processes.&lt;/span&gt;
&lt;span class="n"&gt;num_procs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Distributed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nprocs&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt; &lt;span class="c"&gt;# Total number of processes (main + workers)&lt;/span&gt;
&lt;span class="n"&gt;num_workers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Distributed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nworkers&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt; &lt;span class="c"&gt;# Number of worker processes only&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Total processes (main + workers): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;num_procs&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Number of worker processes: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;num_workers&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;num_procs&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"WARNING: No worker processes found."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Restart Julia with the '-p N' flag (e.g., 'julia -p 4')"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# Exit cleanly if no workers, as subsequent code requires them.&lt;/span&gt;
    &lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 3. Get process IDs.&lt;/span&gt;
&lt;span class="n"&gt;main_pid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Distributed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;myid&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;      &lt;span class="c"&gt;# ID of the *current* process (always 1 for the main script)&lt;/span&gt;
&lt;span class="n"&gt;worker_pids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Distributed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;workers&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt; &lt;span class="c"&gt;# Vector of worker process IDs (e.g., [2, 3, 4, 5])&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Main process ID: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;main_pid&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Worker process IDs: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;worker_pids&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# --- Executing Code Remotely ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Remote Execution ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 4. Define code that needs to exist on *all* processes using '@everywhere'.&lt;/span&gt;
&lt;span class="c"&gt;#    Worker processes start with a clean slate; they don't inherit&lt;/span&gt;
&lt;span class="c"&gt;#    definitions from the main process unless explicitly told.&lt;/span&gt;
&lt;span class="n"&gt;Distributed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nd"&gt;@everywhere&lt;/span&gt; &lt;span class="k"&gt;begin&lt;/span&gt;
    &lt;span class="c"&gt;# This block is executed on the main process AND all workers.&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Sockets&lt;/span&gt; &lt;span class="c"&gt;# Make Sockets available on workers if needed inside function&lt;/span&gt;
    &lt;span class="n"&gt;MY_CONSTANT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;

    &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; get_info&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;pid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Distributed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;myid&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Sockets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gethostname&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;thread_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Threads&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;threadid&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt; &lt;span class="c"&gt;# Each process has at least one thread&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"Process &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;pid on host '&lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;host' (thread &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;thread_id) knows MY_CONSTANT = &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;MY_CONSTANT"&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 5. Execute a function remotely on a specific worker using '@spawnat'.&lt;/span&gt;
&lt;span class="c"&gt;#    '@spawnat worker_pid expression' runs the expression on that worker.&lt;/span&gt;
&lt;span class="c"&gt;#    It returns a 'Future', which is a handle to the remote result.&lt;/span&gt;
&lt;span class="n"&gt;target_worker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;worker_pids&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt; &lt;span class="c"&gt;# e.g., process 2&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Spawning task on worker &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;target_worker..."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;future&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Distributed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nd"&gt;@spawnat&lt;/span&gt; &lt;span class="n"&gt;target_worker&lt;/span&gt; &lt;span class="n"&gt;get_info&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;

&lt;span class="c"&gt;# 6. Retrieve the result from the remote worker using 'fetch()'.&lt;/span&gt;
&lt;span class="c"&gt;#    'fetch(future)' blocks until the remote task completes and sends&lt;/span&gt;
&lt;span class="c"&gt;#    its result back to the main process (involves serialization).&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Waiting for result from worker &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;target_worker..."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fetch&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;future&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Result from worker &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;target_worker: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;result&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# --- Parallel Map-Reduce Across Processes ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Distributed Map-Reduce (@distributed) ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;N&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Calculating sum of squares from 1 to &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;N across workers..."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 7. Use '@distributed (reducer) for ... end' for parallel loops.&lt;/span&gt;
&lt;span class="c"&gt;#    This divides the loop iterations among the *worker* processes.&lt;/span&gt;
&lt;span class="c"&gt;#    Each worker computes its portion, and the results are combined&lt;/span&gt;
&lt;span class="c"&gt;#    using the specified 'reducer' function (e.g., '+').&lt;/span&gt;
&lt;span class="c"&gt;#    Data dependencies must be explicitly handled (e.g., using @everywhere).&lt;/span&gt;
&lt;span class="c"&gt;#    The loop variable 'i' is automatically sent to the worker.&lt;/span&gt;
&lt;span class="c"&gt;#    NOTE: Unlike Threads.@threads, this does NOT run on the main process (ID 1).&lt;/span&gt;
&lt;span class="n"&gt;final_sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Distributed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nd"&gt;@distributed&lt;/span&gt; &lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;
    &lt;span class="c"&gt;# This code block runs on a worker process.&lt;/span&gt;
    &lt;span class="n"&gt;pid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Distributed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;myid&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Worker &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;pid processing i = &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;i"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# Return the value for this iteration to be reduced&lt;/span&gt;
    &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="c"&gt;# Main process blocks here until all workers finish and reduction completes.&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Distributed loop finished."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Final sum of squares: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;final_sum&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces &lt;strong&gt;&lt;code&gt;Distributed.jl&lt;/code&gt;&lt;/strong&gt;, Julia's standard library for &lt;strong&gt;multi-processing&lt;/strong&gt;. This contrasts with multi-threading by using separate &lt;strong&gt;OS processes&lt;/strong&gt;, each with its own independent memory space, enabling parallelism that can scale beyond a single machine and provides memory isolation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Core Concepts: Threads vs. Processes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Multi-Threading (&lt;code&gt;Threads&lt;/code&gt;, Module 10):&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pros:&lt;/strong&gt; Runs within a &lt;strong&gt;single process&lt;/strong&gt;, allowing &lt;strong&gt;direct sharing of memory&lt;/strong&gt;. Communication is extremely fast (just read/write variables). Low overhead to start tasks (&lt;code&gt;@spawn&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cons:&lt;/strong&gt; Requires careful &lt;strong&gt;thread safety&lt;/strong&gt; (locks, atomics) to prevent data races. A crash in one thread can bring down the entire process. Limited to the cores on a single machine.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Multi-Processing (&lt;code&gt;Distributed&lt;/code&gt;, This Lesson):&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pros:&lt;/strong&gt; Runs in &lt;strong&gt;multiple, separate processes&lt;/strong&gt;. Provides complete &lt;strong&gt;memory isolation&lt;/strong&gt; (no data races possible on standard variables). A crash in one worker process does not affect others. Can scale across &lt;strong&gt;multiple machines&lt;/strong&gt; over a network (though this example uses local processes).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cons:&lt;/strong&gt; &lt;strong&gt;Communication is expensive&lt;/strong&gt;. Passing data between processes requires &lt;strong&gt;serialization&lt;/strong&gt; (converting objects to a byte stream), network/inter-process communication (IPC), and &lt;strong&gt;deserialization&lt;/strong&gt;. High overhead to start worker processes (&lt;code&gt;julia -p N&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Guideline (HFT):&lt;/strong&gt; Use &lt;code&gt;Threads&lt;/code&gt; for low-latency, tightly coupled computations on a single machine where shared memory performance is critical (e.g., parallel signal processing within one market data handler). Use &lt;code&gt;Distributed&lt;/code&gt; for higher-level task parallelism where memory isolation is desired, fault tolerance is needed, or scaling across machines is required (e.g., running independent strategy simulations, connecting to different exchange gateways in separate processes).&lt;/p&gt;

&lt;h2&gt;
  
  
  Using &lt;code&gt;Distributed.jl&lt;/code&gt;
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Launch Workers (&lt;code&gt;julia -p N&lt;/code&gt;):&lt;/strong&gt; You &lt;strong&gt;must&lt;/strong&gt; start Julia with the &lt;code&gt;-p N&lt;/code&gt; flag (e.g., &lt;code&gt;julia -p 4&lt;/code&gt;) to create &lt;code&gt;N&lt;/code&gt; additional worker processes alongside the main interactive process (Process 1). Alternatively, use &lt;code&gt;Distributed.addprocs(N)&lt;/code&gt; programmatically (less common for script-based work).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Process IDs:&lt;/strong&gt; &lt;code&gt;Distributed.nprocs()&lt;/code&gt; gives the total count (main + workers). &lt;code&gt;Distributed.nworkers()&lt;/code&gt; gives just the worker count. &lt;code&gt;Distributed.myid()&lt;/code&gt; returns the ID of the current process. &lt;code&gt;Distributed.workers()&lt;/code&gt; returns a list of worker IDs (usually &lt;code&gt;[2, 3, ..., N+1]&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Code on Workers (&lt;code&gt;@everywhere&lt;/code&gt;):&lt;/strong&gt; Worker processes start "empty." They don't inherit code definitions or variable values from Process 1. The &lt;code&gt;Distributed.@everywhere begin ... end&lt;/code&gt; block ensures the enclosed code (module imports, function definitions, constant assignments) is executed on &lt;strong&gt;all&lt;/strong&gt; processes (main + workers), making it available everywhere.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Remote Execution (&lt;code&gt;@spawnat&lt;/code&gt;):&lt;/strong&gt; &lt;code&gt;Distributed.@spawnat worker_id expression&lt;/code&gt; executes the &lt;code&gt;expression&lt;/code&gt; specifically on the worker process with ID &lt;code&gt;worker_id&lt;/code&gt;. It returns a &lt;code&gt;Future&lt;/code&gt;, which is a remote reference to the task.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Fetching Remote Results (&lt;code&gt;fetch&lt;/code&gt;):&lt;/strong&gt; &lt;code&gt;fetch(future)&lt;/code&gt; waits for the remote task referenced by &lt;code&gt;future&lt;/code&gt; to complete and then transfers its result back to the calling process (involving serialization/deserialization).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Parallel Loop (&lt;code&gt;@distributed&lt;/code&gt;):&lt;/strong&gt; &lt;code&gt;Distributed.@distributed (reducer) for ... end&lt;/code&gt; provides a parallel map-reduce pattern across &lt;strong&gt;worker processes&lt;/strong&gt;.

&lt;ul&gt;
&lt;li&gt;It divides the loop iterations among the workers.&lt;/li&gt;
&lt;li&gt;Each worker executes the loop body for its assigned iterations.&lt;/li&gt;
&lt;li&gt;The values returned by each iteration on each worker are collected.&lt;/li&gt;
&lt;li&gt;The specified &lt;code&gt;reducer&lt;/code&gt; function (e.g., &lt;code&gt;+&lt;/code&gt;, &lt;code&gt;vcat&lt;/code&gt;, &lt;code&gt;append!&lt;/code&gt;) is used to combine the results from all workers into a final result returned on the main process.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Communication Overhead
&lt;/h2&gt;

&lt;p&gt;Remember that any data sent to (&lt;code&gt;@spawnat&lt;/code&gt;, loop variables in &lt;code&gt;@distributed&lt;/code&gt;) or received from (&lt;code&gt;fetch&lt;/code&gt;) worker processes must be serialized and deserialized. This adds significant overhead compared to threads accessing shared memory directly. &lt;code&gt;Distributed&lt;/code&gt; is best for coarse-grained parallelism where the computation time is large relative to the communication time.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Parallel Computing", "Distributed Computing":&lt;/strong&gt; Provides a detailed guide to &lt;code&gt;Distributed.jl&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Standard Library, &lt;code&gt;Distributed&lt;/code&gt;:&lt;/strong&gt; Documents &lt;code&gt;addprocs&lt;/code&gt;, &lt;code&gt;nprocs&lt;/code&gt;, &lt;code&gt;nworkers&lt;/code&gt;, &lt;code&gt;myid&lt;/code&gt;, &lt;code&gt;workers&lt;/code&gt;, &lt;code&gt;@everywhere&lt;/code&gt;, &lt;code&gt;@spawnat&lt;/code&gt;, &lt;code&gt;fetch&lt;/code&gt;, &lt;code&gt;@distributed&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;To run the script:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(You MUST start Julia with multiple worker processes, e.g., &lt;code&gt;julia -p 4 0102_distributed_processing.jl&lt;/code&gt;)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia &lt;span class="nt"&gt;-p&lt;/span&gt; 4 0102_distributed_processing.jl
&lt;span class="nt"&gt;---&lt;/span&gt; Distributed Processing Setup &lt;span class="nt"&gt;---&lt;/span&gt;
Total processes &lt;span class="o"&gt;(&lt;/span&gt;main + workers&lt;span class="o"&gt;)&lt;/span&gt;: 5
Number of worker processes: 4
Main process ID: 1
Worker process IDs: &lt;span class="o"&gt;[&lt;/span&gt;2, 3, 4, 5]

&lt;span class="nt"&gt;---&lt;/span&gt; Remote Execution &lt;span class="nt"&gt;---&lt;/span&gt;
Spawning task on worker 2...
Waiting &lt;span class="k"&gt;for &lt;/span&gt;result from worker 2...
Result from worker 2: &lt;span class="s2"&gt;"Process 2 on host '...' (thread 1) knows MY_CONSTANT = 10"&lt;/span&gt;

&lt;span class="nt"&gt;---&lt;/span&gt; Distributed Map-Reduce &lt;span class="o"&gt;(&lt;/span&gt;@distributed&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
Calculating &lt;span class="nb"&gt;sum &lt;/span&gt;of squares from 1 to 10 across workers...
      From worker 2:   Worker 2 processing i &lt;span class="o"&gt;=&lt;/span&gt; 1
      From worker 3:   Worker 3 processing i &lt;span class="o"&gt;=&lt;/span&gt; 4
      From worker 4:   Worker 4 processing i &lt;span class="o"&gt;=&lt;/span&gt; 7
      From worker 5:   Worker 5 processing i &lt;span class="o"&gt;=&lt;/span&gt; 10
      From worker 2:   Worker 2 processing i &lt;span class="o"&gt;=&lt;/span&gt; 2
      From worker 3:   Worker 3 processing i &lt;span class="o"&gt;=&lt;/span&gt; 5
      From worker 4:   Worker 4 processing i &lt;span class="o"&gt;=&lt;/span&gt; 8
      From worker 2:   Worker 2 processing i &lt;span class="o"&gt;=&lt;/span&gt; 3
      From worker 3:   Worker 3 processing i &lt;span class="o"&gt;=&lt;/span&gt; 6
      From worker 4:   Worker 4 processing i &lt;span class="o"&gt;=&lt;/span&gt; 9
Distributed loop finished.
Final &lt;span class="nb"&gt;sum &lt;/span&gt;of squares: 385
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(The exact hostname &lt;code&gt;'...'&lt;/code&gt; and the interleaving of worker output will vary.)&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Simd Vectorization
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;0103_simd_macro.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0103_simd_macro.jl&lt;/span&gt;
&lt;span class="c"&gt;# Introduces the @simd macro for loop vectorization hints.&lt;/span&gt;
&lt;span class="c"&gt;# Requires BenchmarkTools.jl&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BenchmarkTools&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nd"&gt;@btime&lt;/span&gt;

&lt;span class="c"&gt;# --- Standard Loop ---&lt;/span&gt;

&lt;span class="c"&gt;# Function to sum array elements with a standard loop.&lt;/span&gt;
&lt;span class="c"&gt;# The compiler *might* auto-vectorize this, but it's not guaranteed.&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; sum_array_standard&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Vector&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;&lt;span class="x"&gt;})&lt;/span&gt;
    &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;
    &lt;span class="c"&gt;# Use @inbounds for performance, assuming indices are valid.&lt;/span&gt;
    &lt;span class="nd"&gt;@inbounds&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;eachindex&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# --- Loop with @simd Hint ---&lt;/span&gt;

&lt;span class="c"&gt;# Function using the '@simd' macro hint.&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; sum_array_simd&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Vector&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;&lt;span class="x"&gt;})&lt;/span&gt;
    &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;
    &lt;span class="c"&gt;# '@simd' is a *promise* to the compiler that iterations are independent&lt;/span&gt;
    &lt;span class="c"&gt;# and reordering operations (for vectorization) is safe.&lt;/span&gt;
    &lt;span class="nd"&gt;@inbounds&lt;/span&gt; &lt;span class="nd"&gt;@simd&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;eachindex&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="c"&gt;# We promise:&lt;/span&gt;
        &lt;span class="c"&gt;# 1. Iterations are independent (result for 'i' doesn't affect 'i+1').&lt;/span&gt;
        &lt;span class="c"&gt;# 2. No data dependencies across iterations (e.g., A[i] = A[i-1] + ...).&lt;/span&gt;
        &lt;span class="c"&gt;# 3. Floating-point reordering (associativity changes) is acceptable.&lt;/span&gt;
        &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# --- Benchmarking ---&lt;/span&gt;

&lt;span class="c"&gt;# Setup a large array&lt;/span&gt;
&lt;span class="n"&gt;A&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1_000_000&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Benchmarking standard loop:"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# Benchmark the standard loop. Interpolate 'A'.&lt;/span&gt;
&lt;span class="nd"&gt;@btime&lt;/span&gt; &lt;span class="n"&gt;sum_array_standard&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Benchmarking with @simd:"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# Benchmark the loop with the @simd hint. Interpolate 'A'.&lt;/span&gt;
&lt;span class="nd"&gt;@btime&lt;/span&gt; &lt;span class="n"&gt;sum_array_simd&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# --- Verification (Advanced, Optional) ---&lt;/span&gt;
&lt;span class="c"&gt;# To confirm vectorization, you can inspect the generated LLVM code:&lt;/span&gt;
&lt;span class="c"&gt;# julia&amp;gt; import InteractiveUtils: @code_llvm&lt;/span&gt;
&lt;span class="c"&gt;# julia&amp;gt; @code_llvm sum_array_simd(A)&lt;/span&gt;
&lt;span class="c"&gt;# Look for instructions operating on vectors (e.g., "&amp;lt;4 x double&amp;gt;", "vector.body")&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces &lt;strong&gt;SIMD&lt;/strong&gt; (Single Instruction, Multiple Data) and the &lt;strong&gt;&lt;code&gt;@simd&lt;/code&gt;&lt;/strong&gt; macro, a way to potentially achieve significant performance gains by leveraging special CPU vector instructions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Core Concept: SIMD Vectorization
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What is SIMD?&lt;/strong&gt; Modern CPUs have special &lt;strong&gt;vector registers&lt;/strong&gt; (e.g., 128-bit SSE, 256-bit AVX, 512-bit AVX-512) and corresponding instructions that can perform the &lt;em&gt;same operation&lt;/em&gt; (like addition or multiplication) on &lt;em&gt;multiple data elements&lt;/em&gt; (e.g., two &lt;code&gt;Float64&lt;/code&gt;s, four &lt;code&gt;Float32&lt;/code&gt;s, etc.) &lt;strong&gt;in a single clock cycle&lt;/strong&gt;. This is a form of parallelism &lt;em&gt;within&lt;/em&gt; a single CPU core.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Example:&lt;/strong&gt; Instead of adding two &lt;code&gt;Float64&lt;/code&gt;s (&lt;code&gt;addsd&lt;/code&gt;), an AVX-enabled CPU can add &lt;em&gt;four pairs&lt;/em&gt; of &lt;code&gt;Float64&lt;/code&gt;s simultaneously using a single &lt;code&gt;vaddpd&lt;/code&gt; instruction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Goal:&lt;/strong&gt; For loops performing simple arithmetic on arrays, we want the compiler to emit these efficient SIMD instructions instead of scalar instructions. This is called &lt;strong&gt;auto-vectorization&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The &lt;code&gt;@simd&lt;/code&gt; Macro: A Hint to the Compiler
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Compiler Limitations:&lt;/strong&gt; While Julia's compiler (LLVM) is good at auto-vectorization, it can sometimes be too conservative. It might fail to vectorize a loop if it cannot &lt;em&gt;prove&lt;/em&gt; that doing so is safe (e.g., if it suspects potential dependencies between loop iterations or complex memory access patterns).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@simd&lt;/code&gt; Macro:&lt;/strong&gt; The &lt;code&gt;@simd&lt;/code&gt; macro, placed immediately before a &lt;code&gt;for&lt;/code&gt; loop, is a &lt;strong&gt;promise&lt;/strong&gt; or &lt;strong&gt;hint&lt;/strong&gt; from you to the compiler. You are asserting:

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Iteration Independence:&lt;/strong&gt; The computations in one iteration do &lt;strong&gt;not&lt;/strong&gt; affect subsequent iterations.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;No Cross-Iteration Dependencies:&lt;/strong&gt; The loop does not contain dependencies like &lt;code&gt;A[i] = A[i-1] + B[i]&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Floating-Point Safety:&lt;/strong&gt; You accept that the compiler might reorder floating-point operations (e.g., changing &lt;code&gt;(a+b)+c&lt;/code&gt; to &lt;code&gt;a+(b+c)&lt;/code&gt;), which can lead to slightly different results due to precision differences.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Effect:&lt;/strong&gt; By providing this guarantee, &lt;code&gt;@simd&lt;/code&gt; &lt;strong&gt;allows&lt;/strong&gt; the compiler to be more aggressive in applying vectorization transformations that it might otherwise deem unsafe. It &lt;strong&gt;does not force&lt;/strong&gt; vectorization but strongly encourages it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Performance Impact
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Potential Speedup:&lt;/strong&gt; When &lt;code&gt;@simd&lt;/code&gt; successfully enables vectorization for an arithmetic-heavy loop on a contiguous array, the speedup can be significant (typically &lt;strong&gt;2x to 8x&lt;/strong&gt; or more, depending on the operation and the CPU's vector width).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Benchmarking:&lt;/strong&gt; Comparing &lt;code&gt;sum_array_standard&lt;/code&gt; and &lt;code&gt;sum_array_simd&lt;/code&gt; using &lt;code&gt;@btime&lt;/code&gt; is the practical way to see if &lt;code&gt;@simd&lt;/code&gt; provided a benefit &lt;em&gt;in your specific case&lt;/em&gt;. The standard loop might already be auto-vectorized, or the &lt;code&gt;@simd&lt;/code&gt; hint might enable it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Critical Warning: The Promise Must Be True
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Undefined Behavior:&lt;/strong&gt; If you place &lt;code&gt;@simd&lt;/code&gt; before a loop that &lt;strong&gt;violates&lt;/strong&gt; the independence or dependency rules, you are lying to the compiler. It may generate incorrect SIMD code based on your false promise, leading to &lt;strong&gt;wrong results&lt;/strong&gt; (a "vectorized" data race) without any error message.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Responsibility:&lt;/strong&gt; Use &lt;code&gt;@simd&lt;/code&gt; &lt;strong&gt;only when you are certain&lt;/strong&gt; the loop iterations are independent and reordering is safe.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Guideline (HFT):&lt;/strong&gt; &lt;code&gt;@simd&lt;/code&gt; is a valuable tool for optimizing tight, arithmetic loops common in signal processing, financial modeling, or data manipulation. Always benchmark to confirm its effectiveness and ensure your loop meets the independence criteria before using it.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Performance Tips", &lt;code&gt;@simd&lt;/code&gt;:&lt;/strong&gt; Explains the macro as a hint for vectorization and lists the required properties.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LLVM Auto-Vectorizer Documentation:&lt;/strong&gt; (External) Provides insight into the compiler technology Julia uses for vectorization.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;To run the script:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Requires &lt;code&gt;BenchmarkTools.jl&lt;/code&gt; installed)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0103_simd_macro.jl
Benchmarking standard loop: 
  371.123 μs &lt;span class="o"&gt;(&lt;/span&gt;0 allocations: 0 bytes&lt;span class="o"&gt;)&lt;/span&gt;

Benchmarking with @simd:
  84.179 μs &lt;span class="o"&gt;(&lt;/span&gt;0 allocations: 0 bytes&lt;span class="o"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;0104_simd_explicit.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0104_simd_explicit.jl&lt;/span&gt;
&lt;span class="c"&gt;# Demonstrates explicit vectorization using the SIMD.jl package.&lt;/span&gt;
&lt;span class="c"&gt;# Requires SIMD.jl and BenchmarkTools.jl&lt;/span&gt;

&lt;span class="c"&gt;# 1. Import necessary components. See Explanation for installation.&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SIMD&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Vec&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vload&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vloada&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sum&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BenchmarkTools&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nd"&gt;@btime&lt;/span&gt;

&lt;span class="c"&gt;# --- Explicit SIMD Function ---&lt;/span&gt;

&lt;span class="c"&gt;# 2. Define constants for vector width based on target CPU.&lt;/span&gt;
&lt;span class="c"&gt;#    'N = 4' assumes a 256-bit register width (e.g., AVX2) for Float64 (64-bit).&lt;/span&gt;
&lt;span class="c"&gt;#    If using AVX-512, N could be 8. For SSE, N would be 2.&lt;/span&gt;
&lt;span class="c"&gt;#    'VecType' is an alias for the specific SIMD vector type.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="c"&gt;# Vector width (e.g., 4 x Float64 for 256-bit AVX2)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;VecType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Vec&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Float64&lt;/span&gt;&lt;span class="x"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Function using explicit SIMD instructions via SIMD.jl&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; sum_explicit_simd&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Vector&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;&lt;span class="x"&gt;})&lt;/span&gt;
    &lt;span class="c"&gt;# 3. Precondition: Array length must be a multiple of the vector width.&lt;/span&gt;
    &lt;span class="c"&gt;#    Real-world code needs to handle trailing elements (remainder).&lt;/span&gt;
    &lt;span class="nd"&gt;@assert&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="s"&gt;"Array length must be a multiple of SIMD width (&lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;N)"&lt;/span&gt;

    &lt;span class="c"&gt;# 4. Initialize accumulator vector(s).&lt;/span&gt;
    &lt;span class="c"&gt;#    'zero(VecType)' creates a vector register filled with zeros.&lt;/span&gt;
    &lt;span class="c"&gt;#    Using multiple accumulators can sometimes improve instruction-level parallelism.&lt;/span&gt;
    &lt;span class="n"&gt;vsum1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;zero&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;VecType&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# vsum2 = zero(VecType) # Example if using 2 accumulators&lt;/span&gt;

    &lt;span class="c"&gt;# 5. Iterate through the array in steps of the vector width 'N'.&lt;/span&gt;
    &lt;span class="c"&gt;#    '@inbounds' is crucial to remove bounds checks within the SIMD loop.&lt;/span&gt;
    &lt;span class="nd"&gt;@inbounds&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="c"&gt;# 6. Load 'N' elements from memory into a vector register.&lt;/span&gt;
        &lt;span class="c"&gt;#    'vload(VecType, pointer, index)' performs a vector load.&lt;/span&gt;
        &lt;span class="c"&gt;#    'pointer(A, i)' gets the pointer to the i-th element.&lt;/span&gt;
        &lt;span class="c"&gt;#    Alternatively, 'vloada' might assume alignment for potentially faster loads.&lt;/span&gt;
        &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vload&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;VecType&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pointer&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
        &lt;span class="c"&gt;# v = vloada(VecType, pointer(A, i)) # If memory is guaranteed aligned&lt;/span&gt;

        &lt;span class="c"&gt;# 7. Perform vector addition.&lt;/span&gt;
        &lt;span class="c"&gt;#    This compiles to a single SIMD instruction (e.g., 'vaddpd').&lt;/span&gt;
        &lt;span class="n"&gt;vsum1&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;

        &lt;span class="c"&gt;# If using multiple accumulators:&lt;/span&gt;
        &lt;span class="c"&gt;# v = vload(VecType, pointer(A, i + N))&lt;/span&gt;
        &lt;span class="c"&gt;# vsum2 += v&lt;/span&gt;
        &lt;span class="c"&gt;# (Loop step would then be 2*N)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="c"&gt;# 8. Reduce the final vector accumulator(s) to a scalar sum.&lt;/span&gt;
    &lt;span class="c"&gt;#    'sum(vsum1)' adds up the elements within the vector register.&lt;/span&gt;
    &lt;span class="n"&gt;total_sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vsum1&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# + sum(vsum2) if using multiple&lt;/span&gt;

    &lt;span class="c"&gt;# Handle trailing elements here if the length wasn't a multiple of N.&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;total_sum&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# --- Benchmarking ---&lt;/span&gt;

&lt;span class="c"&gt;# Setup a large array (ensure length is a multiple of N)&lt;/span&gt;
&lt;span class="n"&gt;len&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1_000_000&lt;/span&gt;
&lt;span class="c"&gt;# Adjust length slightly if needed: len = floor(Int, len / N) * N&lt;/span&gt;
&lt;span class="n"&gt;A&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Load the @simd version from the previous lesson for comparison&lt;/span&gt;
&lt;span class="c"&gt;# (Assuming 0103_simd_macro.jl is accessible and defines sum_array_simd)&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="n"&gt;include&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0103_simd_macro.jl"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Benchmarking previous @simd version:"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@btime&lt;/span&gt; &lt;span class="n"&gt;sum_array_simd&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Could not load sum_array_simd for comparison: &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;e"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Benchmarking explicit SIMD (SIMD.jl):"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# Benchmark the explicit SIMD function. Interpolate 'A'.&lt;/span&gt;
&lt;span class="nd"&gt;@btime&lt;/span&gt; &lt;span class="n"&gt;sum_explicit_simd&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces the &lt;strong&gt;&lt;code&gt;SIMD.jl&lt;/code&gt;&lt;/strong&gt; package, which provides tools for &lt;strong&gt;explicit vectorization&lt;/strong&gt;. Unlike the &lt;code&gt;@simd&lt;/code&gt; macro (which is a &lt;em&gt;hint&lt;/em&gt;), &lt;code&gt;SIMD.jl&lt;/code&gt; allows you to directly control the use of CPU vector registers and instructions, offering potentially higher and more predictable performance at the cost of increased code complexity.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Installation Note:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;SIMD.jl&lt;/code&gt; is an external package. You need to add it to your project environment once.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Start the Julia REPL: &lt;code&gt;julia&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt; Enter Pkg mode: &lt;code&gt;]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt; Add the package: &lt;code&gt;add SIMD&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt; Exit Pkg mode: Press Backspace or &lt;code&gt;Ctrl+C&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; You can now run this script (assuming &lt;code&gt;BenchmarkTools.jl&lt;/code&gt; is also installed).&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Core Concept: Explicit vs. Implicit Vectorization
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Implicit (&lt;code&gt;@simd&lt;/code&gt;, Auto-vectorization):&lt;/strong&gt; You write a standard loop and &lt;em&gt;hope&lt;/em&gt; or &lt;em&gt;hint&lt;/em&gt; (&lt;code&gt;@simd&lt;/code&gt;) that the compiler (LLVM) is smart enough to generate efficient SIMD instructions. Performance can vary depending on compiler heuristics and loop complexity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explicit (&lt;code&gt;SIMD.jl&lt;/code&gt;):&lt;/strong&gt; You &lt;strong&gt;manually structure&lt;/strong&gt; your loop to operate on chunks of data that fit into CPU vector registers. You use specific types (&lt;code&gt;Vec{N, T}&lt;/code&gt;) and functions (&lt;code&gt;vload&lt;/code&gt;, vector arithmetic) that directly map to SIMD hardware capabilities. You are essentially writing a high-level assembly language for the vector unit.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Using &lt;code&gt;SIMD.jl&lt;/code&gt;
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Vector Type (&lt;code&gt;Vec{N, T}&lt;/code&gt;):&lt;/strong&gt; This type represents a CPU vector register holding &lt;code&gt;N&lt;/code&gt; elements of type &lt;code&gt;T&lt;/code&gt;. &lt;code&gt;Vec{4, Float64}&lt;/code&gt; directly corresponds to a 256-bit AVX register. You choose &lt;code&gt;N&lt;/code&gt; based on your target CPU architecture (e.g., 4 for AVX2 &lt;code&gt;Float64&lt;/code&gt;, 8 for AVX-512 &lt;code&gt;Float64&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Loop Structure:&lt;/strong&gt; The loop must iterate in steps of &lt;code&gt;N&lt;/code&gt; (&lt;code&gt;1:N:length(A)&lt;/code&gt;). You must ensure the array length is compatible (often a multiple of &lt;code&gt;N&lt;/code&gt;) and typically handle any remaining elements separately (this simple example uses an &lt;code&gt;@assert&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Vector Load (&lt;code&gt;vload&lt;/code&gt;, &lt;code&gt;vloada&lt;/code&gt;):&lt;/strong&gt; Instead of scalar loads (&lt;code&gt;A[i]&lt;/code&gt;), you use &lt;code&gt;vload(VecType, pointer, index)&lt;/code&gt; to load &lt;code&gt;N&lt;/code&gt; elements from memory directly into a &lt;code&gt;Vec&lt;/code&gt; register. &lt;code&gt;vloada&lt;/code&gt; is similar but assumes the memory address is aligned, which can be faster on some architectures if true. &lt;code&gt;@inbounds&lt;/code&gt; is crucial here.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Vector Arithmetic (&lt;code&gt;+&lt;/code&gt;, &lt;code&gt;*&lt;/code&gt;, etc.):&lt;/strong&gt; Standard arithmetic operators (&lt;code&gt;+&lt;/code&gt;, &lt;code&gt;-&lt;/code&gt;, &lt;code&gt;*&lt;/code&gt;, &lt;code&gt;/&lt;/code&gt;) and math functions (&lt;code&gt;sqrt&lt;/code&gt;, &lt;code&gt;sin&lt;/code&gt;, etc.) are overloaded for &lt;code&gt;Vec&lt;/code&gt; types. &lt;code&gt;vsum1 + v&lt;/code&gt; compiles to a single vector addition instruction (e.g., &lt;code&gt;vaddpd&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Reduction (&lt;code&gt;sum&lt;/code&gt;):&lt;/strong&gt; After the loop, the accumulator (&lt;code&gt;vsum1&lt;/code&gt;) is a &lt;code&gt;Vec&lt;/code&gt; register containing &lt;code&gt;N&lt;/code&gt; partial sums. You need a final step to reduce this vector to a single scalar value, e.g., using &lt;code&gt;sum(vsum1)&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Performance and Trade-offs
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Potential Gain:&lt;/strong&gt; Explicit SIMD can sometimes outperform compiler auto-vectorization (even with &lt;code&gt;@simd&lt;/code&gt;), especially for complex loops or when the compiler fails to vectorize optimally. It gives you maximum control and performance predictability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complexity:&lt;/strong&gt; Writing explicit SIMD code is significantly more complex and less portable. You need to know the vector width (&lt;code&gt;N&lt;/code&gt;) of your target CPU, handle array lengths that aren't multiples of &lt;code&gt;N&lt;/code&gt;, and manage multiple accumulators if needed for instruction-level parallelism.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;When to Use (HFT):&lt;/strong&gt; This is typically reserved for the absolute most critical, "hot" loops in your application, identified through profiling, where the potential gains from manual vectorization outweigh the complexity and maintenance costs. You wouldn't write your entire application this way.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Guideline:&lt;/strong&gt; Start with standard loops, use &lt;code&gt;@simd&lt;/code&gt; if appropriate and benchmark the improvement. Only resort to explicit SIMD (&lt;code&gt;SIMD.jl&lt;/code&gt;) if profiling shows a specific loop remains a major bottleneck and auto-vectorization (with or without &lt;code&gt;@simd&lt;/code&gt;) isn't achieving the desired performance.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;SIMD.jl&lt;/code&gt; Documentation:&lt;/strong&gt; (&lt;a href="https://github.com/eschnett/SIMD.jl" rel="noopener noreferrer"&gt;https://github.com/eschnett/SIMD.jl&lt;/a&gt; or relevant package documentation). Explains &lt;code&gt;Vec&lt;/code&gt;, &lt;code&gt;vload&lt;/code&gt;, and other vector operations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CPU Vendor Intrinsics Guides (e.g., Intel):&lt;/strong&gt; Provide detailed information on the underlying hardware SIMD instructions that &lt;code&gt;SIMD.jl&lt;/code&gt; maps to.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;To run the script:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Requires &lt;code&gt;SIMD.jl&lt;/code&gt; and &lt;code&gt;BenchmarkTools.jl&lt;/code&gt; installed. Assumes &lt;code&gt;0103_simd_macro.jl&lt;/code&gt; is runnable for comparison.)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0104_simd_explicit.jl
Benchmarking standard loop: 
  371.126 μs &lt;span class="o"&gt;(&lt;/span&gt;0 allocations: 0 bytes&lt;span class="o"&gt;)&lt;/span&gt;

Benchmarking with @simd:
  84.159 μs &lt;span class="o"&gt;(&lt;/span&gt;0 allocations: 0 bytes&lt;span class="o"&gt;)&lt;/span&gt;
Benchmarking previous @simd version:
  84.153 μs &lt;span class="o"&gt;(&lt;/span&gt;0 allocations: 0 bytes&lt;span class="o"&gt;)&lt;/span&gt;

Benchmarking explicit SIMD &lt;span class="o"&gt;(&lt;/span&gt;SIMD.jl&lt;span class="o"&gt;)&lt;/span&gt;:
  93.132 μs &lt;span class="o"&gt;(&lt;/span&gt;0 allocations: 0 bytes&lt;span class="o"&gt;)&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Module 11: Metaprogramming for Zero-Cost Abstractions
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Expressions And Symbols
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;0105_module_intro.md&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This module introduces &lt;strong&gt;metaprogramming&lt;/strong&gt; in Julia: the ability for code to manipulate or generate other code. We move beyond writing functions that operate on &lt;em&gt;values&lt;/em&gt; to writing code that operates on &lt;em&gt;syntax&lt;/em&gt; (&lt;code&gt;Expr&lt;/code&gt; objects) and &lt;em&gt;types&lt;/em&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Beyond Type Stability: Telling the Compiler What to Do
&lt;/h2&gt;

&lt;p&gt;In previous modules, especially Module 6, we focused on writing &lt;strong&gt;type-stable&lt;/strong&gt; functions. This helps the compiler &lt;em&gt;infer&lt;/em&gt; types and generate efficient machine code. Metaprogramming takes this a step further: instead of just &lt;em&gt;helping&lt;/em&gt; the compiler, we will &lt;strong&gt;directly instruct&lt;/strong&gt; the compiler on exactly what code to generate in certain situations.&lt;/p&gt;




&lt;h2&gt;
  
  
  Zero-Cost Abstractions: The Holy Grail
&lt;/h2&gt;

&lt;p&gt;The primary goal of metaprogramming in a performance context is to achieve &lt;strong&gt;zero-cost abstractions&lt;/strong&gt;. This means writing code that is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;High-level and Abstract:&lt;/strong&gt; Readable, reusable, and easy to reason about (e.g., a generic &lt;code&gt;dot_product(a, b)&lt;/code&gt; function).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Zero-Cost:&lt;/strong&gt; Compiles down to the &lt;em&gt;exact same&lt;/em&gt; highly optimized machine code as if you had manually written the low-level, specialized version (e.g., the fully unrolled loop &lt;code&gt;a[1]*b[1] + a[2]*b[2] + ...&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Metaprogramming provides the bridge between high-level expression and low-level performance, eliminating the usual trade-off where abstraction introduces runtime overhead (like function call penalties or dynamic dispatch).&lt;/p&gt;




&lt;h2&gt;
  
  
  Code as Data: The Lisp Heritage
&lt;/h2&gt;

&lt;p&gt;Julia, like Lisp, treats code itself as a &lt;strong&gt;first-class data structure&lt;/strong&gt;. An expression like &lt;code&gt;a + b&lt;/code&gt; isn't just syntax; it can be captured, stored in a variable as an &lt;code&gt;Expr&lt;/code&gt; object, inspected (&lt;code&gt;.head&lt;/code&gt;, &lt;code&gt;.args&lt;/code&gt;), manipulated, and ultimately evaluated. This ability to treat &lt;strong&gt;code as data&lt;/strong&gt; is the foundation upon which Julia's metaprogramming tools are built.&lt;/p&gt;




&lt;h2&gt;
  
  
  Relevance to High-Performance Computing (HFT)
&lt;/h2&gt;

&lt;p&gt;In low-latency environments like High-Frequency Trading, &lt;strong&gt;every nanosecond counts&lt;/strong&gt;. Abstraction overhead that might be acceptable elsewhere (like virtual function calls, dynamic lookups, or even simple function call overhead in the tightest loops) is often intolerable.&lt;/p&gt;

&lt;p&gt;Metaprogramming allows developers to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Eliminate Abstraction Penalties:&lt;/strong&gt; Write clean, reusable abstractions (like generic vector math functions) that compile away completely, leaving only the bare-metal machine instructions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generate Specialized Code:&lt;/strong&gt; Automatically generate highly optimized code tailored to specific data types or sizes known at compile time (e.g., unrolling loops for fixed-size vectors).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reduce Boilerplate:&lt;/strong&gt; Automate the generation of repetitive code patterns.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Tools: Macros and Generated Functions
&lt;/h2&gt;

&lt;p&gt;This module will focus on the two primary compile-time metaprogramming tools in Julia:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Macros (&lt;code&gt;@macro_name&lt;/code&gt;):&lt;/strong&gt; Functions that run during &lt;strong&gt;parsing/macro expansion&lt;/strong&gt;. They take Julia syntax (&lt;code&gt;Expr&lt;/code&gt;, &lt;code&gt;Symbol&lt;/code&gt;, literals) as input and return transformed Julia syntax as output. Ideal for syntactic abstraction and code generation based on the literal code written.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Generated Functions (&lt;code&gt;@generated&lt;/code&gt;):&lt;/strong&gt; Functions that run during &lt;strong&gt;type inference/compilation&lt;/strong&gt;. They take &lt;strong&gt;types&lt;/strong&gt; as input and return an expression (&lt;code&gt;Expr&lt;/code&gt;) representing the specialized code body to be compiled for those specific input types. Ideal for generating optimal code based on type information.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We will also briefly discuss why runtime code generation (&lt;code&gt;eval&lt;/code&gt;) is generally unsuitable for high-performance metaprogramming.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Metaprogramming":&lt;/strong&gt; The primary reference covering expressions, quoting, macros, and generated functions.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;0106_expressions_and_quoting.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0106_expressions_and_quoting.jl&lt;/span&gt;
&lt;span class="c"&gt;# Introduces Expr, Symbol, and quoting: Code as Data.&lt;/span&gt;

&lt;span class="c"&gt;# --- Quoting ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Quoting Code ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 1. The colon ':' followed by parentheses '(...)' or a 'begin...end' block&lt;/span&gt;
&lt;span class="c"&gt;#    is the "quoting" syntax. It prevents execution and captures the&lt;/span&gt;
&lt;span class="c"&gt;#    code structure as data.&lt;/span&gt;
&lt;span class="n"&gt;ex1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ex2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;quote&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
    &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Quoted expression 1: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ex1&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Type of ex1: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex1&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# Expr&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Quoted block expression 2: "&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex2&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Type of ex2: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex2&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# Expr&lt;/span&gt;

&lt;span class="c"&gt;# --- Expr: The Structure of Code ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Inspecting Expr ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 2. An 'Expr' object represents a piece of Julia code internally.&lt;/span&gt;
&lt;span class="c"&gt;#    It has two main fields:&lt;/span&gt;
&lt;span class="c"&gt;#    - 'head': A Symbol indicating the kind of expression (e.g., :call, :(=), :block).&lt;/span&gt;
&lt;span class="c"&gt;#    - 'args': A Vector{Any} containing the parts (arguments) of the expression.&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ex1.head: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ex1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# :call (because '+' is a function call)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ex1.args: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ex1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# [:+ (Symbol), 1 (Int), :(2 * 3) (Expr)]&lt;/span&gt;

&lt;span class="c"&gt;# Accessing parts of the expression tree&lt;/span&gt;
&lt;span class="n"&gt;operator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ex1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;arg1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ex1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;sub_expression&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ex1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Operator: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;operator&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;" (Type: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;operator&lt;/span&gt;&lt;span class="x"&gt;),&lt;/span&gt; &lt;span class="s"&gt;")"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Symbol&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Argument 1: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;" (Type: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg1&lt;/span&gt;&lt;span class="x"&gt;),&lt;/span&gt; &lt;span class="s"&gt;")"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;     &lt;span class="c"&gt;# Int64&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Argument 2: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sub_expression&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;" (Type: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sub_expression&lt;/span&gt;&lt;span class="x"&gt;),&lt;/span&gt; &lt;span class="s"&gt;")"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Expr&lt;/span&gt;

&lt;span class="c"&gt;# Inspect the sub-expression&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Sub-expression head: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sub_expression&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# :call&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Sub-expression args: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sub_expression&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# [:*, 2, 3]&lt;/span&gt;

&lt;span class="c"&gt;# Inspect the block expression&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;ex2.head: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ex2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# :block&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ex2.args (lines/expressions in block): "&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ex2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;" (Type: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="x"&gt;),&lt;/span&gt; &lt;span class="s"&gt;")"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# LineNumberNode or Expr&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# --- Symbols ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Symbols ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 3. A 'Symbol' is an "interned string" used to represent identifiers&lt;/span&gt;
&lt;span class="c"&gt;#    (variable names, function names, operators, keywords) in the code structure.&lt;/span&gt;
&lt;span class="c"&gt;#    It's created with a colon ':'.&lt;/span&gt;
&lt;span class="n"&gt;sym_var&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;my_variable&lt;/span&gt;
&lt;span class="n"&gt;sym_op&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;:+&lt;/span&gt;
&lt;span class="n"&gt;sym_kw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Symbol sym_var: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sym_var&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Type of sym_var: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sym_var&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# Symbol&lt;/span&gt;
&lt;span class="c"&gt;# Symbols guarantee that identical names point to the same object (interning),&lt;/span&gt;
&lt;span class="c"&gt;# making comparisons very fast (relevant from Module 3).&lt;/span&gt;

&lt;span class="c"&gt;# --- Building Expressions Programmatically ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Building Expressions ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 4. You can construct Expr objects directly.&lt;/span&gt;
&lt;span class="c"&gt;#    Expr(head::Symbol, args...)&lt;/span&gt;
&lt;span class="n"&gt;ex_manual&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Expr&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="o"&gt;:*&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Equivalent to :(a * b)&lt;/span&gt;
&lt;span class="n"&gt;ex_assign&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Expr&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="x"&gt;),&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ex_manual&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Equivalent to :(result = a * b)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Manually built expression: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ex_assign&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# --- Evaluating Expressions ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Evaluating Expressions (eval) ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 5. 'eval(expr)' takes an Expr object and executes it in the&lt;/span&gt;
&lt;span class="c"&gt;#    *global scope* of the current module at *runtime*.&lt;/span&gt;
&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;
&lt;span class="c"&gt;# 'result' does not exist yet.&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Before eval: a=&lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;a, b=&lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;b"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# eval(ex_assign) will execute 'result = a * b'&lt;/span&gt;
&lt;span class="n"&gt;eval&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex_assign&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 'result' now exists as a global variable.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"After eval: result=&lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;result"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 6. WARNING: 'eval' is generally SLOW and should be AVOIDED in&lt;/span&gt;
&lt;span class="c"&gt;#    performance-critical code. It invokes the compiler at runtime&lt;/span&gt;
&lt;span class="c"&gt;#    and operates on global variables. Macros and @generated functions&lt;/span&gt;
&lt;span class="c"&gt;#    perform code generation at compile time.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces the fundamental concepts underpinning Julia's metaprogramming capabilities: the ability to treat &lt;strong&gt;code as data&lt;/strong&gt; using &lt;strong&gt;&lt;code&gt;Expr&lt;/code&gt;&lt;/strong&gt; objects, **&lt;code&gt;Symbol&lt;/code&gt;**s, and the &lt;strong&gt;quoting&lt;/strong&gt; syntax.&lt;/p&gt;

&lt;h2&gt;
  
  
  Core Concept: Code as Data (&lt;code&gt;Expr&lt;/code&gt;)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;In Julia, code can be represented as a data structure &lt;em&gt;before&lt;/em&gt; it's compiled or executed. The primary data structure for this is &lt;code&gt;Expr&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Expr&lt;/code&gt; Objects:&lt;/strong&gt; An &lt;code&gt;Expr&lt;/code&gt; represents a compound piece of Julia syntax, like a function call, an assignment, a block of code, or a loop. It essentially represents a node in the code's &lt;strong&gt;Abstract Syntax Tree (AST)&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structure:&lt;/strong&gt; An &lt;code&gt;Expr&lt;/code&gt; has two main components:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;.head&lt;/code&gt;: A &lt;code&gt;Symbol&lt;/code&gt; indicating the &lt;em&gt;type&lt;/em&gt; of expression (e.g., &lt;code&gt;:call&lt;/code&gt; for a function call, &lt;code&gt;:=&lt;/code&gt; for assignment, &lt;code&gt;:block&lt;/code&gt; for a sequence of statements, &lt;code&gt;:if&lt;/code&gt; for an if-statement).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.args&lt;/code&gt;: A &lt;code&gt;Vector{Any}&lt;/code&gt; containing the &lt;em&gt;parts&lt;/em&gt; or arguments of the expression. These parts can be literal values (like &lt;code&gt;1&lt;/code&gt;, &lt;code&gt;"hello"&lt;/code&gt;), &lt;code&gt;Symbol&lt;/code&gt;s, or even other nested &lt;code&gt;Expr&lt;/code&gt; objects.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Quoting (&lt;code&gt;:&lt;/code&gt; or &lt;code&gt;quote ... end&lt;/code&gt;)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Purpose:&lt;/strong&gt; The quoting syntax (&lt;code&gt;:(...)&lt;/code&gt; or &lt;code&gt;quote ... end&lt;/code&gt;) is how you &lt;strong&gt;capture&lt;/strong&gt; Julia code as an &lt;code&gt;Expr&lt;/code&gt; data structure &lt;em&gt;without executing it&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Example:&lt;/strong&gt; &lt;code&gt;ex1 = :(1 + 2 * 3)&lt;/code&gt; does not calculate &lt;code&gt;7&lt;/code&gt;. It creates an &lt;code&gt;Expr&lt;/code&gt; object representing the addition and multiplication operations. Inspecting &lt;code&gt;ex1.head&lt;/code&gt; (&lt;code&gt;:call&lt;/code&gt;) and &lt;code&gt;ex1.args&lt;/code&gt; (&lt;code&gt;[:+, 1, :(2 * 3)]&lt;/code&gt;) reveals this structure. The &lt;code&gt;:(2 * 3)&lt;/code&gt; is itself a nested &lt;code&gt;Expr&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Blocks:&lt;/strong&gt; &lt;code&gt;quote ... end&lt;/code&gt; is useful for capturing multi-line blocks of code. The resulting &lt;code&gt;Expr&lt;/code&gt; typically has &lt;code&gt;.head == :block&lt;/code&gt;, and its &lt;code&gt;.args&lt;/code&gt; contain the individual expressions and line number information from the block.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Symbols (&lt;code&gt;:name&lt;/code&gt;)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Purpose:&lt;/strong&gt; A &lt;code&gt;Symbol&lt;/code&gt; is a special, &lt;strong&gt;interned&lt;/strong&gt; string used primarily to represent &lt;strong&gt;identifiers&lt;/strong&gt; (names) within code structures. Function names (&lt;code&gt;:+&lt;/code&gt;, &lt;code&gt;:sin&lt;/code&gt;), variable names (&lt;code&gt;:x&lt;/code&gt;, &lt;code&gt;:my_variable&lt;/code&gt;), keywords (&lt;code&gt;:if&lt;/code&gt;, &lt;code&gt;:for&lt;/code&gt;), and expression heads (&lt;code&gt;:call&lt;/code&gt;, &lt;code&gt;:block&lt;/code&gt;) are represented as &lt;code&gt;Symbol&lt;/code&gt;s within an &lt;code&gt;Expr&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interning:&lt;/strong&gt; "Interned" means that only one &lt;code&gt;Symbol&lt;/code&gt; object exists for any given name. &lt;code&gt;:x === :x&lt;/code&gt; is always true, and this comparison is as fast as comparing integers (relevant from Module 3 on Symbols vs. Strings). This makes them efficient keys for representing code structure.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Building and Evaluating Expressions
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Programmatic Construction:&lt;/strong&gt; You can build &lt;code&gt;Expr&lt;/code&gt; objects manually using &lt;code&gt;Expr(head, args...)&lt;/code&gt;. This is what macros often do internally to construct the code they will return. &lt;code&gt;Expr(:(=), :result, Expr(:call, :*, :a, :b))&lt;/code&gt; programmatically builds the AST for &lt;code&gt;result = a * b&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;eval(expr)&lt;/code&gt;:&lt;/strong&gt; This function takes an &lt;code&gt;Expr&lt;/code&gt; object and &lt;strong&gt;executes&lt;/strong&gt; it within the &lt;strong&gt;global scope&lt;/strong&gt; of the current module at &lt;strong&gt;runtime&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;eval&lt;/code&gt; Warning:&lt;/strong&gt; While useful for demonstration or interactive use, &lt;code&gt;eval&lt;/code&gt; should generally be &lt;strong&gt;avoided&lt;/strong&gt; in performance-sensitive code. It has significant overhead because:

&lt;ol&gt;
&lt;li&gt; It often involves invoking the compiler &lt;strong&gt;at runtime&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; It operates in the &lt;strong&gt;global scope&lt;/strong&gt;, which hinders compiler optimizations (due to potential type instability, as seen in Module 6).&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Metaprogramming Goal:&lt;/strong&gt; The goal of high-performance metaprogramming (using macros and generated functions) is to perform code generation and transformation &lt;strong&gt;at compile time&lt;/strong&gt;, avoiding runtime &lt;code&gt;eval&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Understanding &lt;code&gt;Expr&lt;/code&gt;, &lt;code&gt;Symbol&lt;/code&gt;, and quoting is the foundation for writing macros, which manipulate these code structures before compilation.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Metaprogramming", "Expressions":&lt;/strong&gt; Explains &lt;code&gt;Expr&lt;/code&gt;, &lt;code&gt;Symbol&lt;/code&gt;, quoting (&lt;code&gt;quote&lt;/code&gt;), and &lt;code&gt;dump&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Metaprogramming", "Eval":&lt;/strong&gt; Describes &lt;code&gt;eval&lt;/code&gt; and its scope implications.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0106_expressions_and_quoting.jl
&lt;span class="nt"&gt;---&lt;/span&gt; Quoting Code &lt;span class="nt"&gt;---&lt;/span&gt;
Quoted expression 1: 1 + 2 &lt;span class="k"&gt;*&lt;/span&gt; 3
Type of ex1: Expr

Quoted block expression 2:
quote
    &lt;span class="c"&gt;#= ... =#&lt;/span&gt;
    x &lt;span class="o"&gt;=&lt;/span&gt; 10
    &lt;span class="c"&gt;#= ... =#&lt;/span&gt;
    y &lt;span class="o"&gt;=&lt;/span&gt; x + 5
end
Type of ex2: Expr

&lt;span class="nt"&gt;---&lt;/span&gt; Inspecting Expr &lt;span class="nt"&gt;---&lt;/span&gt;
ex1.head: call
ex1.args: Any[:+, 1, :&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;Expr&lt;span class="o"&gt;(&lt;/span&gt;:call, :&lt;span class="k"&gt;*&lt;/span&gt;, 2, 3&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="o"&gt;)]&lt;/span&gt;
  Operator: + &lt;span class="o"&gt;(&lt;/span&gt;Type: Symbol&lt;span class="o"&gt;)&lt;/span&gt;
  Argument 1: 1 &lt;span class="o"&gt;(&lt;/span&gt;Type: Int64&lt;span class="o"&gt;)&lt;/span&gt;
  Argument 2: 2 &lt;span class="k"&gt;*&lt;/span&gt; 3 &lt;span class="o"&gt;(&lt;/span&gt;Type: Expr&lt;span class="o"&gt;)&lt;/span&gt;
  Sub-expression &lt;span class="nb"&gt;head&lt;/span&gt;: call
  Sub-expression args: Any[:&lt;span class="k"&gt;*&lt;/span&gt;, 2, 3]

ex2.head: block
ex2.args &lt;span class="o"&gt;(&lt;/span&gt;lines/expressions &lt;span class="k"&gt;in &lt;/span&gt;block&lt;span class="o"&gt;)&lt;/span&gt;:
  LineNumberNode&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;, :none&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;Type: LineNumberNode&lt;span class="o"&gt;)&lt;/span&gt;
  :&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;Expr&lt;span class="o"&gt;(&lt;/span&gt;:&lt;span class="o"&gt;(=)&lt;/span&gt;, :x, 10&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;Type: Expr&lt;span class="o"&gt;)&lt;/span&gt;
  LineNumberNode&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;, :none&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;Type: LineNumberNode&lt;span class="o"&gt;)&lt;/span&gt;
  :&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;Expr&lt;span class="o"&gt;(&lt;/span&gt;:&lt;span class="o"&gt;(=)&lt;/span&gt;, :y, Expr&lt;span class="o"&gt;(&lt;/span&gt;:call, :+, :x, 5&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;Type: Expr&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nt"&gt;---&lt;/span&gt; Symbols &lt;span class="nt"&gt;---&lt;/span&gt;
Symbol sym_var: my_variable
Type of sym_var: Symbol

&lt;span class="nt"&gt;---&lt;/span&gt; Building Expressions &lt;span class="nt"&gt;---&lt;/span&gt;
Manually built expression: result &lt;span class="o"&gt;=&lt;/span&gt; a &lt;span class="k"&gt;*&lt;/span&gt; b

&lt;span class="nt"&gt;---&lt;/span&gt; Evaluating Expressions &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;eval&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
Before &lt;span class="nb"&gt;eval&lt;/span&gt;: &lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5, &lt;span class="nv"&gt;b&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;6
After &lt;span class="nb"&gt;eval&lt;/span&gt;: &lt;span class="nv"&gt;result&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;30
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(LineNumberNode details and exact Expr printing might vary slightly.)&lt;/em&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;0107_dump_and_ast.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0107_dump_and_ast.jl&lt;/span&gt;
&lt;span class="c"&gt;# Using dump() to inspect the structure of Expr objects (AST).&lt;/span&gt;

&lt;span class="c"&gt;# 1. Basic arithmetic expression&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- dump(:(1 + 2 * 3)) ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# Quoting captures the code as an Expr object.&lt;/span&gt;
&lt;span class="n"&gt;ex1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# dump() provides a detailed, recursive view of the object's structure.&lt;/span&gt;
&lt;span class="n"&gt;dump&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex1&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Separator&lt;/span&gt;

&lt;span class="c"&gt;# 2. Function call expression&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- dump(:(println(&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;Hello &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;, name))) ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ex2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;dump&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex2&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 3. Assignment expression with array indexing&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- dump(:(results[i] = compute(data[i]))) ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ex3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;compute&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="x"&gt;]))&lt;/span&gt;
&lt;span class="n"&gt;dump&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex3&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 4. Block expression (e.g., from 'begin...end' or multi-line quote)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- dump(quote ... end) ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ex4&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;quote&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
        &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Greater"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="n"&gt;dump&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex4&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces the &lt;strong&gt;&lt;code&gt;dump()&lt;/code&gt;&lt;/strong&gt; function, an indispensable tool for metaprogramming in Julia. It allows you to visualize the detailed internal structure of any Julia object, and it's particularly useful for understanding the &lt;strong&gt;Abstract Syntax Tree (AST)&lt;/strong&gt; represented by &lt;strong&gt;&lt;code&gt;Expr&lt;/code&gt;&lt;/strong&gt; objects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Core Concept: Visualizing the AST
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Expr&lt;/code&gt; Review:&lt;/strong&gt; As seen in the previous lesson, Julia code captured by quoting (&lt;code&gt;:&lt;/code&gt; or &lt;code&gt;quote...end&lt;/code&gt;) is stored as nested &lt;code&gt;Expr&lt;/code&gt; objects. An &lt;code&gt;Expr&lt;/code&gt; has a &lt;code&gt;.head&lt;/code&gt; (a &lt;code&gt;Symbol&lt;/code&gt; indicating the operation type) and &lt;code&gt;.args&lt;/code&gt; (a &lt;code&gt;Vector{Any}&lt;/code&gt; containing the parts).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;dump(object)&lt;/code&gt;:&lt;/strong&gt; This built-in function provides a &lt;strong&gt;recursive, indented printout&lt;/strong&gt; of the structure and fields of any Julia object. When applied to an &lt;code&gt;Expr&lt;/code&gt;, it reveals the entire tree structure of the captured code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why Use &lt;code&gt;dump()&lt;/code&gt;?&lt;/strong&gt; When writing macros (which receive &lt;code&gt;Expr&lt;/code&gt; objects as input), you &lt;em&gt;need&lt;/em&gt; to know the exact structure of the code you are receiving to correctly transform it. &lt;code&gt;dump()&lt;/code&gt; is your primary tool for inspecting these input expressions during macro development and debugging.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Analyzing the Output
&lt;/h2&gt;

&lt;p&gt;Let's examine the &lt;code&gt;dump&lt;/code&gt; output for each example:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;dump(:(1 + 2 * 3))&lt;/code&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  * Shows the top-level `Expr` with `head: call` and `args: [+, 1, Expr]`. This confirms that `1 + ...` is treated as a function call to `+`.
  * Recursively shows the nested `Expr` for `2 * 3` also having `head: call` and `args: [*, 2, 3]`.
  * This reveals the **operator precedence** and nesting captured in the AST.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;dump(:(println("Hello ", name)))&lt;/code&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  * `head: call`.
  * `args: [println (GlobalRef), "Hello " (String), name (Symbol)]`.
  * Illustrates how function names (`println`), literal strings, and variable names (`name`, represented as a `Symbol`) appear within the `.args` list.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;dump(:(results[i] = compute(data[i])))&lt;/code&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  * Top-level `head: =` (assignment).
  * `args[1]` is an `Expr` representing the left-hand side `results[i]`, with `head: ref` (array reference/indexing) and `args: [results, i]`.
  * `args[2]` is an `Expr` representing the right-hand side `compute(data[i])`, with `head: call` and `args: [compute, Expr]`, where the nested `Expr` is for `data[i]` (`head: ref`, `args: [data, i]`).
  * Shows how complex statements involving assignments, function calls, and indexing are represented as nested trees.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;dump(quote ... end)&lt;/code&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  * Top-level `head: block`.
  * `args` contains a sequence of items representing the lines within the block, often alternating between `LineNumberNode` (for debugging info) and `Expr` objects for each actual statement (like the assignment `x = 10` (`head: =`) and the `if` statement (`head: if`)).
  * Shows the structure for multi-line code blocks.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;By using &lt;code&gt;dump()&lt;/code&gt;, you gain a precise understanding of how Julia represents syntax internally. This knowledge is crucial before attempting to write macros that manipulate or generate code effectively.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Base Documentation, &lt;code&gt;dump&lt;/code&gt;:&lt;/strong&gt; "Show every part of the representation of a value."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Metaprogramming", "Expressions":&lt;/strong&gt; Describes the &lt;code&gt;Expr&lt;/code&gt; structure that &lt;code&gt;dump&lt;/code&gt; visualizes.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0107_dump_and_ast.jl
&lt;span class="nt"&gt;---&lt;/span&gt; dump&lt;span class="o"&gt;(&lt;/span&gt;:&lt;span class="o"&gt;(&lt;/span&gt;1 + 2 &lt;span class="k"&gt;*&lt;/span&gt; 3&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
Expr
  &lt;span class="nb"&gt;head&lt;/span&gt;: Symbol call
  args: Array&lt;span class="o"&gt;{&lt;/span&gt;Any&lt;span class="o"&gt;}((&lt;/span&gt;3,&lt;span class="o"&gt;))&lt;/span&gt;
    1: Symbol +
    2: Int64 1
    3: Expr
      &lt;span class="nb"&gt;head&lt;/span&gt;: Symbol call
      args: Array&lt;span class="o"&gt;{&lt;/span&gt;Any&lt;span class="o"&gt;}((&lt;/span&gt;3,&lt;span class="o"&gt;))&lt;/span&gt;
        1: Symbol &lt;span class="k"&gt;*&lt;/span&gt;
        2: Int64 2
        3: Int64 3

&lt;span class="nt"&gt;------------------------------&lt;/span&gt;

&lt;span class="nt"&gt;---&lt;/span&gt; dump&lt;span class="o"&gt;(&lt;/span&gt;:&lt;span class="o"&gt;(&lt;/span&gt;println&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Hello "&lt;/span&gt;, name&lt;span class="o"&gt;)))&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
Expr
  &lt;span class="nb"&gt;head&lt;/span&gt;: Symbol call
  args: Array&lt;span class="o"&gt;{&lt;/span&gt;Any&lt;span class="o"&gt;}((&lt;/span&gt;3,&lt;span class="o"&gt;))&lt;/span&gt;
    1: Symbol println
    2: String &lt;span class="s2"&gt;"Hello "&lt;/span&gt;
    3: Symbol name

&lt;span class="nt"&gt;------------------------------&lt;/span&gt;

&lt;span class="nt"&gt;---&lt;/span&gt; dump&lt;span class="o"&gt;(&lt;/span&gt;:&lt;span class="o"&gt;(&lt;/span&gt;results[i] &lt;span class="o"&gt;=&lt;/span&gt; compute&lt;span class="o"&gt;(&lt;/span&gt;data[i]&lt;span class="o"&gt;)))&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
Expr
  &lt;span class="nb"&gt;head&lt;/span&gt;: Symbol &lt;span class="o"&gt;=&lt;/span&gt;
  args: Array&lt;span class="o"&gt;{&lt;/span&gt;Any&lt;span class="o"&gt;}((&lt;/span&gt;2,&lt;span class="o"&gt;))&lt;/span&gt;
    1: Expr
      &lt;span class="nb"&gt;head&lt;/span&gt;: Symbol ref
      args: Array&lt;span class="o"&gt;{&lt;/span&gt;Any&lt;span class="o"&gt;}((&lt;/span&gt;2,&lt;span class="o"&gt;))&lt;/span&gt;
        1: Symbol results
        2: Symbol i
    2: Expr
      &lt;span class="nb"&gt;head&lt;/span&gt;: Symbol call
      args: Array&lt;span class="o"&gt;{&lt;/span&gt;Any&lt;span class="o"&gt;}((&lt;/span&gt;2,&lt;span class="o"&gt;))&lt;/span&gt;
        1: Symbol compute
        2: Expr
          &lt;span class="nb"&gt;head&lt;/span&gt;: Symbol ref
          args: Array&lt;span class="o"&gt;{&lt;/span&gt;Any&lt;span class="o"&gt;}((&lt;/span&gt;2,&lt;span class="o"&gt;))&lt;/span&gt;
            1: Symbol data
            2: Symbol i

&lt;span class="nt"&gt;------------------------------&lt;/span&gt;

&lt;span class="nt"&gt;---&lt;/span&gt; dump&lt;span class="o"&gt;(&lt;/span&gt;quote ... end&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
Expr
  &lt;span class="nb"&gt;head&lt;/span&gt;: Symbol block
  args: Array&lt;span class="o"&gt;{&lt;/span&gt;Any&lt;span class="o"&gt;}((&lt;/span&gt;3,&lt;span class="o"&gt;))&lt;/span&gt;
    1: LineNumberNode
      line: Int64 36
      file: Symbol &lt;span class="c"&gt;## path to file ##&lt;/span&gt;
    2: Expr
      &lt;span class="nb"&gt;head&lt;/span&gt;: Symbol &lt;span class="o"&gt;=&lt;/span&gt;
      args: Array&lt;span class="o"&gt;{&lt;/span&gt;Any&lt;span class="o"&gt;}((&lt;/span&gt;2,&lt;span class="o"&gt;))&lt;/span&gt;
        1: Symbol x
        2: Int64 10
    3: Expr
      &lt;span class="nb"&gt;head&lt;/span&gt;: Symbol &lt;span class="k"&gt;if
      &lt;/span&gt;args: Array&lt;span class="o"&gt;{&lt;/span&gt;Any&lt;span class="o"&gt;}((&lt;/span&gt;2,&lt;span class="o"&gt;))&lt;/span&gt;
        1: Expr
          &lt;span class="nb"&gt;head&lt;/span&gt;: Symbol call
          args: Array&lt;span class="o"&gt;{&lt;/span&gt;Any&lt;span class="o"&gt;}((&lt;/span&gt;3,&lt;span class="o"&gt;))&lt;/span&gt;
            1: Symbol &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            2: Symbol x
            3: Int64 5
        2: Expr
          &lt;span class="nb"&gt;head&lt;/span&gt;: Symbol block
          args: Array&lt;span class="o"&gt;{&lt;/span&gt;Any&lt;span class="o"&gt;}((&lt;/span&gt;2,&lt;span class="o"&gt;))&lt;/span&gt;
            1: LineNumberNode
              line: Int64 38
              file: Symbol &lt;span class="c"&gt;## path to file ##&lt;/span&gt;
            2: Expr
              &lt;span class="nb"&gt;head&lt;/span&gt;: Symbol call
              args: Array&lt;span class="o"&gt;{&lt;/span&gt;Any&lt;span class="o"&gt;}((&lt;/span&gt;2,&lt;span class="o"&gt;))&lt;/span&gt;
                1: Symbol println
                2: String &lt;span class="s2"&gt;"Greater"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(File paths and line numbers in the output will vary.)&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Macros
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;0108_macros_basics.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0108_macros_basics.jl&lt;/span&gt;
&lt;span class="c"&gt;# Defines and uses a simple macro.&lt;/span&gt;

&lt;span class="c"&gt;# 1. Define a macro using the 'macro' keyword.&lt;/span&gt;
&lt;span class="c"&gt;#    The macro name MUST start with '@'.&lt;/span&gt;
&lt;span class="c"&gt;#    Macros receive their arguments as quoted expressions (Expr, Symbol, literals).&lt;/span&gt;
&lt;span class="k"&gt;macro&lt;/span&gt;&lt;span class="nf"&gt; print_expression_info&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expression_arg&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# This code runs during macro expansion (before runtime).&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Inside Macro '@print_expression_info' (Compile Time) ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Received expression: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expression_arg&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Type of expression:  "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expression_arg&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  String representation: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expression_arg&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# Convert Symbol or Expr to String&lt;/span&gt;

    &lt;span class="c"&gt;# 2. Return a new expression.&lt;/span&gt;
    &lt;span class="c"&gt;#    This expression will *replace* the original macro call in the code.&lt;/span&gt;
    &lt;span class="c"&gt;#    We use '$' interpolation to insert the *string* representation&lt;/span&gt;
    &lt;span class="c"&gt;#    of the original expression into the 'println' call we are building.&lt;/span&gt;
    &lt;span class="n"&gt;returned_expr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;quote&lt;/span&gt;
        &lt;span class="c"&gt;# This code will run at runtime&lt;/span&gt;
        &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Executing Code Generated by Macro (Runtime) ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="c"&gt;# Interpolate the stringified expression from compile time&lt;/span&gt;
        &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Original expression was: '"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expression_arg&lt;/span&gt;&lt;span class="x"&gt;)),&lt;/span&gt; &lt;span class="s"&gt;"'"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="c"&gt;# Interpolate the original expression itself to be evaluated at runtime&lt;/span&gt;
        &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;result_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expression_arg&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Its runtime value is:  "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result_value&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Macro Returning Expression ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;returned_expr&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"---------------------------------"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;returned_expr&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;


&lt;span class="c"&gt;# --- Using the Macro ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Script Execution (Runtime) ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Preparing to call the macro..."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 3. Call the macro.&lt;/span&gt;
&lt;span class="c"&gt;#    Julia parses this line, sees the '@', and executes the macro function,&lt;/span&gt;
&lt;span class="c"&gt;#    passing the quoted argument ':(1 + 2 * 3)'.&lt;/span&gt;
&lt;span class="c"&gt;#    The code below is *replaced* by the 'returned_expr' from the macro.&lt;/span&gt;
&lt;span class="nd"&gt;@print_expression_info&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Preparing to call the macro with a variable..."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;my_var&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
&lt;span class="nd"&gt;@print_expression_info&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_var&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Script finished."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces &lt;strong&gt;macros&lt;/strong&gt;, a core metaprogramming tool in Julia. Macros are functions that run at &lt;strong&gt;parse/expansion time&lt;/strong&gt; to &lt;strong&gt;transform&lt;/strong&gt; code syntax before it is fully compiled and executed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Core Concept: Syntax Transformation
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Macro Definition:&lt;/strong&gt; You define a macro using the &lt;code&gt;macro MacroName(args...) ... end&lt;/code&gt; syntax. The name &lt;strong&gt;must&lt;/strong&gt; start with &lt;code&gt;@&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Input:&lt;/strong&gt; Macros &lt;strong&gt;do not receive evaluated values&lt;/strong&gt; like regular functions. They receive the &lt;strong&gt;literal syntax&lt;/strong&gt; (code) passed to them as arguments, automatically quoted into &lt;code&gt;Expr&lt;/code&gt; objects, &lt;code&gt;Symbol&lt;/code&gt;s, or literal values.

&lt;ul&gt;
&lt;li&gt;In &lt;code&gt;@print_expression_info(1 + 2 * 3)&lt;/code&gt;, the macro receives the &lt;code&gt;Expr&lt;/code&gt; object &lt;code&gt;:(1 + 2 * 3)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;In &lt;code&gt;@print_expression_info(my_var / 2)&lt;/code&gt;, it receives the &lt;code&gt;Expr&lt;/code&gt; object &lt;code&gt;:(my_var / 2)&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Execution Time:&lt;/strong&gt; The code &lt;em&gt;inside&lt;/em&gt; the macro definition (e.g., the &lt;code&gt;println&lt;/code&gt; statements within &lt;code&gt;macro print_expression_info&lt;/code&gt;) runs &lt;strong&gt;before&lt;/strong&gt; the main script execution, during a phase called &lt;strong&gt;macro expansion&lt;/strong&gt;. This is often loosely called "compile time" or "parse time".&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Output (Return Value):&lt;/strong&gt; A macro &lt;strong&gt;must return&lt;/strong&gt; a valid Julia expression (&lt;code&gt;Expr&lt;/code&gt;, &lt;code&gt;Symbol&lt;/code&gt;, or literal).&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Transformation:&lt;/strong&gt; The crucial step is that the original macro call (e.g., &lt;code&gt;@print_expression_info(1 + 2 * 3)&lt;/code&gt;) is &lt;strong&gt;completely replaced&lt;/strong&gt; in the program's Abstract Syntax Tree (AST) by the &lt;strong&gt;expression returned&lt;/strong&gt; by the macro. The final compiled code contains the &lt;em&gt;result&lt;/em&gt; of the macro's transformation, not the macro call itself.&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Interpolation (&lt;code&gt;$&lt;/code&gt;) in Macros
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Purpose:&lt;/strong&gt; The dollar sign &lt;code&gt;$&lt;/code&gt; is used &lt;em&gt;inside&lt;/em&gt; a quoted expression (&lt;code&gt;:()&lt;/code&gt; or &lt;code&gt;quote...end&lt;/code&gt;) within a macro definition. It signifies &lt;strong&gt;interpolation&lt;/strong&gt; or "unquoting."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Behavior:&lt;/strong&gt; It means "evaluate the expression immediately following the &lt;code&gt;$&lt;/code&gt; &lt;strong&gt;during macro expansion time&lt;/strong&gt;, and substitute its resulting &lt;em&gt;value&lt;/em&gt; into the expression being built."

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;$(string(expression_arg))&lt;/code&gt;: Evaluates &lt;code&gt;string(expression_arg)&lt;/code&gt; at expansion time (converting the input &lt;code&gt;Expr&lt;/code&gt; or &lt;code&gt;Symbol&lt;/code&gt; like &lt;code&gt;:my_var&lt;/code&gt; to the &lt;code&gt;String&lt;/code&gt; &lt;code&gt;"my_var"&lt;/code&gt;) and inserts that string literal into the &lt;code&gt;println&lt;/code&gt; call being constructed.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$(expression_arg)&lt;/code&gt;: Inserts the &lt;em&gt;original &lt;code&gt;Expr&lt;/code&gt; object&lt;/em&gt; received by the macro (&lt;code&gt;:(1 + 2 * 3)&lt;/code&gt; or &lt;code&gt;:(my_var / 2)&lt;/code&gt;) directly into the code being built. This ensures the original calculation is performed at &lt;em&gt;runtime&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Example Walkthrough (&lt;code&gt;@print_expression_info(1 + 2 * 3)&lt;/code&gt;)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Parsing:&lt;/strong&gt; Julia sees &lt;code&gt;@print_expression_info(1 + 2 * 3)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Macro Call:&lt;/strong&gt; It calls the &lt;code&gt;print_expression_info&lt;/code&gt; macro function, passing &lt;code&gt;expression_arg = :(1 + 2 * 3)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Macro Execution (Expansion Time):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;println&lt;/code&gt;s inside the macro run, printing info about the &lt;code&gt;Expr&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;string(expression_arg)&lt;/code&gt; evaluates to &lt;code&gt;"1 + 2 * 3"&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The &lt;code&gt;quote ... end&lt;/code&gt; block constructs a new &lt;code&gt;Expr&lt;/code&gt; object. Interpolation substitutes &lt;code&gt;"1 + 2 * 3"&lt;/code&gt; and &lt;code&gt;:(1 + 2 * 3)&lt;/code&gt;. The resulting &lt;code&gt;Expr&lt;/code&gt; is equivalent to:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="k"&gt;quote&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Executing Code Generated by Macro (Runtime) ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Original expression was: '"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"1 + 2 * 3"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"'"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;result_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# The original expression inserted&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Its runtime value is:  "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result_value&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Replacement:&lt;/strong&gt; The original line &lt;code&gt;@print_expression_info(1 + 2 * 3)&lt;/code&gt; in the code is replaced by this generated &lt;code&gt;quote&lt;/code&gt; block.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Compilation &amp;amp; Runtime:&lt;/strong&gt; Julia compiles the &lt;em&gt;transformed&lt;/em&gt; code. When the script runs, the &lt;code&gt;println&lt;/code&gt; statements &lt;em&gt;inside the generated quote block&lt;/em&gt; execute, calculating &lt;code&gt;1 + 2 * 3&lt;/code&gt; and printing the runtime value &lt;code&gt;7&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Macros allow you to manipulate syntax, reduce boilerplate, create domain-specific languages, and perform code generation before the main compilation phase, enabling powerful abstractions without runtime cost.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Metaprogramming", "Macros":&lt;/strong&gt; Explains macro definition, expansion, interpolation, and provides examples.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0108_macros_basics.jl
Preparing to call the macro...
&lt;span class="nt"&gt;---&lt;/span&gt; Inside Macro &lt;span class="s1"&gt;'@print_expression_info'&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;Compile Time&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
  Received expression:   1 + 2 &lt;span class="k"&gt;*&lt;/span&gt; 3
  Type of expression:    Expr
  String representation: 1 + 2 &lt;span class="k"&gt;*&lt;/span&gt; 3
&lt;span class="nt"&gt;---&lt;/span&gt; Macro Returning Expression &lt;span class="nt"&gt;---&lt;/span&gt;
begin
    &lt;span class="c"&gt;#= 0108_macros_basics.jl:8 =#&lt;/span&gt;
    println&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"--- Executing Code Generated by Macro (Runtime) ---"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;#= 0108_macros_basics.jl:9 =#&lt;/span&gt;
    println&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"  Original expression was: '"&lt;/span&gt;, &lt;span class="s2"&gt;"1 + 2 * 3"&lt;/span&gt;, &lt;span class="s2"&gt;"'"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;#= 0108_macros_basics.jl:10 =#&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;result_value &lt;span class="o"&gt;=&lt;/span&gt; 1 + 2 &lt;span class="k"&gt;*&lt;/span&gt; 3
    &lt;span class="c"&gt;#= 0108_macros_basics.jl:11 =#&lt;/span&gt;
    println&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"  Its runtime value is:  "&lt;/span&gt;, result_value&lt;span class="o"&gt;)&lt;/span&gt;
end
&lt;span class="nt"&gt;----------------------------------&lt;/span&gt;
&lt;span class="nt"&gt;---&lt;/span&gt; Executing Code Generated by Macro &lt;span class="o"&gt;(&lt;/span&gt;Runtime&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
  Original expression was: &lt;span class="s1"&gt;'1 + 2 * 3'&lt;/span&gt;
  Its runtime value is:  7

Preparing to call the macro with a variable...
&lt;span class="nt"&gt;---&lt;/span&gt; Inside Macro &lt;span class="s1"&gt;'@print_expression_info'&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;Compile Time&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
  Received expression:   my_var / 2
  Type of expression:    Expr
  String representation: my_var / 2
&lt;span class="nt"&gt;---&lt;/span&gt; Macro Returning Expression &lt;span class="nt"&gt;---&lt;/span&gt;
begin
    &lt;span class="c"&gt;#= 0108_macros_basics.jl:8 =#&lt;/span&gt;
    println&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"--- Executing Code Generated by Macro (Runtime) ---"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;#= 0108_macros_basics.jl:9 =#&lt;/span&gt;
    println&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"  Original expression was: '"&lt;/span&gt;, &lt;span class="s2"&gt;"my_var / 2"&lt;/span&gt;, &lt;span class="s2"&gt;"'"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;#= 0108_macros_basics.jl:10 =#&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;result_value &lt;span class="o"&gt;=&lt;/span&gt; my_var / 2
    &lt;span class="c"&gt;#= 0108_macros_basics.jl:11 =#&lt;/span&gt;
    println&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"  Its runtime value is:  "&lt;/span&gt;, result_value&lt;span class="o"&gt;)&lt;/span&gt;
end
&lt;span class="nt"&gt;----------------------------------&lt;/span&gt;
&lt;span class="nt"&gt;---&lt;/span&gt; Executing Code Generated by Macro &lt;span class="o"&gt;(&lt;/span&gt;Runtime&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
  Original expression was: &lt;span class="s1"&gt;'my_var / 2'&lt;/span&gt;
  Its runtime value is:  50.0

Script finished.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(Note: Line number nodes &lt;code&gt;#= ... =#&lt;/code&gt; and internal variable names will vary but show the structure of the generated code.)&lt;/em&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;0109_macro_hygiene_and_esc.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0109_macro_hygiene_and_esc.jl&lt;/span&gt;
&lt;span class="c"&gt;# Explains macro hygiene and how to bypass it with esc().&lt;/span&gt;

&lt;span class="c"&gt;# --- Part 1: Hygienic Macro (Default Behavior) ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Part 1: Hygienic Macro ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;macro&lt;/span&gt;&lt;span class="nf"&gt; hygienic_example&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
    &lt;span class="c"&gt;# This macro defines a variable 'x' internally.&lt;/span&gt;
    &lt;span class="c"&gt;# Due to hygiene, this 'x' will be automatically renamed&lt;/span&gt;
    &lt;span class="c"&gt;# by the compiler to avoid collision with any 'x' outside the macro.&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  (Macro Expansion Time: Defining hygienic 'x')"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;quote&lt;/span&gt;
        &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Value from Hygienic Macro"&lt;/span&gt; &lt;span class="c"&gt;# Renamed internally (e.g., ##x#123)&lt;/span&gt;
        &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Inside generated code (Runtime): Hygienic x = "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# Define a global 'x' in the calling scope.&lt;/span&gt;
&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Value from Global Scope"&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Before macro call: Global x = "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Call the macro. The 'x' inside the macro's generated code&lt;/span&gt;
&lt;span class="c"&gt;# will NOT interfere with the global 'x'.&lt;/span&gt;
&lt;span class="nd"&gt;@hygienic_example&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"After macro call: Global x = "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Remains unchanged&lt;/span&gt;


&lt;span class="c"&gt;# --- Part 2: Unhygienic Macro (Using esc()) ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Part 2: Unhygienic Macro (using esc()) ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;macro&lt;/span&gt;&lt;span class="nf"&gt; unhygienic_assignment&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;varname&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# This macro *intends* to assign to a variable in the *caller's* scope.&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  (Macro Expansion Time: Assigning to caller's variable)"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# 'esc(varname)' tells the hygiene system NOT to rename 'varname'.&lt;/span&gt;
    &lt;span class="c"&gt;# It ensures the assignment targets the variable from the calling scope.&lt;/span&gt;
    &lt;span class="c"&gt;# 'value' is interpolated as usual.&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;esc&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;varname&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 'y' does not exist yet in global scope.&lt;/span&gt;
&lt;span class="c"&gt;# The macro call will create and assign to the global 'y'.&lt;/span&gt;
&lt;span class="nd"&gt;@unhygienic_assignment&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"After macro call: Global y = "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# y now exists and is 123&lt;/span&gt;

&lt;span class="c"&gt;# Modify an existing variable 'x' using the unhygienic macro.&lt;/span&gt;
&lt;span class="nd"&gt;@unhygienic_assignment&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Value assigned via unhygienic macro"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"After macro call: Global x = "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# x has been changed&lt;/span&gt;


&lt;span class="c"&gt;# --- Part 3: Hygienic Wrapping Macro (Common Pattern) ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Part 3: Hygienic Wrapping Macro (@simple_time) ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# A macro to time an expression, using hygiene correctly.&lt;/span&gt;
&lt;span class="k"&gt;macro&lt;/span&gt;&lt;span class="nf"&gt; simple_time&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expression_to_run&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# Variables defined *by the macro* should be hygienic (local).&lt;/span&gt;
    &lt;span class="c"&gt;# The code *provided by the user* needs to run in the caller's scope.&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;quote&lt;/span&gt;
        &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;start_ns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time_ns&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
        &lt;span class="c"&gt;# 'esc(expression_to_run)' ensures the user's code runs&lt;/span&gt;
        &lt;span class="c"&gt;# correctly in their scope, seeing their variables.&lt;/span&gt;
        &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;esc&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expression_to_run&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
        &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;end_ns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time_ns&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
        &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;elapsed_ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end_ns&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start_ns&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1_000_000&lt;/span&gt;
        &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Expression `"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expression_to_run&lt;/span&gt;&lt;span class="x"&gt;)),&lt;/span&gt; &lt;span class="s"&gt;"` executed in: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;round&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elapsed_ms&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;digits&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="x"&gt;),&lt;/span&gt; &lt;span class="s"&gt;" ms"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="c"&gt;# Ensure the macro call evaluates to the result of the user's expression&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# Use the timing macro&lt;/span&gt;
&lt;span class="n"&gt;z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;
&lt;span class="n"&gt;timed_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;@simple_time&lt;/span&gt; &lt;span class="k"&gt;begin&lt;/span&gt;
    &lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Simulate work&lt;/span&gt;
    &lt;span class="n"&gt;z&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;       &lt;span class="c"&gt;# Access local variable 'z'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Result of timed expression: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timed_result&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Should be 100&lt;/span&gt;
&lt;span class="c"&gt;# 'start_ns', 'result', 'end_ns', 'elapsed_ms' from the macro do not leak.&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script delves into &lt;strong&gt;macro hygiene&lt;/strong&gt;, a crucial feature that makes macros safer and easier to compose, and introduces &lt;code&gt;esc()&lt;/code&gt; for intentionally bypassing hygiene when needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Core Concept: Macro Hygiene
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Problem:&lt;/strong&gt; Imagine macros didn't have hygiene. If a macro defined an internal variable &lt;code&gt;x&lt;/code&gt;, and the code calling the macro also used a variable &lt;code&gt;x&lt;/code&gt;, the macro's variable could accidentally overwrite or interfere with the user's variable, leading to chaos.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hygiene Solution:&lt;/strong&gt; Julia macros are &lt;strong&gt;hygienic by default&lt;/strong&gt;. The compiler automatically and invisibly &lt;strong&gt;renames&lt;/strong&gt; variables introduced &lt;em&gt;within&lt;/em&gt; the macro's generated code.

&lt;ul&gt;
&lt;li&gt;In &lt;code&gt;@hygienic_example&lt;/code&gt;, the &lt;code&gt;local x = ...&lt;/code&gt; inside the &lt;code&gt;quote&lt;/code&gt; block does not refer to the global &lt;code&gt;x&lt;/code&gt;. The compiler effectively renames the macro's &lt;code&gt;x&lt;/code&gt; to something unique (like &lt;code&gt;##x#123&lt;/code&gt;), ensuring it cannot clash with any &lt;code&gt;x&lt;/code&gt; in the scope where the macro is called.&lt;/li&gt;
&lt;li&gt;This allows macro authors to use common variable names internally without fear of breaking the user's code.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Bypassing Hygiene: &lt;code&gt;esc(expression)&lt;/code&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Need:&lt;/strong&gt; Sometimes, a macro &lt;em&gt;intentionally&lt;/em&gt; needs to interact with or modify variables in the &lt;strong&gt;calling scope&lt;/strong&gt;. Common examples include macros that perform assignments (like &lt;code&gt;@unhygienic_assignment&lt;/code&gt;) or macros that need to evaluate user-provided code within the user's context (like &lt;code&gt;@simple_time&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;esc()&lt;/code&gt; Function:&lt;/strong&gt; The &lt;code&gt;esc(expression)&lt;/code&gt; function is used &lt;em&gt;inside&lt;/em&gt; the macro's returned &lt;code&gt;quote&lt;/code&gt; block. It marks &lt;code&gt;expression&lt;/code&gt; (which must be an &lt;code&gt;Expr&lt;/code&gt; or &lt;code&gt;Symbol&lt;/code&gt;) as needing to &lt;strong&gt;"escape"&lt;/strong&gt; the hygiene mechanism.

&lt;ul&gt;
&lt;li&gt;When the compiler sees &lt;code&gt;esc(varname)&lt;/code&gt; during macro expansion, it &lt;strong&gt;does not rename&lt;/strong&gt; &lt;code&gt;varname&lt;/code&gt;. It leaves the symbol exactly as it appeared in the macro call.&lt;/li&gt;
&lt;li&gt;In &lt;code&gt;@unhygienic_assignment(y, 123)&lt;/code&gt;, the macro receives &lt;code&gt;varname = :y&lt;/code&gt; and &lt;code&gt;value = 123&lt;/code&gt;. The returned expression &lt;code&gt;:($(esc(varname)) = $value)&lt;/code&gt; becomes &lt;code&gt;:(y = 123)&lt;/code&gt;. Since &lt;code&gt;y&lt;/code&gt; was escaped, this assignment refers to the variable &lt;code&gt;y&lt;/code&gt; in the &lt;em&gt;caller's&lt;/em&gt; scope (creating it if it doesn't exist).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Hygienic Wrapping Pattern (&lt;code&gt;@simple_time&lt;/code&gt;)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Combining Hygiene and Escape:&lt;/strong&gt; Many useful macros &lt;em&gt;wrap&lt;/em&gt; user-provided code, adding some functionality before and/or after. The &lt;code&gt;@simple_time&lt;/code&gt; macro is a classic example.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Correct Implementation:&lt;/strong&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Macro Variables:&lt;/strong&gt; Variables needed &lt;em&gt;by the macro itself&lt;/em&gt; (&lt;code&gt;start_ns&lt;/code&gt;, &lt;code&gt;result&lt;/code&gt;, &lt;code&gt;end_ns&lt;/code&gt;, &lt;code&gt;elapsed_ms&lt;/code&gt;) should be declared &lt;code&gt;local&lt;/code&gt; within the returned &lt;code&gt;quote&lt;/code&gt; block. They will remain hygienic and won't clash with user variables.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;User Expression:&lt;/strong&gt; The code provided &lt;em&gt;by the user&lt;/em&gt; (&lt;code&gt;expression_to_run&lt;/code&gt;) &lt;strong&gt;must be escaped&lt;/strong&gt; (&lt;code&gt;$(esc(expression_to_run))&lt;/code&gt;). This ensures that when the user's code (e.g., &lt;code&gt;sleep(0.05); z * 2&lt;/code&gt;) runs, it does so in the &lt;em&gt;caller's&lt;/em&gt; scope, where variables like &lt;code&gt;z&lt;/code&gt; are correctly defined.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Result:&lt;/strong&gt; The macro adds timing logic using safe, hygienic internal variables, while correctly executing the user's code in their own context. The macro call evaluates to the &lt;em&gt;result&lt;/em&gt; of the user's code (&lt;code&gt;result&lt;/code&gt;), making it composable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Understanding hygiene and &lt;code&gt;esc&lt;/code&gt; is essential for writing correct and robust macros that interact predictably with the code that calls them. Use hygiene by default; use &lt;code&gt;esc&lt;/code&gt; deliberately and carefully when interaction with the caller's scope is intended.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Metaprogramming", "Hygiene":&lt;/strong&gt; Provides a detailed explanation of hygiene and the &lt;code&gt;esc&lt;/code&gt; function with examples.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0109_macro_hygiene_and_esc.jl
&lt;span class="nt"&gt;---&lt;/span&gt; Part 1: Hygienic Macro &lt;span class="nt"&gt;---&lt;/span&gt;
Before macro call: Global x &lt;span class="o"&gt;=&lt;/span&gt; Value from Global Scope
  &lt;span class="o"&gt;(&lt;/span&gt;Macro Expansion Time: Defining hygienic &lt;span class="s1"&gt;'x'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  Inside generated code &lt;span class="o"&gt;(&lt;/span&gt;Runtime&lt;span class="o"&gt;)&lt;/span&gt;: Hygienic x &lt;span class="o"&gt;=&lt;/span&gt; Value from Hygienic Macro
After macro call: Global x &lt;span class="o"&gt;=&lt;/span&gt; Value from Global Scope

&lt;span class="nt"&gt;---&lt;/span&gt; Part 2: Unhygienic Macro &lt;span class="o"&gt;(&lt;/span&gt;using esc&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
  &lt;span class="o"&gt;(&lt;/span&gt;Macro Expansion Time: Assigning to &lt;span class="nb"&gt;caller&lt;/span&gt;&lt;span class="s1"&gt;'s variable)
After macro call: Global y = 123
  (Macro Expansion Time: Assigning to caller'&lt;/span&gt;s variable&lt;span class="o"&gt;)&lt;/span&gt;
After macro call: Global x &lt;span class="o"&gt;=&lt;/span&gt; Value assigned via unhygienic macro

&lt;span class="nt"&gt;---&lt;/span&gt; Part 3: Hygienic Wrapping Macro &lt;span class="o"&gt;(&lt;/span&gt;@simple_time&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
Expression &lt;span class="sb"&gt;`&lt;/span&gt;begin
    &lt;span class="c"&gt;#= ... =#&lt;/span&gt;
    &lt;span class="nb"&gt;sleep&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;0.05&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;#= ... =#&lt;/span&gt;
    Main.z &lt;span class="k"&gt;*&lt;/span&gt; 2
end&lt;span class="sb"&gt;`&lt;/span&gt; executed &lt;span class="k"&gt;in&lt;/span&gt;: 5X.XXX ms &lt;span class="c"&gt;# Actual time will vary slightly&lt;/span&gt;
Result of timed expression: 100

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(The expansion time messages appear during compilation/loading. Runtime messages appear during execution. The exact timing will vary.)&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Generated Functions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;0110_generated_functions_basics.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0110_generated_functions_basics.jl&lt;/span&gt;
&lt;span class="c"&gt;# Introduces @generated functions for compile-time code generation based on types.&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;InteractiveUtils&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nd"&gt;@code_lowered&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;@code_typed&lt;/span&gt; &lt;span class="c"&gt;# For inspecting generated code&lt;/span&gt;

&lt;span class="c"&gt;# --- Standard Function (Runtime Logic) ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Standard Function ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 1. A regular function determines behavior based on runtime *values*.&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; get_container_description_runtime&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# This uses 'isa' checks at runtime.&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;isa&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"Container holds an Integer"&lt;/span&gt;
    &lt;span class="k"&gt;elseif&lt;/span&gt; &lt;span class="k"&gt;isa&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"Container holds a String"&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"Container holds Other type"&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# Define a simple parametric struct&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="nc"&gt; Container&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="x"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;c_int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Container&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;c_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Container&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hello"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Runtime dispatch:"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Input Container{Int}: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;get_container_description_runtime&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c_int&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Input Container{String}: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;get_container_description_runtime&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c_str&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;

&lt;span class="c"&gt;# --- Generated Function (Compile-Time Logic based on Types) ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- @generated Function ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 2. A @generated function runs *during compilation* for each unique&lt;/span&gt;
&lt;span class="c"&gt;#    combination of *input types*. It returns an *expression* (code)&lt;/span&gt;
&lt;span class="c"&gt;#    that becomes the compiled body for those specific types.&lt;/span&gt;
&lt;span class="c"&gt;#    Note: Arguments to the generator are TYPE objects, not values.&lt;/span&gt;
&lt;span class="nd"&gt;@generated&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; get_container_description_compiletime&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Container&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="x"&gt;})&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="x"&gt;}&lt;/span&gt;
    &lt;span class="c"&gt;# This code runs AT COMPILE TIME, once per distinct 'T'.&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  (@generated running for T = &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;T)"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# Logic based *purely* on the type 'T'.&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;:&lt;/span&gt; &lt;span class="kt"&gt;Integer&lt;/span&gt; &lt;span class="c"&gt;# Check if T is a subtype of Integer&lt;/span&gt;
        &lt;span class="c"&gt;# Return the *code* to be compiled for integer containers&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;quote&lt;/span&gt;
            &lt;span class="c"&gt;# This code runs at RUNTIME for Container{Int} etc.&lt;/span&gt;
            &lt;span class="s"&gt;"Container holds an Integer (determined at compile time)"&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;elseif&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
        &lt;span class="c"&gt;# Return the *code* to be compiled for string containers&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;quote&lt;/span&gt;
            &lt;span class="c"&gt;# This code runs at RUNTIME for Container{String}&lt;/span&gt;
            &lt;span class="s"&gt;"Container holds a String (determined at compile time)"&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="c"&gt;# Return the *code* for any other type&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;quote&lt;/span&gt;
            &lt;span class="c"&gt;# This code runs at RUNTIME for other Container{T}&lt;/span&gt;
            &lt;span class="s"&gt;"Container holds Other type (determined at compile time)"&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="c"&gt;# End of @generated function&lt;/span&gt;

&lt;span class="c"&gt;# 3. Call the @generated function.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Compile-time dispatch:"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# First call with Container{Int64}: Triggers generator, compiles, runs.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Input Container{Int}: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;get_container_description_compiletime&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c_int&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;

&lt;span class="c"&gt;# Second call with Container{Int64}: Runs pre-compiled method.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Input Container{Int} (again): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;get_container_description_compiletime&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c_int&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;

&lt;span class="c"&gt;# First call with Container{String}: Triggers generator, compiles, runs.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Input Container{String}: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;get_container_description_compiletime&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c_str&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;

&lt;span class="c"&gt;# --- Inspecting Generated Code (Advanced) ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Inspecting Code ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Code for runtime version (Container{Int}):"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# Explicitly print the result of @code_typed&lt;/span&gt;
&lt;span class="c"&gt;# Note: @code_typed shows optimized code *after* type inference.&lt;/span&gt;
&lt;span class="c"&gt;# The `isa` check might be optimized away for this specific input `c_int`,&lt;/span&gt;
&lt;span class="c"&gt;# but the branching structure would exist in the general method.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@code_typed&lt;/span&gt; &lt;span class="n"&gt;get_container_description_runtime&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c_int&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Code for compile-time version (Container{Int}):"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# Explicitly print the result of @code_typed&lt;/span&gt;
&lt;span class="c"&gt;# This might trigger the "@generated running..." message again as it compiles&lt;/span&gt;
&lt;span class="c"&gt;# the specific method needed for inspection.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@code_typed&lt;/span&gt; &lt;span class="n"&gt;get_container_description_compiletime&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c_int&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces &lt;strong&gt;&lt;code&gt;@generated&lt;/code&gt; functions&lt;/strong&gt;, the second major tool for compile-time metaprogramming in Julia. Unlike macros which operate on syntax, &lt;code&gt;@generated&lt;/code&gt; functions operate based on &lt;strong&gt;types&lt;/strong&gt; inferred during compilation, allowing for extreme specialization of code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Core Concept: Compile-Time Code Generation Based on Types
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@generated function func(args...) ... end&lt;/code&gt;:&lt;/strong&gt; Defines a generated function.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Execution Model:&lt;/strong&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;First Call (Type Signature):&lt;/strong&gt; When Julia encounters a call to &lt;code&gt;@generated&lt;/code&gt; function &lt;code&gt;func&lt;/code&gt; with a &lt;em&gt;new combination of argument types&lt;/em&gt; (e.g., &lt;code&gt;get_container_description_compiletime(::Container{Int64})&lt;/code&gt;), it &lt;strong&gt;runs the body&lt;/strong&gt; of the &lt;code&gt;@generated&lt;/code&gt; function definition &lt;strong&gt;at compile time&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Input = Types:&lt;/strong&gt; The arguments passed to the generator code are &lt;strong&gt;Type objects&lt;/strong&gt; (e.g., &lt;code&gt;T&lt;/code&gt; will be &lt;code&gt;Int64&lt;/code&gt;, not the value &lt;code&gt;10&lt;/code&gt;). You cannot access the &lt;em&gt;values&lt;/em&gt; of the arguments inside the generator body.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Output = Code (&lt;code&gt;Expr&lt;/code&gt;):&lt;/strong&gt; The generator body &lt;strong&gt;must return&lt;/strong&gt; a Julia expression (&lt;code&gt;Expr&lt;/code&gt; object, usually created with &lt;code&gt;quote...end&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Compilation:&lt;/strong&gt; Julia takes the returned expression and &lt;strong&gt;compiles it&lt;/strong&gt; as the &lt;strong&gt;method body&lt;/strong&gt; specifically for that combination of input types.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Runtime Execution:&lt;/strong&gt; The compiled, specialized method body is then executed at runtime.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Subsequent Calls:&lt;/strong&gt; For &lt;em&gt;all future calls&lt;/em&gt; with the &lt;strong&gt;same argument types&lt;/strong&gt;, Julia skips the generator step and directly executes the already-compiled, specialized method body.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Contrast with Macros:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Macros:&lt;/strong&gt; Run earlier (parse time), operate on syntax (&lt;code&gt;Expr&lt;/code&gt;), unaware of types.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@generated&lt;/code&gt;:&lt;/strong&gt; Run later (compile/type-inference time), operate on types (&lt;code&gt;Type&lt;/code&gt;), unaware of specific syntax used by the caller.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Example Walkthrough
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;get_container_description_runtime&lt;/code&gt;:&lt;/strong&gt; This standard function uses runtime &lt;code&gt;isa&lt;/code&gt; checks. Every time it's called, it potentially performs these type checks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;get_container_description_compiletime&lt;/code&gt;:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;When called with &lt;code&gt;c_int&lt;/code&gt; (&lt;code&gt;Container{Int64}&lt;/code&gt;), the generator runs (&lt;code&gt;println(" (@generated running...)")&lt;/code&gt;). &lt;code&gt;T&lt;/code&gt; is &lt;code&gt;Int64&lt;/code&gt;. The &lt;code&gt;if T &amp;lt;: Integer&lt;/code&gt; branch matches. The generator &lt;em&gt;returns the expression&lt;/em&gt; &lt;code&gt;quote "Container holds an Integer..." end&lt;/code&gt;. Julia compiles this simple expression (which just returns a string constant) as the method for &lt;code&gt;Container{Int64}&lt;/code&gt;. This compiled method is then run.&lt;/li&gt;
&lt;li&gt;When called with &lt;code&gt;c_int&lt;/code&gt; &lt;em&gt;again&lt;/em&gt;, the generator does &lt;strong&gt;not&lt;/strong&gt; run. The already compiled method (which just returns the string) is executed instantly.&lt;/li&gt;
&lt;li&gt;When called with &lt;code&gt;c_str&lt;/code&gt; (&lt;code&gt;Container{String}&lt;/code&gt;), the generator runs again. &lt;code&gt;T&lt;/code&gt; is &lt;code&gt;String&lt;/code&gt;. The &lt;code&gt;elseif T == String&lt;/code&gt; branch matches. The generator returns the appropriate &lt;code&gt;quote&lt;/code&gt; block, which Julia compiles as the method for &lt;code&gt;Container{String}&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Zero-Cost Abstraction Achieved
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Inspection:&lt;/strong&gt; Using &lt;code&gt;println(@code_typed(...))&lt;/code&gt; confirms the benefit:

&lt;ul&gt;
&lt;li&gt;The runtime version's typed code might still show branching logic (depending on optimization level and context), representing the runtime &lt;code&gt;isa&lt;/code&gt; checks. Your output &lt;code&gt;CodeInfo( ... return "Container holds an Integer" ) =&amp;gt; String&lt;/code&gt; suggests the compiler &lt;em&gt;was&lt;/em&gt; able to constant-propagate the &lt;code&gt;isa(c_int.value, Int)&lt;/code&gt; check for this specific call, but the general method still contains the branching logic.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;@generated&lt;/code&gt; version's typed code (for &lt;code&gt;Container{Int}&lt;/code&gt;) shows &lt;strong&gt;no branching&lt;/strong&gt;; it compiles &lt;em&gt;directly&lt;/em&gt; to &lt;code&gt;CodeInfo( return "Container holds an Integer (determined at compile time)" ) =&amp;gt; String&lt;/code&gt;. All the &lt;code&gt;if/elseif/else&lt;/code&gt; logic based on the &lt;em&gt;type&lt;/em&gt; &lt;code&gt;T&lt;/code&gt; happened &lt;strong&gt;at compile time&lt;/strong&gt; and vanished entirely from the runtime code for this specific &lt;code&gt;T&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Performance:&lt;/strong&gt; &lt;code&gt;@generated&lt;/code&gt; functions allow you to write generic-looking code where the dispatch logic (based on types) is resolved entirely during compilation, resulting in highly specialized, efficient runtime code with zero dispatch overhead. This is a key technique for implementing zero-cost abstractions based on type information.&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Restrictions
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;You &lt;strong&gt;cannot access argument values&lt;/strong&gt; inside the generator body, only their types.&lt;/li&gt;
&lt;li&gt;You cannot cause side effects (like modifying global state) inside the generator body that affect runtime behavior (though &lt;code&gt;println&lt;/code&gt; for debugging is okay). The generator's only job is to return the code expression.&lt;/li&gt;
&lt;/ul&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Metaprogramming", "@generated Functions":&lt;/strong&gt; Provides the definitive explanation and rules for generated functions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Base Documentation, &lt;code&gt;@generated&lt;/code&gt;:&lt;/strong&gt; Macro documentation.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0110_generated_functions_basics.jl
&lt;span class="nt"&gt;---&lt;/span&gt; Standard Function &lt;span class="nt"&gt;---&lt;/span&gt;
Runtime dispatch:
  Input Container&lt;span class="o"&gt;{&lt;/span&gt;Int&lt;span class="o"&gt;}&lt;/span&gt;: Container holds an Integer
  Input Container&lt;span class="o"&gt;{&lt;/span&gt;String&lt;span class="o"&gt;}&lt;/span&gt;: Container holds a String

&lt;span class="nt"&gt;---&lt;/span&gt; @generated Function &lt;span class="nt"&gt;---&lt;/span&gt;

Compile-time dispatch:
  &lt;span class="o"&gt;(&lt;/span&gt;@generated running &lt;span class="k"&gt;for &lt;/span&gt;T &lt;span class="o"&gt;=&lt;/span&gt; Int64&lt;span class="o"&gt;)&lt;/span&gt;
  Input Container&lt;span class="o"&gt;{&lt;/span&gt;Int&lt;span class="o"&gt;}&lt;/span&gt;: Container holds an Integer &lt;span class="o"&gt;(&lt;/span&gt;determined at compile &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  Input Container&lt;span class="o"&gt;{&lt;/span&gt;Int&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;again&lt;span class="o"&gt;)&lt;/span&gt;: Container holds an Integer &lt;span class="o"&gt;(&lt;/span&gt;determined at compile &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;(&lt;/span&gt;@generated running &lt;span class="k"&gt;for &lt;/span&gt;T &lt;span class="o"&gt;=&lt;/span&gt; String&lt;span class="o"&gt;)&lt;/span&gt;
  Input Container&lt;span class="o"&gt;{&lt;/span&gt;String&lt;span class="o"&gt;}&lt;/span&gt;: Container holds a String &lt;span class="o"&gt;(&lt;/span&gt;determined at compile &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nt"&gt;---&lt;/span&gt; Inspecting Code &lt;span class="nt"&gt;---&lt;/span&gt;
Code &lt;span class="k"&gt;for &lt;/span&gt;runtime version &lt;span class="o"&gt;(&lt;/span&gt;Container&lt;span class="o"&gt;{&lt;/span&gt;Int&lt;span class="o"&gt;})&lt;/span&gt;:
CodeInfo&lt;span class="o"&gt;(&lt;/span&gt;
1 ─     nothing::Nothing
└──     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;"Container holds an Integer"&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; String

Code &lt;span class="k"&gt;for &lt;/span&gt;compile-time version &lt;span class="o"&gt;(&lt;/span&gt;Container&lt;span class="o"&gt;{&lt;/span&gt;Int&lt;span class="o"&gt;})&lt;/span&gt;:
  &lt;span class="o"&gt;(&lt;/span&gt;@generated running &lt;span class="k"&gt;for &lt;/span&gt;T &lt;span class="o"&gt;=&lt;/span&gt; Int64&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Note: Runs again for inspection call&lt;/span&gt;
CodeInfo&lt;span class="o"&gt;(&lt;/span&gt;
1 ─     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;"Container holds an Integer (determined at compile time)"&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; String

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(The &lt;code&gt;@code_typed&lt;/code&gt; output confirms the specialized, non-branching code generated by the &lt;code&gt;@generated&lt;/code&gt; function for the &lt;code&gt;Int64&lt;/code&gt; case.)&lt;/em&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;0111_generated_loop_unroll.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0111_generated_loop_unroll.jl&lt;/span&gt;
&lt;span class="c"&gt;# Demonstrates loop unrolling using @generated functions for NTuples.&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BenchmarkTools&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nd"&gt;@btime&lt;/span&gt;

&lt;span class="c"&gt;# --- Runtime Loop Version (for Tuples/AbstractVectors) ---&lt;/span&gt;

&lt;span class="c"&gt;# 1. Standard function using a runtime loop.&lt;/span&gt;
&lt;span class="c"&gt;#    Works for any Tuple or AbstractVector.&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; dot_runtime&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Union&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;Tuple&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;AbstractVector&lt;/span&gt;&lt;span class="x"&gt;},&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Union&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;Tuple&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;AbstractVector&lt;/span&gt;&lt;span class="x"&gt;})&lt;/span&gt;
    &lt;span class="n"&gt;len_a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;len_b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# Basic error check (could be more robust)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;len_a&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;len_b&lt;/span&gt;
        &lt;span class="n"&gt;throw&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;DimensionMismatch&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Vectors must have same length"&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt; &lt;span class="c"&gt;# Use Float64 for accumulation&lt;/span&gt;
    &lt;span class="nd"&gt;@inbounds&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;len_a&lt;/span&gt;
        &lt;span class="c"&gt;# Runtime loop: involves counter, bounds check (unless @inbounds), branching.&lt;/span&gt;
        &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# --- Compile-Time Unrolled Version (for NTuples) ---&lt;/span&gt;

&lt;span class="c"&gt;# 2. @generated function specifically for NTuples.&lt;/span&gt;
&lt;span class="c"&gt;#    'NTuple{N, T}' is a fixed-size, stack-allocated tuple where 'N' (length)&lt;/span&gt;
&lt;span class="c"&gt;#    is part of the type information *available at compile time*.&lt;/span&gt;
&lt;span class="nd"&gt;@generated&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; dot_compiletime_unrolled&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;NTuple&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="x"&gt;},&lt;/span&gt;
            &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;NTuple&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="x"&gt;}&lt;/span&gt;
        &lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;:&lt;/span&gt;&lt;span class="kt"&gt;Number&lt;/span&gt;&lt;span class="x"&gt;}&lt;/span&gt; &lt;span class="c"&gt;# Constrain T to be Number, N is length&lt;/span&gt;

    &lt;span class="c"&gt;# This code runs AT COMPILE TIME. 'N' is the known length.&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  (@generated running dot_unrolled for N=&lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;N, T=&lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;T)"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# 3. Start building the expression tree for the function body.&lt;/span&gt;
    &lt;span class="c"&gt;#    We initialize the expression to the first multiplication.&lt;/span&gt;
    &lt;span class="c"&gt;#    Handles N=0 case implicitly (though perhaps needs explicit check).&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;zero&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# Return 0.0 if tuples are empty&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="c"&gt;# Start with the first element's calculation&lt;/span&gt;
    &lt;span class="n"&gt;ex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;])&lt;/span&gt;

    &lt;span class="c"&gt;# 4. This loop runs AT COMPILE TIME, from i=2 up to N.&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;
        &lt;span class="c"&gt;# 5. Append the next term '+ a[i] * b[i]' to the expression tree.&lt;/span&gt;
        &lt;span class="n"&gt;ex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="x"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"    Generated code for N=&lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;N: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# 6. Return the fully unrolled expression tree.&lt;/span&gt;
    &lt;span class="c"&gt;#    This expression becomes the *entire* compiled body for this NTuple size.&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# --- Benchmarking ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Benchmarking ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Define input data&lt;/span&gt;
&lt;span class="c"&gt;# Use NTuple for the unrolled version&lt;/span&gt;
&lt;span class="n"&gt;a_ntup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;3.0&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;4.0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# NTuple{4, Float64}&lt;/span&gt;
&lt;span class="n"&gt;b_ntup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;5.0&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;6.0&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;7.0&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;8.0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# NTuple{4, Float64}&lt;/span&gt;

&lt;span class="c"&gt;# Use Vectors for the runtime version (for fair comparison of loop vs unroll)&lt;/span&gt;
&lt;span class="n"&gt;a_vec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;3.0&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;4.0&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt; &lt;span class="c"&gt;# Vector{Float64}&lt;/span&gt;
&lt;span class="n"&gt;b_vec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;5.0&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;6.0&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;7.0&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;8.0&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt; &lt;span class="c"&gt;# Vector{Float64}&lt;/span&gt;

&lt;span class="c"&gt;# Benchmark the standard loop version&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Benchmarking dot_runtime (Vector input):"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@btime&lt;/span&gt; &lt;span class="n"&gt;dot_runtime&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;a_vec&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;b_vec&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Benchmark the @generated unrolled version&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Benchmarking dot_compiletime_unrolled (NTuple input):"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# First call triggers generator, subsequent calls use compiled code.&lt;/span&gt;
&lt;span class="nd"&gt;@btime&lt;/span&gt; &lt;span class="n"&gt;dot_compiletime_unrolled&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;a_ntup&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;b_ntup&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# --- Verification ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Verification ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;res_runtime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dot_runtime&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a_vec&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b_vec&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;res_unrolled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dot_compiletime_unrolled&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a_ntup&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b_ntup&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Runtime result:   "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res_runtime&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Unrolled result:  "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res_unrolled&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Results match:    "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res_runtime&lt;/span&gt; &lt;span class="n"&gt;≈&lt;/span&gt; &lt;span class="n"&gt;res_unrolled&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script showcases a powerful application of &lt;strong&gt;&lt;code&gt;@generated&lt;/code&gt; functions&lt;/strong&gt;: achieving &lt;strong&gt;compile-time loop unrolling&lt;/strong&gt; for operations on fixed-size collections like &lt;code&gt;NTuple&lt;/code&gt;. This is a classic technique for maximizing performance by eliminating loop overhead entirely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Core Concept: Loop Unrolling
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Runtime Loops:&lt;/strong&gt; A standard &lt;code&gt;for&lt;/code&gt; loop (like in &lt;code&gt;dot_runtime&lt;/code&gt;) involves runtime overhead:

&lt;ul&gt;
&lt;li&gt;Incrementing and checking the loop counter (&lt;code&gt;i&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Performing bounds checks on array accesses (&lt;code&gt;a[i]&lt;/code&gt;, &lt;code&gt;b[i]&lt;/code&gt;) unless disabled by &lt;code&gt;@inbounds&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Conditional branching at the end of each iteration.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Loop Unrolling:&lt;/strong&gt; For loops with a small, &lt;em&gt;fixed&lt;/em&gt; number of iterations known &lt;em&gt;at compile time&lt;/em&gt;, these overheads can be eliminated by &lt;strong&gt;unrolling&lt;/strong&gt; the loop. The compiler replaces the loop structure with a straight sequence of the operations from each iteration.

&lt;ul&gt;
&lt;li&gt;For N=4, &lt;code&gt;dot_compiletime_unrolled&lt;/code&gt; aims to generate code equivalent to: &lt;code&gt;a[1]*b[1] + a[2]*b[2] + a[3]*b[3] + a[4]*b[4]&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Benefit:&lt;/strong&gt; The unrolled version contains only the essential arithmetic operations, with no counters, checks, or branches. This allows the CPU to execute the instructions more efficiently, often utilizing techniques like instruction pipelining and potentially SIMD more effectively.&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Using &lt;code&gt;@generated&lt;/code&gt; for Unrolling
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;NTuple{N, T}&lt;/code&gt;:&lt;/strong&gt; The key enabler is &lt;code&gt;NTuple{N, T}&lt;/code&gt;. It's an immutable, &lt;code&gt;isbits&lt;/code&gt; tuple type where the &lt;strong&gt;length &lt;code&gt;N&lt;/code&gt; is part of the type information&lt;/strong&gt;. This means &lt;code&gt;N&lt;/code&gt; is known to the compiler during type inference.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generator Logic:&lt;/strong&gt;

&lt;ol&gt;
&lt;li&gt; The &lt;code&gt;@generated function dot_compiletime_unrolled&lt;/code&gt; receives the &lt;em&gt;types&lt;/em&gt; &lt;code&gt;NTuple{N, T}&lt;/code&gt; as input. The &lt;code&gt;where {N, T&amp;lt;:Number}&lt;/code&gt; clause extracts the compile-time constant &lt;code&gt;N&lt;/code&gt; (the length) and the element type &lt;code&gt;T&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; The code inside the generator runs &lt;strong&gt;at compile time&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; It uses a standard Julia &lt;code&gt;for i in 2:N&lt;/code&gt; loop (running &lt;em&gt;at compile time&lt;/em&gt;) to programmatically build an &lt;code&gt;Expr&lt;/code&gt; object (&lt;code&gt;ex&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt; In each iteration of this compile-time loop, it appends the next term (&lt;code&gt;+ a[$i] * b[$i]&lt;/code&gt;) to the &lt;code&gt;Expr&lt;/code&gt; tree.&lt;/li&gt;
&lt;li&gt; The final &lt;code&gt;Expr&lt;/code&gt; returned by the generator is the fully unrolled sequence of additions and multiplications.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero-Cost Abstraction:&lt;/strong&gt; Julia compiles this returned expression as the &lt;em&gt;entire body&lt;/em&gt; of the function specifically for that &lt;code&gt;N&lt;/code&gt;. When you call &lt;code&gt;dot_compiletime_unrolled(a_ntup, b_ntup)&lt;/code&gt; at runtime, you execute the straight-line, unrolled code directly. The generic function definition with the compile-time loop has vanished, achieving a &lt;strong&gt;zero-cost abstraction&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Benchmarking Results
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The benchmark comparison between &lt;code&gt;dot_runtime&lt;/code&gt; (using &lt;code&gt;Vector&lt;/code&gt;s and a runtime loop) and &lt;code&gt;dot_compiletime_unrolled&lt;/code&gt; (using &lt;code&gt;NTuple&lt;/code&gt;s and compile-time unrolling) should show the unrolled version is significantly faster for small &lt;code&gt;N&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Important:&lt;/strong&gt; This specific &lt;code&gt;@generated&lt;/code&gt; function only works for &lt;code&gt;NTuple&lt;/code&gt;. &lt;code&gt;dot_runtime&lt;/code&gt; is more general but potentially slower due to the loop overhead (and potential heap allocation if Vectors are large or escape). Using &lt;code&gt;StaticArrays.jl&lt;/code&gt; provides similar performance benefits for fixed-size arrays with a more convenient interface than manual &lt;code&gt;@generated&lt;/code&gt; functions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Loop unrolling via &lt;code&gt;@generated&lt;/code&gt; functions is a powerful technique for optimizing performance-critical code operating on small, fixed-size data structures, commonly encountered in fields like graphics, physics simulations, and low-level signal processing.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Metaprogramming", "@generated Functions":&lt;/strong&gt; Shows examples including generating specialized code based on type parameters.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Base Documentation, &lt;code&gt;NTuple&lt;/code&gt;:&lt;/strong&gt; Describes the fixed-size tuple type where length is part of the type.&lt;/li&gt;
&lt;li&gt;(Loop unrolling is a standard compiler optimization technique).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;To run the script:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Requires &lt;code&gt;BenchmarkTools.jl&lt;/code&gt; installed)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0111_generated_loop_unroll.jl
&lt;span class="nt"&gt;---&lt;/span&gt; Benchmarking &lt;span class="nt"&gt;---&lt;/span&gt;
Benchmarking dot_runtime &lt;span class="o"&gt;(&lt;/span&gt;Vector input&lt;span class="o"&gt;)&lt;/span&gt;:
  2.888 ns &lt;span class="o"&gt;(&lt;/span&gt;0 allocations: 0 bytes&lt;span class="o"&gt;)&lt;/span&gt;

Benchmarking dot_compiletime_unrolled &lt;span class="o"&gt;(&lt;/span&gt;NTuple input&lt;span class="o"&gt;)&lt;/span&gt;:
  &lt;span class="o"&gt;(&lt;/span&gt;@generated running dot_unrolled &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="nv"&gt;N&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4, &lt;span class="nv"&gt;T&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Float64&lt;span class="o"&gt;)&lt;/span&gt;
    Generated code &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="nv"&gt;N&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4: &lt;span class="o"&gt;((&lt;/span&gt;a[1] &lt;span class="k"&gt;*&lt;/span&gt; b[1] + a[2] &lt;span class="k"&gt;*&lt;/span&gt; b[2]&lt;span class="o"&gt;)&lt;/span&gt; + a[3] &lt;span class="k"&gt;*&lt;/span&gt; b[3]&lt;span class="o"&gt;)&lt;/span&gt; + a[4] &lt;span class="k"&gt;*&lt;/span&gt; b[4]
  1.490 ns &lt;span class="o"&gt;(&lt;/span&gt;0 allocations: 0 bytes&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nt"&gt;---&lt;/span&gt; Verification &lt;span class="nt"&gt;---&lt;/span&gt;
Runtime result:   70.0
Unrolled result:  70.0
Results match:    &lt;span class="nb"&gt;true&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Eval Vs Compile
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;0112_eval_and_world_age.md&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;While Julia &lt;em&gt;can&lt;/em&gt; execute code represented as data structures (&lt;code&gt;Expr&lt;/code&gt;) at runtime using the &lt;code&gt;eval()&lt;/code&gt; function, this approach is fundamentally different from compile-time metaprogramming (macros, &lt;code&gt;@generated&lt;/code&gt; functions) and generally unsuitable for high-performance code. Understanding &lt;code&gt;eval&lt;/code&gt;'s limitations and the related "world age" concept solidifies &lt;em&gt;why&lt;/em&gt; compile-time code generation is preferred.&lt;/p&gt;




&lt;h2&gt;
  
  
  Runtime Code Execution: &lt;code&gt;eval()&lt;/code&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What it does:&lt;/strong&gt; &lt;code&gt;eval(expression::Expr)&lt;/code&gt; takes an &lt;code&gt;Expr&lt;/code&gt; object and executes it as code within the &lt;strong&gt;global scope&lt;/strong&gt; of the module where &lt;code&gt;eval&lt;/code&gt; is called. It effectively invokes the Julia compiler and execution engine &lt;strong&gt;at runtime&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Example:&lt;/strong&gt; &lt;code&gt;eval(:(x = 10 + 5))&lt;/code&gt; compiles and runs &lt;code&gt;x = 15&lt;/code&gt;, creating or modifying the global variable &lt;code&gt;x&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why &lt;code&gt;eval()&lt;/code&gt; is Slow and Problematic for Performance
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Runtime Compilation Overhead:&lt;/strong&gt; Every time &lt;code&gt;eval&lt;/code&gt; is called with a new expression (or one that hasn't been cached), it must invoke the Julia compiler (type inference, optimization, machine code generation). This is a significant overhead compared to executing already-compiled code.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Global Scope:&lt;/strong&gt; &lt;code&gt;eval&lt;/code&gt; operates in the global scope. As established in Module 6, code relying heavily on &lt;strong&gt;non-constant global variables&lt;/strong&gt; is inherently &lt;strong&gt;type-unstable&lt;/strong&gt; and slow because the compiler cannot specialize code effectively. &lt;code&gt;eval&lt;/code&gt; compounds this problem by both reading &lt;em&gt;and potentially defining&lt;/em&gt; global variables dynamically.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Type Instability:&lt;/strong&gt; Because &lt;code&gt;eval&lt;/code&gt; runs arbitrary code at runtime, the compiler usually cannot predict the type of the value returned by &lt;code&gt;eval&lt;/code&gt;, leading to type instability in the code that uses the result.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  The "World Age" Problem
&lt;/h2&gt;

&lt;p&gt;This is a subtle but important concept related to Julia's JIT compilation and method dispatch, which particularly affects runtime &lt;code&gt;eval&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Compilation and World Age:&lt;/strong&gt; Julia compiles functions &lt;em&gt;just-in-time&lt;/em&gt;. When a function is compiled, it "knows about" all the methods and global variables that exist at that specific moment (its "world age"). Julia maintains a global counter for this "world age," incrementing it whenever a new method is defined or a relevant global changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Rule:&lt;/strong&gt; A function running in an older "world" &lt;strong&gt;cannot call&lt;/strong&gt; methods defined in a newer "world." This prevents inconsistencies during dynamic code updates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;eval&lt;/code&gt; Creates a New World:&lt;/strong&gt; When &lt;code&gt;eval&lt;/code&gt; defines a new function or method at runtime, it &lt;strong&gt;increments the world age counter&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Conflict:&lt;/strong&gt; If you call &lt;code&gt;eval&lt;/code&gt; &lt;em&gt;inside&lt;/em&gt; a function &lt;code&gt;f&lt;/code&gt; to define a new function &lt;code&gt;g&lt;/code&gt;, and then immediately try to call &lt;code&gt;g()&lt;/code&gt; from within that &lt;em&gt;same&lt;/em&gt; execution of &lt;code&gt;f&lt;/code&gt;, you will likely get a &lt;code&gt;MethodError&lt;/code&gt;. Why? Because &lt;code&gt;f&lt;/code&gt; was compiled in an older world age and doesn't "see" the &lt;code&gt;g&lt;/code&gt; function that &lt;code&gt;eval&lt;/code&gt; just created in the newer world.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; run_eval&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Current world: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_world_counter&lt;/span&gt;&lt;span class="x"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;eval&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; my_new_func&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt; &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello from new func!"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"World after eval: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_world_counter&lt;/span&gt;&lt;span class="x"&gt;())&lt;/span&gt; &lt;span class="c"&gt;# Incremented!&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;
        &lt;span class="n"&gt;my_new_func&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt; &lt;span class="c"&gt;# Error! run_eval() lives in the older world.&lt;/span&gt;
    &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
        &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Caught Error: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="c"&gt;# run_eval() # This would error inside&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  &lt;code&gt;Base.invokelatest()&lt;/code&gt;: The Slow Workaround
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Purpose:&lt;/strong&gt; &lt;code&gt;Base.invokelatest(f, args...)&lt;/code&gt; is designed specifically to overcome the world age problem for &lt;strong&gt;interactive use&lt;/strong&gt; (like the REPL).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How it Works:&lt;/strong&gt; It explicitly tells Julia: "Look up the &lt;em&gt;absolute newest&lt;/em&gt; definition of function &lt;code&gt;f&lt;/code&gt; (in the latest world age) and call it with &lt;code&gt;args&lt;/code&gt;, even if my current function doesn't know about it yet."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance:&lt;/strong&gt; &lt;code&gt;invokelatest&lt;/code&gt; is &lt;strong&gt;extremely slow&lt;/strong&gt; and &lt;strong&gt;type-unstable&lt;/strong&gt; by design. It involves runtime method lookups and cannot be optimized by the compiler. It completely defeats the purpose of Julia's JIT specialization.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Guideline:&lt;/strong&gt; &lt;code&gt;invokelatest&lt;/code&gt; is a tool for REPLs, debuggers, and interactive widgets. It should &lt;strong&gt;never&lt;/strong&gt; appear in performance-critical code.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Conclusion: Compile-Time Metaprogramming is Key
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;eval&lt;/code&gt; and &lt;code&gt;invokelatest&lt;/code&gt; are for &lt;strong&gt;runtime flexibility&lt;/strong&gt;, primarily in interactive contexts. They come at a significant performance cost.&lt;/li&gt;
&lt;li&gt;High-performance code generation in Julia relies on &lt;strong&gt;compile-time metaprogramming&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Macros (&lt;code&gt;@macro&lt;/code&gt;)&lt;/strong&gt;: Transform &lt;strong&gt;syntax&lt;/strong&gt; at parse time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generated Functions (&lt;code&gt;@generated&lt;/code&gt;)&lt;/strong&gt;: Generate specialized code based on &lt;strong&gt;types&lt;/strong&gt; at compile time.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;These tools allow you to perform complex code generation and optimization &lt;em&gt;before&lt;/em&gt; runtime, leveraging Julia's JIT compiler to produce efficient, specialized machine code, thus achieving true zero-cost abstractions. If you feel the need to use &lt;code&gt;eval&lt;/code&gt; within a performance-sensitive function, it's almost always a sign that a macro or &lt;code&gt;@generated&lt;/code&gt; function is the more appropriate (and faster) solution.&lt;/li&gt;

&lt;/ul&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Metaprogramming", "Eval":&lt;/strong&gt; Describes &lt;code&gt;eval&lt;/code&gt; and its global scope behavior.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Calling C and Fortran Code" / "Embedding Julia" / &lt;code&gt;devdocs&lt;/code&gt;:&lt;/strong&gt; Discussions of the "world age counter" often appear in advanced sections related to compilation and runtime interaction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Base Documentation, &lt;code&gt;Base.invokelatest&lt;/code&gt;:&lt;/strong&gt; Explains its purpose for calling functions defined after the caller was compiled. Explicitly notes performance implications.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h1&gt;
  
  
  Module 12: System Integration and Interoperability
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Calling C Code
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;0113_module_intro.md&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This module focuses on &lt;strong&gt;system integration and interoperability&lt;/strong&gt;, bridging the gap between high-performance Julia code and the vast ecosystem of existing native libraries (C, C++, Fortran) and operating system interfaces. Mastering this is essential for building real-world, high-performance systems.&lt;/p&gt;




&lt;h2&gt;
  
  
  Beyond Pure Julia: Leveraging Native Code
&lt;/h2&gt;

&lt;p&gt;While Julia itself is exceptionally fast, achieving performance often comparable to C, much of the world's highly optimized code for specific tasks (numerical libraries, hardware drivers, OS primitives) is written in C or C++. Julia was &lt;strong&gt;designed from the ground up&lt;/strong&gt; for seamless interoperability with these languages. We don't call C because Julia is &lt;em&gt;slow&lt;/em&gt;, but to leverage existing, battle-tested, and often hardware-specific native code for tasks like:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Specialized Libraries:&lt;/strong&gt; Utilizing highly optimized libraries like BLAS (Basic Linear Algebra Subprograms), LAPACK, Intel MKL, FFTW, or custom vendor libraries for hardware acceleration.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Hardware Interaction:&lt;/strong&gt; Interfacing directly with network card drivers, GPU APIs (beyond high-level packages), or other hardware through their native C interfaces.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Operating System Primitives:&lt;/strong&gt; Accessing low-level OS features not exposed directly in Julia's standard library (e.g., advanced process control, specific system calls, memory mapping options).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Legacy Codebases:&lt;/strong&gt; Integrating Julia components into larger systems predominantly written in C or C++.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Julia's Interoperability Strengths
&lt;/h2&gt;

&lt;p&gt;Julia's design makes C interoperability remarkably clean and efficient:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;isbits&lt;/code&gt; Layout:&lt;/strong&gt; Immutable &lt;code&gt;struct&lt;/code&gt;s composed of primitive types (&lt;code&gt;isbits&lt;/code&gt;) have a memory layout identical to their C &lt;code&gt;struct&lt;/code&gt; counterparts (Module 9), allowing them to be passed directly without conversion or serialization.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Native Pointers (&lt;code&gt;Ptr{T}&lt;/code&gt;):&lt;/strong&gt; Julia has a first-class pointer type (&lt;code&gt;Ptr&lt;/code&gt;) that maps directly to C pointers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ccall&lt;/code&gt;:&lt;/strong&gt; The built-in &lt;code&gt;ccall&lt;/code&gt; function provides a direct, low-overhead mechanism to call functions within compiled shared libraries (&lt;code&gt;.so&lt;/code&gt;, &lt;code&gt;.dll&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No GIL:&lt;/strong&gt; Julia's multi-threading model allows C library calls from different threads to run truly in parallel without interference from a Global Interpreter Lock.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GC Safety:&lt;/strong&gt; The interaction between &lt;code&gt;ccall&lt;/code&gt; and the Garbage Collector ensures that Julia objects passed by pointer to C are "pinned" (not moved or collected) during the C call.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The &lt;code&gt;ccall&lt;/code&gt; Interface and Responsibility
&lt;/h2&gt;

&lt;p&gt;The primary tool we will use is &lt;code&gt;ccall&lt;/code&gt;. It allows calling C functions (and by extension, C++ functions exposed via &lt;code&gt;extern "C"&lt;/code&gt;) as if they were native Julia functions. However, this power comes with significant responsibility:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Type Correctness is Absolute:&lt;/strong&gt; &lt;code&gt;ccall&lt;/code&gt; bypasses Julia's dynamic type checking. You must provide the &lt;strong&gt;exact&lt;/strong&gt; C function signature (return type and argument types) to &lt;code&gt;ccall&lt;/code&gt;. Mismatches in type size, alignment, or calling convention will lead to &lt;strong&gt;undefined behavior&lt;/strong&gt;, typically &lt;strong&gt;segmentation faults&lt;/strong&gt; or silent memory corruption, not Julia &lt;code&gt;MethodError&lt;/code&gt;s.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory Management:&lt;/strong&gt; You are responsible for understanding the memory ownership rules of the C library. Who allocates? Who frees? Does the C function return a pointer you now own, or a pointer to static memory you must not free? Mistakes here lead to memory leaks or double-free crashes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Calling Conventions:&lt;/strong&gt; &lt;code&gt;ccall&lt;/code&gt; handles the platform's default C calling convention, but awareness may be needed for non-standard conventions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This module will guide you through using &lt;code&gt;ccall&lt;/code&gt; safely and effectively, starting with simple examples and progressing to passing complex data like arrays and structs, and even handling callbacks.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Calling C and Fortran Code":&lt;/strong&gt; The primary guide for &lt;code&gt;ccall&lt;/code&gt; and related interoperability features.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;C Language Standards / ABI Documentation:&lt;/strong&gt; (External) Necessary for understanding the C side of the interface (type sizes, alignment, calling conventions).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;0114_ccall_basics_simple.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0114_ccall_basics_simple.jl&lt;/span&gt;
&lt;span class="c"&gt;# Demonstrates basic 'ccall' usage with simple C standard library functions,&lt;/span&gt;
&lt;span class="c"&gt;# highlighting different ways to specify the library.&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Libc&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Clong&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cvoid&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;C_NULL&lt;/span&gt; &lt;span class="c"&gt;# Import C types explicitly&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Calling C Standard Library Functions via ccall ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 1. Basic ccall Syntax:&lt;/span&gt;
&lt;span class="c"&gt;#    result = ccall( Fspec, ReturnType, ArgTypes, ArgValues... )&lt;/span&gt;

&lt;span class="c"&gt;# 2. Finding the C Standard Library:&lt;/span&gt;
&lt;span class="c"&gt;#    There are multiple ways to specify 'libc':&lt;/span&gt;
&lt;span class="c"&gt;#    a) "" or C_NULL: Search current process (reliable for common functions).&lt;/span&gt;
&lt;span class="c"&gt;#    b) Explicit Path: "/path/to/libc.so.6" (works if path is correct, not portable).&lt;/span&gt;
&lt;span class="c"&gt;#    c) :libc Symbol: Platform-independent alias (should work, but might fail&lt;/span&gt;
&lt;span class="c"&gt;#       in non-standard environments if Julia's search path is confused).&lt;/span&gt;

&lt;span class="c"&gt;# --- Example 1: Calling C's time() using Explicit Path ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Calling time(NULL) [Using Explicit Path] ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# C function prototype: time_t time(time_t *tloc);&lt;/span&gt;
&lt;span class="c"&gt;# Returns time_t (Clong). We call time(NULL). Argument type is Ptr{Cvoid}.&lt;/span&gt;

&lt;span class="c"&gt;# !! NOTE !! This path MUST be correct for your specific system.&lt;/span&gt;
&lt;span class="c"&gt;# Found via `ldconfig -p | grep libc.so.6` or `find /usr/lib /lib -name libc.so.6`&lt;/span&gt;
&lt;span class="c"&gt;# This makes the script NON-PORTABLE.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;ACTUAL_LIBC_PATH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"/usr/lib/x86_64-linux-gnu/libc.so.6"&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Using explicit libc path: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ACTUAL_LIBC_PATH&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;current_time_t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="n"&gt;ccall&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;
        &lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ACTUAL_LIBC_PATH&lt;/span&gt;&lt;span class="x"&gt;),&lt;/span&gt; &lt;span class="c"&gt;# Use the explicit path string&lt;/span&gt;
        &lt;span class="kt"&gt;Clong&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt;
        &lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Ptr&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;Cvoid&lt;/span&gt;&lt;span class="x"&gt;},),&lt;/span&gt;
        &lt;span class="nb"&gt;C_NULL&lt;/span&gt;
    &lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ERROR calling time with explicit path '&lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;ACTUAL_LIBC_PATH': "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="kt"&gt;Clong&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Return dummy value on error&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;current_time_t&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Result of C's time(NULL): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;current_time_t&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Type of result:           "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current_time_t&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Julia's time():           "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="x"&gt;())&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;


&lt;span class="c"&gt;# --- Example 2: Calling C's clock() using "" (Search Current Process) ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Calling clock() [Using &lt;/span&gt;&lt;span class="se"&gt;\"\"&lt;/span&gt;&lt;span class="s"&gt; Library Path] ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# C function prototype: clock_t clock(void);&lt;/span&gt;
&lt;span class="c"&gt;# Returns clock_t (Clong). Takes no arguments.&lt;/span&gt;
&lt;span class="c"&gt;# Using "" tells ccall to look for 'clock' in the already loaded process space.&lt;/span&gt;
&lt;span class="c"&gt;# This is generally reliable for standard functions.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;LIBC_LOOKUP_CURRENT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;

&lt;span class="n"&gt;ticks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="n"&gt;ccall&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;
        &lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;clock&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LIBC_LOOKUP_CURRENT&lt;/span&gt;&lt;span class="x"&gt;),&lt;/span&gt; &lt;span class="c"&gt;# Look for 'clock' in current process&lt;/span&gt;
        &lt;span class="kt"&gt;Clong&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt;
        &lt;span class="x"&gt;()&lt;/span&gt;
    &lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ERROR calling clock with &lt;/span&gt;&lt;span class="se"&gt;\"\"&lt;/span&gt;&lt;span class="s"&gt; library path: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="kt"&gt;Clong&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Return dummy value on error&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ticks&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Result of C's clock(): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ticks&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;" ticks"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;CLOCKS_PER_SEC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1_000_000&lt;/span&gt; &lt;span class="c"&gt;# Assume standard value&lt;/span&gt;
    &lt;span class="n"&gt;time_in_seconds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ticks&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;CLOCKS_PER_SEC&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Time in seconds (approx): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time_in_seconds&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# --- Example 3: Demonstrating Potential Failure with :libc ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Calling getpid() [Using :libc Symbol - Might Fail] ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# C function prototype: pid_t getpid(void);&lt;/span&gt;
&lt;span class="c"&gt;# Returns pid_t (usually Cint). Takes no arguments.&lt;/span&gt;
&lt;span class="c"&gt;# We use ':libc', the platform-independent alias. This *should* work,&lt;/span&gt;
&lt;span class="c"&gt;# but can fail if the library search path is misconfigured or points&lt;/span&gt;
&lt;span class="c"&gt;# to an invalid file (like a linker script instead of the .so).&lt;/span&gt;

&lt;span class="n"&gt;pid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt;
     &lt;span class="n"&gt;ccall&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;
        &lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;getpid&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;libc&lt;/span&gt;&lt;span class="x"&gt;),&lt;/span&gt; &lt;span class="c"&gt;# Use the standard :libc alias&lt;/span&gt;
        &lt;span class="kt"&gt;Cint&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt;
        &lt;span class="x"&gt;()&lt;/span&gt;
    &lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ERROR calling getpid with :libc symbol: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  This demonstrates that ':libc' lookup can sometimes fail,"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  especially in non-standard environments. Using &lt;/span&gt;&lt;span class="se"&gt;\"\"&lt;/span&gt;&lt;span class="s"&gt; might be more robust."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="kt"&gt;Cint&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Return dummy value on error&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Result of C's getpid(): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Julia's getpid():       "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;getpid&lt;/span&gt;&lt;span class="x"&gt;())&lt;/span&gt; &lt;span class="c"&gt;# Compare with Julia's wrapper&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="c"&gt;# Try again with "" if :libc failed, just to show it often works&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Trying getpid() again using &lt;/span&gt;&lt;span class="se"&gt;\"\"&lt;/span&gt;&lt;span class="s"&gt; library path..."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;pid_fallback&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt;
        &lt;span class="n"&gt;ccall&lt;/span&gt;&lt;span class="x"&gt;((&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;getpid&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="x"&gt;),&lt;/span&gt; &lt;span class="kt"&gt;Cint&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="x"&gt;())&lt;/span&gt;
    &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="n"&gt;e_fallback&lt;/span&gt;
        &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  ERROR calling getpid with &lt;/span&gt;&lt;span class="se"&gt;\"\"&lt;/span&gt;&lt;span class="s"&gt; as well: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e_fallback&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="kt"&gt;Cint&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;pid_fallback&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  Result using &lt;/span&gt;&lt;span class="se"&gt;\"\"&lt;/span&gt;&lt;span class="s"&gt;: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pid_fallback&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;" (Success)"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces the fundamental &lt;code&gt;ccall&lt;/code&gt; function for calling C functions, demonstrating different ways to specify the C standard library (&lt;code&gt;libc&lt;/code&gt;) and highlighting potential pitfalls.&lt;/p&gt;

&lt;h2&gt;
  
  
  Core Concept: &lt;code&gt;ccall&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;ccall&lt;/code&gt; provides a direct, low-overhead way to invoke native compiled code from shared libraries, handling platform ABI details.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;ccall&lt;/code&gt; Syntax Breakdown
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ccall&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt; &lt;span class="n"&gt;Fspec&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ReturnType&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ArgTypes&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ArgValues&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="x"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;Fspec&lt;/code&gt; (Function Specifier):&lt;/strong&gt; &lt;code&gt;(:function_name, library_specifier)&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;function_name::Symbol&lt;/code&gt;: Name of the C function (e.g., &lt;code&gt;:time&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;library_specifier&lt;/code&gt;: Identifies the library. Crucial variations:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;""&lt;/code&gt; or &lt;code&gt;C_NULL&lt;/code&gt;:&lt;/strong&gt; Searches only within the &lt;strong&gt;current Julia process&lt;/strong&gt; and libraries already loaded into it. Often the most reliable way for ubiquitous functions (like &lt;code&gt;time&lt;/code&gt;, &lt;code&gt;clock&lt;/code&gt;, &lt;code&gt;malloc&lt;/code&gt;, &lt;code&gt;printf&lt;/code&gt;) that are typically linked into the main executable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explicit Path (&lt;code&gt;String&lt;/code&gt;):&lt;/strong&gt; e.g., &lt;code&gt;"/usr/lib/x86_64-linux-gnu/libc.so.6"&lt;/code&gt;. Directly tells Julia which file to load. Works if the path is correct but makes the script &lt;strong&gt;non-portable&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Symbolic Name (&lt;code&gt;Symbol&lt;/code&gt; or &lt;code&gt;String&lt;/code&gt;):&lt;/strong&gt; e.g., &lt;code&gt;:libc&lt;/code&gt;, &lt;code&gt;"libc"&lt;/code&gt;, &lt;code&gt;"libm"&lt;/code&gt;. Tells Julia to search standard system library paths and potentially use pre-configured aliases. &lt;code&gt;:libc&lt;/code&gt; &lt;em&gt;should&lt;/em&gt; be the platform-independent way, but as demonstrated, it can fail if the search mechanism finds an incorrect file (like a linker script instead of the actual &lt;code&gt;.so&lt;/code&gt;) in non-standard environments.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;ReturnType&lt;/code&gt;:&lt;/strong&gt; Julia type matching C return type (e.g., &lt;code&gt;Clong&lt;/code&gt;, &lt;code&gt;Cint&lt;/code&gt;, &lt;code&gt;Float64&lt;/code&gt;, &lt;code&gt;Ptr{T}&lt;/code&gt;, &lt;code&gt;Cvoid&lt;/code&gt;). &lt;strong&gt;Must be correct.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;ArgTypes&lt;/code&gt;:&lt;/strong&gt; &lt;code&gt;Tuple&lt;/code&gt; of Julia types matching C argument types (e.g., &lt;code&gt;(Cint, Float64, Ptr{Cvoid})&lt;/code&gt;). &lt;code&gt;()&lt;/code&gt; for no arguments. &lt;strong&gt;Must be correct.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;ArgValues...&lt;/code&gt;:&lt;/strong&gt; Actual values passed to the C function.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Examples Explained
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;time(NULL)&lt;/code&gt; [Explicit Path]:&lt;/strong&gt; We use the exact path &lt;code&gt;/usr/lib/x86_64-linux-gnu/libc.so.6&lt;/code&gt; (which must be correct for the specific system). This works reliably if the path is right but isn't portable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;clock()&lt;/code&gt; [&lt;code&gt;""&lt;/code&gt; Path]:&lt;/strong&gt; We use &lt;code&gt;""&lt;/code&gt; for the library. &lt;code&gt;ccall&lt;/code&gt; finds the &lt;code&gt;clock&lt;/code&gt; symbol already loaded within the Julia process memory space. This is often robust for standard functions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;getpid()&lt;/code&gt; [&lt;code&gt;:libc&lt;/code&gt; Symbol - Potential Failure]:&lt;/strong&gt; We attempt to use the standard &lt;code&gt;:libc&lt;/code&gt; alias. In correctly configured systems, this works. However, the &lt;code&gt;try...catch&lt;/code&gt; block demonstrates that if Julia's search path logic incorrectly identifies the library file (as observed during debugging where it found an invalid ELF header), this call will fail. We then show that retrying with &lt;code&gt;""&lt;/code&gt; often succeeds because &lt;code&gt;getpid&lt;/code&gt; is likely already loaded.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Critical Notes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Type Accuracy:&lt;/strong&gt; Correctly specifying &lt;code&gt;ReturnType&lt;/code&gt; and &lt;code&gt;ArgTypes&lt;/code&gt; is &lt;strong&gt;paramount&lt;/strong&gt; to avoid crashes. Use Julia's C-compatible types (&lt;code&gt;Cint&lt;/code&gt;, &lt;code&gt;Clong&lt;/code&gt;, etc.).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Library Path Choice:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;For very common C standard library functions, &lt;code&gt;""&lt;/code&gt; is often the most robust method.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;:libc&lt;/code&gt; or &lt;code&gt;:libm&lt;/code&gt; &lt;em&gt;should&lt;/em&gt; be preferred for platform independence when they work correctly in your environment.&lt;/li&gt;
&lt;li&gt;Explicit paths are non-portable but necessary if the library isn't in standard locations or if symbolic lookups fail.&lt;/li&gt;
&lt;li&gt;For your own or third-party libraries, use the library name (e.g., &lt;code&gt;"libmycoolstuff"&lt;/code&gt;) or a relative/absolute path (&lt;code&gt;"./libmycoolstuff.so"&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Calling C and Fortran Code", &lt;code&gt;ccall&lt;/code&gt;:&lt;/strong&gt; Primary documentation, mentions using &lt;code&gt;C_NULL&lt;/code&gt; or &lt;code&gt;""&lt;/code&gt; for searching the current process.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Calling C and Fortran Code", "Mapping C Types to Julia":&lt;/strong&gt; Lists type correspondences.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;C Standard Library Documentation (e.g., man pages for &lt;code&gt;time&lt;/code&gt;, &lt;code&gt;clock&lt;/code&gt;, &lt;code&gt;getpid&lt;/code&gt;):&lt;/strong&gt; Provides C function prototypes.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0114_ccall_basics_simple.jl
&lt;span class="nt"&gt;---&lt;/span&gt; Calling C Standard Library Functions via ccall &lt;span class="nt"&gt;---&lt;/span&gt;

&lt;span class="nt"&gt;---&lt;/span&gt; Calling &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;NULL&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;Using Explicit Path] &lt;span class="nt"&gt;---&lt;/span&gt;
Using explicit libc path: /usr/lib/x86_64-linux-gnu/libc.so.6
Result of C&lt;span class="s1"&gt;'s time(NULL): 1761130895
Type of result:           Int64
Julia'&lt;/span&gt;s &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;:           1.761130896255223e9

&lt;span class="nt"&gt;---&lt;/span&gt; Calling clock&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;Using &lt;span class="s2"&gt;""&lt;/span&gt; Library Path] &lt;span class="nt"&gt;---&lt;/span&gt;
Result of C&lt;span class="s1"&gt;'s clock(): 1717681 ticks
Time in seconds (approx): 1.717681

--- Calling getpid() [Using :libc Symbol - Might Fail] ---
ERROR calling getpid with :libc symbol: ErrorException("could not load library \"libc\"\n/lib/x86_64-linux-gnu/libc.so: invalid ELF header")
  This demonstrates that '&lt;/span&gt;:libc&lt;span class="s1"&gt;' lookup can sometimes fail,
  especially in non-standard environments. Using "" might be more robust.
Trying getpid() again using "" library path...
  Result using "": 16986 (Success)

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(Exact timestamp, ticks, PID values, and whether the &lt;code&gt;:libc&lt;/code&gt; call fails will vary.)&lt;/em&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;0115_ccall_type_mapping.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0115_ccall_type_mapping.jl&lt;/span&gt;
&lt;span class="c"&gt;# Demonstrates mapping common C types to Julia types for 'ccall'.&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Libc&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Cint&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Clong&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Csize_t&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cdouble&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cfloat&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cchar&lt;/span&gt; &lt;span class="c"&gt;# Import C-specific types&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Mapping C Types to Julia Types in ccall ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# We will call C's standard math function 'atan2' from 'libm'.&lt;/span&gt;
&lt;span class="c"&gt;# C prototype: double atan2(double y, double x);&lt;/span&gt;

&lt;span class="c"&gt;# Input values for the function&lt;/span&gt;
&lt;span class="n"&gt;y_jl&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;
&lt;span class="n"&gt;x_jl&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;

&lt;span class="c"&gt;# 1. The Core Type Mapping:&lt;/span&gt;
&lt;span class="c"&gt;#    C Type        | Julia Type    | Typical Size (64-bit Linux/macOS)&lt;/span&gt;
&lt;span class="c"&gt;#    ------------------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;#    int           | Cint          | 4 bytes (Int32)&lt;/span&gt;
&lt;span class="c"&gt;#    unsigned int  | Cuint         | 4 bytes (UInt32)&lt;/span&gt;
&lt;span class="c"&gt;#    long          | Clong         | 8 bytes (Int64)&lt;/span&gt;
&lt;span class="c"&gt;#    unsigned long | Culong        | 8 bytes (UInt64)&lt;/span&gt;
&lt;span class="c"&gt;#    long long     | Clonglong     | 8 bytes (Int64)&lt;/span&gt;
&lt;span class="c"&gt;#    unsigned long long | Culonglong| 8 bytes (UInt64)&lt;/span&gt;
&lt;span class="c"&gt;#    short         | Cshort        | 2 bytes (Int16)&lt;/span&gt;
&lt;span class="c"&gt;#    unsigned short| Cushort       | 2 bytes (UInt16)&lt;/span&gt;
&lt;span class="c"&gt;#    char          | Cchar         | 1 byte (Int8 or UInt8, platform dependent)&lt;/span&gt;
&lt;span class="c"&gt;#    signed char   | Cchar         | 1 byte (Int8) (Usually same as char)&lt;/span&gt;
&lt;span class="c"&gt;#    unsigned char | Cuchar        | 1 byte (UInt8)&lt;/span&gt;
&lt;span class="c"&gt;#    float         | Cfloat        | 4 bytes (Float32)&lt;/span&gt;
&lt;span class="c"&gt;#    double        | Cdouble       | 8 bytes (Float64)&lt;/span&gt;
&lt;span class="c"&gt;#    size_t        | Csize_t       | 8 bytes (UInt64)&lt;/span&gt;
&lt;span class="c"&gt;#    ptrdiff_t     | Cptrdiff_t    | 8 bytes (Int64)&lt;/span&gt;
&lt;span class="c"&gt;#    void          | Cvoid         | (Used only for ReturnType)&lt;/span&gt;
&lt;span class="c"&gt;#    T* | Ptr{T}        | 8 bytes (Pointer to Julia type T)&lt;/span&gt;
&lt;span class="c"&gt;#    void* | Ptr{Cvoid}    | 8 bytes&lt;/span&gt;
&lt;span class="c"&gt;#    char* | Ptr{UInt8} or Ptr{Cchar} | 8 bytes (Often use unsafe_string)&lt;/span&gt;
&lt;span class="c"&gt;#    struct T      | T (if isbits) | sizeof(T) (Pass via Ref{T} for T*)&lt;/span&gt;

&lt;span class="c"&gt;# 2. Call atan2 using the mapping.&lt;/span&gt;
&lt;span class="c"&gt;#    Use ":libm" for the standard math library. Use "" if it might be linked in already.&lt;/span&gt;
&lt;span class="n"&gt;libm_spec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="c"&gt;# Or :libm if "" fails&lt;/span&gt;

&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="n"&gt;ccall&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;
        &lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;atan2&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;libm_spec&lt;/span&gt;&lt;span class="x"&gt;),&lt;/span&gt; &lt;span class="c"&gt;# Function "atan2" in the math library (or current process)&lt;/span&gt;
        &lt;span class="kt"&gt;Cdouble&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt;             &lt;span class="c"&gt;# Return type is C double -&amp;gt; Julia Cdouble (Float64)&lt;/span&gt;
        &lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Cdouble&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cdouble&lt;/span&gt;&lt;span class="x"&gt;),&lt;/span&gt;  &lt;span class="c"&gt;# Argument types are (C double, C double)&lt;/span&gt;
        &lt;span class="n"&gt;y_jl&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x_jl&lt;/span&gt;           &lt;span class="c"&gt;# Pass the Julia Float64 values&lt;/span&gt;
    &lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ERROR calling atan2: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;NaN&lt;/span&gt; &lt;span class="c"&gt;# Return dummy value&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;isnan&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"C's atan2(&lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;y_jl, &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;x_jl):   "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# Compare with Julia's built-in version&lt;/span&gt;
    &lt;span class="n"&gt;julia_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;atan&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y_jl&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x_jl&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Julia's atan(&lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;y_jl, &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;x_jl): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;julia_result&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Results are approx equal: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="n"&gt;≈&lt;/span&gt; &lt;span class="n"&gt;julia_result&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 3. Verifying sizes of C-specific types on this platform.&lt;/span&gt;
&lt;span class="c"&gt;#    It's crucial these match the C compiler's sizes.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Verifying C Type Sizes on this Platform ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sizeof(Cint):      "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sizeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Cint&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sizeof(Clong):     "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sizeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Clong&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sizeof(Clonglong): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sizeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Clonglong&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sizeof(Csize_t):   "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sizeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Csize_t&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sizeof(Cchar):     "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sizeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Cchar&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt; &lt;span class="c"&gt;# Can be signed or unsigned by default&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sizeof(Cfloat):    "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sizeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Cfloat&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sizeof(Cdouble):   "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sizeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Cdouble&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script focuses on the crucial &lt;strong&gt;type mapping&lt;/strong&gt; required when using &lt;code&gt;ccall&lt;/code&gt;. Because &lt;code&gt;ccall&lt;/code&gt; bypasses Julia's type system to call native code, you &lt;em&gt;must&lt;/em&gt; explicitly tell Julia the exact C types expected by the function for both arguments and the return value, using the corresponding Julia types.&lt;/p&gt;

&lt;h2&gt;
  
  
  Core Concept: The &lt;code&gt;ccall&lt;/code&gt; Type Contract
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;ReturnType&lt;/code&gt; and &lt;code&gt;ArgTypes&lt;/code&gt; tuple provided to &lt;code&gt;ccall&lt;/code&gt; form a &lt;strong&gt;strict contract&lt;/strong&gt; between your Julia code and the native C library. Julia uses this contract to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Convert Arguments:&lt;/strong&gt; Convert the Julia values you provide (&lt;code&gt;ArgValues&lt;/code&gt;) into the binary representation expected by the C function based on the &lt;code&gt;ArgTypes&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Generate Calling Code:&lt;/strong&gt; Emit the correct machine instructions to pass these arguments according to the platform's C ABI (Application Binary Interface) – handling registers vs. stack appropriately.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Interpret Return Value:&lt;/strong&gt; Interpret the binary data returned by the C function as the specified &lt;code&gt;ReturnType&lt;/code&gt; and convert it back into a Julia value.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;If this contract (the type mapping) is wrong, &lt;code&gt;ccall&lt;/code&gt; will generate incorrect code, leading to crashes (segmentation faults), garbage results, or silent memory corruption.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Julia-to-C Type Map
&lt;/h2&gt;

&lt;p&gt;Julia provides a set of &lt;strong&gt;C-specific type aliases&lt;/strong&gt; (like &lt;code&gt;Cint&lt;/code&gt;, &lt;code&gt;Clong&lt;/code&gt;, &lt;code&gt;Cdouble&lt;/code&gt;) within the &lt;code&gt;Base.Libc&lt;/code&gt; module. &lt;strong&gt;You should always use these specific types in &lt;code&gt;ccall&lt;/code&gt; signatures&lt;/strong&gt;, rather than generic Julia types like &lt;code&gt;Int&lt;/code&gt; or &lt;code&gt;Float64&lt;/code&gt; directly (even though &lt;code&gt;Cdouble&lt;/code&gt; &lt;em&gt;is&lt;/em&gt; often just an alias for &lt;code&gt;Float64&lt;/code&gt;, and &lt;code&gt;Clong&lt;/code&gt; for &lt;code&gt;Int64&lt;/code&gt; on 64-bit systems), because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Platform Portability:&lt;/strong&gt; The exact size of C types like &lt;code&gt;int&lt;/code&gt; and &lt;code&gt;long&lt;/code&gt; can vary between platforms (e.g., &lt;code&gt;long&lt;/code&gt; is often 32 bits on 32-bit Windows but 64 bits on 64-bit Linux). Julia's &lt;code&gt;Cint&lt;/code&gt;, &lt;code&gt;Clong&lt;/code&gt;, etc., are defined correctly for the specific platform Julia was compiled for, ensuring your &lt;code&gt;ccall&lt;/code&gt; signature remains correct when your code is run on different operating systems or architectures.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clarity:&lt;/strong&gt; Using &lt;code&gt;Cint&lt;/code&gt; explicitly signals that you are interfacing with a C function expecting an &lt;code&gt;int&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The table in the code provides the standard mapping. Key points include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;Cint&lt;/code&gt;, &lt;code&gt;Clong&lt;/code&gt;, &lt;code&gt;Csize_t&lt;/code&gt;, etc., for C integer types.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;Cfloat&lt;/code&gt; (maps to &lt;code&gt;Float32&lt;/code&gt;) for C &lt;code&gt;float&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;Cdouble&lt;/code&gt; (maps to &lt;code&gt;Float64&lt;/code&gt;) for C &lt;code&gt;double&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;Ptr{JuliaType}&lt;/code&gt; for C &lt;code&gt;CType*&lt;/code&gt;, where &lt;code&gt;JuliaType&lt;/code&gt; corresponds to &lt;code&gt;CType&lt;/code&gt;. Use &lt;code&gt;Ptr{Cvoid}&lt;/code&gt; for &lt;code&gt;void*&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;Cvoid&lt;/code&gt; as the &lt;code&gt;ReturnType&lt;/code&gt; for C &lt;code&gt;void&lt;/code&gt; functions.&lt;/li&gt;
&lt;li&gt;Pass &lt;code&gt;isbits struct&lt;/code&gt;s &lt;em&gt;by pointer&lt;/em&gt; (&lt;code&gt;T*&lt;/code&gt;) using &lt;code&gt;Ref{T}&lt;/code&gt; as the &lt;code&gt;ArgType&lt;/code&gt; and &lt;code&gt;Ref(value)&lt;/code&gt; as the &lt;code&gt;ArgValue&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Example: &lt;code&gt;atan2&lt;/code&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The C prototype is &lt;code&gt;double atan2(double y, double x)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ReturnType&lt;/code&gt; is &lt;code&gt;Cdouble&lt;/code&gt; (maps to Julia's &lt;code&gt;Float64&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ArgTypes&lt;/code&gt; is &lt;code&gt;(Cdouble, Cdouble)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;We pass Julia &lt;code&gt;Float64&lt;/code&gt; values (&lt;code&gt;y_jl&lt;/code&gt;, &lt;code&gt;x_jl&lt;/code&gt;). &lt;code&gt;ccall&lt;/code&gt; ensures they are passed correctly as C doubles.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;The script concludes by printing the &lt;code&gt;sizeof&lt;/code&gt; Julia's C-aliased types on the current platform. This allows you to verify that Julia's understanding of C type sizes matches what your C compiler uses. Mismatches here would indicate a potential problem with the Julia build or environment configuration.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Calling C and Fortran Code", "Mapping C Types to Julia":&lt;/strong&gt; The definitive table and explanation of type correspondences.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Base Documentation, &lt;code&gt;Libc&lt;/code&gt;:&lt;/strong&gt; Lists the available C-compatible type aliases (&lt;code&gt;Cint&lt;/code&gt;, &lt;code&gt;Clong&lt;/code&gt;, etc.).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;C Language Standard / Platform ABI Documentation:&lt;/strong&gt; (External) Defines the sizes and alignment of C types on specific platforms.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0115_ccall_type_mapping.jl
&lt;span class="nt"&gt;---&lt;/span&gt; Mapping C Types to Julia Types &lt;span class="k"&gt;in &lt;/span&gt;ccall &lt;span class="nt"&gt;---&lt;/span&gt;
C&lt;span class="s1"&gt;'s atan2(1.0, -1.0):   2.356194490192345
Julia'&lt;/span&gt;s atan&lt;span class="o"&gt;(&lt;/span&gt;1.0, &lt;span class="nt"&gt;-1&lt;/span&gt;.0&lt;span class="o"&gt;)&lt;/span&gt;: 2.356194490192345
Results are approx equal: &lt;span class="nb"&gt;true&lt;/span&gt;

&lt;span class="nt"&gt;---&lt;/span&gt; Verifying C Type Sizes on this Platform &lt;span class="nt"&gt;---&lt;/span&gt;
sizeof&lt;span class="o"&gt;(&lt;/span&gt;Cint&lt;span class="o"&gt;)&lt;/span&gt;:      4
sizeof&lt;span class="o"&gt;(&lt;/span&gt;Clong&lt;span class="o"&gt;)&lt;/span&gt;:     8
sizeof&lt;span class="o"&gt;(&lt;/span&gt;Clonglong&lt;span class="o"&gt;)&lt;/span&gt;: 8
sizeof&lt;span class="o"&gt;(&lt;/span&gt;Csize_t&lt;span class="o"&gt;)&lt;/span&gt;:   8
sizeof&lt;span class="o"&gt;(&lt;/span&gt;Cchar&lt;span class="o"&gt;)&lt;/span&gt;:     1
sizeof&lt;span class="o"&gt;(&lt;/span&gt;Cfloat&lt;span class="o"&gt;)&lt;/span&gt;:    4
sizeof&lt;span class="o"&gt;(&lt;/span&gt;Cdouble&lt;span class="o"&gt;)&lt;/span&gt;:   8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(The specific sizes reflect a typical 64-bit Linux/macOS environment. &lt;code&gt;Clong&lt;/code&gt; might be 4 on 32-bit systems or 64-bit Windows.)&lt;/em&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;0116_ccall_passing_vectors.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0116_ccall_passing_vectors.jl&lt;/span&gt;
&lt;span class="c"&gt;# Demonstrates passing a Julia Vector to C using pointer and length.&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Libc&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Csize_t&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cdouble&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Libdl&lt;/span&gt; &lt;span class="c"&gt;# For dlopen/dlsym if not compiling string&lt;/span&gt;

&lt;span class="c"&gt;# --- C Function Simulation ---&lt;/span&gt;
&lt;span class="c"&gt;# We simulate a C function that sums elements of a double array:&lt;/span&gt;
&lt;span class="c"&gt;# // C prototype:&lt;/span&gt;
&lt;span class="c"&gt;# // double sum_array(const double* arr, size_t len);&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# For self-containment, we'll compile this C code from a string&lt;/span&gt;
&lt;span class="c"&gt;# into a temporary shared library. In real use, you'd link against&lt;/span&gt;
&lt;span class="c"&gt;# an existing library.&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;c_code_sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"""
#include &amp;lt;stddef.h&amp;gt; // for size_t
double sum_array(const double* arr, size_t len) {
    double sum = 0.0;
    for (size_t i = 0; i &amp;lt; len; i++) {
        sum += arr[i];
    }
    return sum;
}
"""&lt;/span&gt;

&lt;span class="c"&gt;# Compile the C code into a temporary shared library&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; compile_c_code&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c_code&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lib_name&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;lib_filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lib_name&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="s"&gt;"."&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Libdl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dlext&lt;/span&gt; &lt;span class="c"&gt;# Platform-specific extension (.so, .dll, .dylib)&lt;/span&gt;
    &lt;span class="c"&gt;# Basic check if gcc exists&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;isnothing&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;which&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"gcc"&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"gcc not found. Please install gcc to run this example."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="n"&gt;compile_cmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sb"&gt;`gcc -fPIC -shared -x c -o $lib_filename -`&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Compiling C code to &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;lib_filename..."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;
        &lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;compile_cmd&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"w"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;stdout&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;
            &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c_code&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
        &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Compilation successful."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;abspath&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lib_filename&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Return full path&lt;/span&gt;
    &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
        &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ERROR compiling C code: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;nothing&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;temp_lib_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;compile_c_code&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c_code_sum&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"libtempsum"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;temp_lib_path&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nb"&gt;nothing&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Exiting due to compilation failure."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# --- Julia Data and ccall ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Calling C function with Julia Vector ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 1. The Julia Vector we want to pass.&lt;/span&gt;
&lt;span class="c"&gt;#    It's crucial that its element type matches the C function's expectation.&lt;/span&gt;
&lt;span class="n"&gt;julia_vector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Float64&lt;/span&gt;&lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;1.1&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;2.2&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;3.3&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;4.4&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;5.5&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt;

&lt;span class="c"&gt;# 2. Prepare arguments for ccall:&lt;/span&gt;
&lt;span class="c"&gt;#    - C 'const double* arr': Use 'pointer(julia_vector)' which returns Ptr{Float64}.&lt;/span&gt;
&lt;span class="c"&gt;#      Float64 matches Cdouble. Ptr{Float64} matches Ptr{Cdouble}.&lt;/span&gt;
&lt;span class="c"&gt;#    - C 'size_t len': Use 'length(julia_vector)' which returns Int.&lt;/span&gt;
&lt;span class="c"&gt;#      ccall automatically converts Int to Csize_t.&lt;/span&gt;
&lt;span class="n"&gt;ptr_to_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pointer&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;julia_vector&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;vector_length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;julia_vector&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Julia Vector: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;julia_vector&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Pointer to data: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ptr_to_data&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Vector length: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vector_length&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 3. Perform the ccall.&lt;/span&gt;
&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="n"&gt;ccall&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;
        &lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;sum_array&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;temp_lib_path&lt;/span&gt;&lt;span class="x"&gt;),&lt;/span&gt; &lt;span class="c"&gt;# Function name and path to our temporary library&lt;/span&gt;
        &lt;span class="kt"&gt;Cdouble&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt;                     &lt;span class="c"&gt;# Return type: double -&amp;gt; Cdouble (Float64)&lt;/span&gt;
        &lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Ptr&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;Cdouble&lt;/span&gt;&lt;span class="x"&gt;},&lt;/span&gt; &lt;span class="kt"&gt;Csize_t&lt;/span&gt;&lt;span class="x"&gt;),&lt;/span&gt;     &lt;span class="c"&gt;# Argument types: (double*, size_t)&lt;/span&gt;
        &lt;span class="n"&gt;ptr_to_data&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vector_length&lt;/span&gt;   &lt;span class="c"&gt;# Argument values: pointer and length&lt;/span&gt;
    &lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ERROR during ccall: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;NaN&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# --- Verification and Cleanup ---&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;isnan&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Result from C's sum_array: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;julia_sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;julia_vector&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Julia's sum():             "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;julia_sum&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Results approximately equal: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="n"&gt;≈&lt;/span&gt; &lt;span class="n"&gt;julia_sum&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# Clean up the temporary library file&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="n"&gt;rm&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;temp_lib_path&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Removed temporary library: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;temp_lib_path&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Warning: Could not remove temporary library '&lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;temp_lib_path': "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script demonstrates the most common and crucial pattern for C interoperability: passing a Julia &lt;code&gt;Vector&lt;/code&gt; (or &lt;code&gt;Array&lt;/code&gt;) to a C function that expects a pointer to the data and the number of elements. This is achieved efficiently and safely using &lt;code&gt;pointer()&lt;/code&gt; and &lt;code&gt;length()&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Core Concept: Pointer + Length Idiom
&lt;/h2&gt;

&lt;p&gt;Many C functions operating on arrays follow the pattern &lt;code&gt;return_type function_name(element_type* data_pointer, size_type number_of_elements)&lt;/code&gt;. To call such a function from Julia with a &lt;code&gt;Vector&lt;/code&gt; named &lt;code&gt;A&lt;/code&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Get Pointer to Data:&lt;/strong&gt; Use &lt;code&gt;pointer(A)&lt;/code&gt;. As covered in Module 9, this returns a &lt;code&gt;Ptr{T}&lt;/code&gt; (where &lt;code&gt;T&lt;/code&gt; is the element type of &lt;code&gt;A&lt;/code&gt;) pointing directly to the &lt;strong&gt;first element&lt;/strong&gt; (&lt;code&gt;A[1]&lt;/code&gt;) in the vector's contiguous memory buffer.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Get Number of Elements:&lt;/strong&gt; Use &lt;code&gt;length(A)&lt;/code&gt;. This returns the number of elements in the vector as a Julia &lt;code&gt;Int&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;ccall&lt;/code&gt; Signature:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;ArgTypes&lt;/code&gt; tuple must match the C function. C &lt;code&gt;T*&lt;/code&gt; maps to Julia &lt;code&gt;Ptr{CorrespondingJuliaT}&lt;/code&gt; (e.g., &lt;code&gt;double*&lt;/code&gt; -&amp;gt; &lt;code&gt;Ptr{Cdouble}&lt;/code&gt;). C &lt;code&gt;size_t&lt;/code&gt; maps to Julia &lt;code&gt;Csize_t&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Pass &lt;code&gt;pointer(A)&lt;/code&gt; and &lt;code&gt;length(A)&lt;/code&gt; as the corresponding &lt;code&gt;ArgValues&lt;/code&gt;. &lt;code&gt;ccall&lt;/code&gt; automatically handles converting the Julia &lt;code&gt;Int&lt;/code&gt; from &lt;code&gt;length&lt;/code&gt; to the required C integer type (&lt;code&gt;Csize_t&lt;/code&gt; in this case).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Zero-Copy Performance
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No Data Copying:&lt;/strong&gt; This is a &lt;strong&gt;zero-copy&lt;/strong&gt; operation. &lt;code&gt;pointer(A)&lt;/code&gt; simply gets the memory address where the vector's data &lt;em&gt;already resides&lt;/em&gt;. The data itself is &lt;strong&gt;not copied&lt;/strong&gt; before being passed to C. The C function operates directly on Julia's memory buffer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Efficiency:&lt;/strong&gt; This makes calling C functions with large arrays extremely efficient, avoiding the potentially massive overhead of copying data between Julia and C.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  GC Safety: Pinning
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Problem:&lt;/strong&gt; Julia's garbage collector (GC) occasionally moves objects in memory to compact the heap. If the GC moved the data buffer of &lt;code&gt;julia_vector&lt;/code&gt; &lt;em&gt;while&lt;/em&gt; the C function &lt;code&gt;sum_array&lt;/code&gt; was reading from &lt;code&gt;ptr_to_data&lt;/code&gt;, the C function would suddenly be accessing invalid memory, leading to a crash.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ccall&lt;/code&gt;'s Solution:&lt;/strong&gt; When &lt;code&gt;ccall&lt;/code&gt; sees that one of its arguments (&lt;code&gt;ptr_to_data&lt;/code&gt;) was derived from a Julia object (&lt;code&gt;julia_vector&lt;/code&gt; via &lt;code&gt;pointer()&lt;/code&gt;), it automatically &lt;strong&gt;"pins"&lt;/strong&gt; the object (&lt;code&gt;julia_vector&lt;/code&gt;). This tells the GC: "Do &lt;strong&gt;not&lt;/strong&gt; move or garbage collect this object or its data buffer until this &lt;code&gt;ccall&lt;/code&gt; completes."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Guaranteed Safety:&lt;/strong&gt; This pinning mechanism ensures that the pointer passed to C remains valid for the entire duration of the native function call, preventing GC-related memory corruption. You do not need to manually manage pinning when using &lt;code&gt;pointer()&lt;/code&gt; with &lt;code&gt;ccall&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This &lt;code&gt;pointer(A), length(A)&lt;/code&gt; pattern combined with &lt;code&gt;ccall&lt;/code&gt;'s automatic GC pinning provides a safe, efficient, and idiomatic way to leverage C libraries that operate on arrays, forming the backbone of numerical and systems integration in Julia.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Calling C and Fortran Code", "Passing Pointers for Modifying Inputs":&lt;/strong&gt; Although discussing modification, it implicitly covers passing arrays via pointers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Base Documentation, &lt;code&gt;pointer&lt;/code&gt;:&lt;/strong&gt; "Get the native address..." Mentions safety for &lt;code&gt;ccall&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Base Documentation, &lt;code&gt;length&lt;/code&gt;:&lt;/strong&gt; Returns the number of elements.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;To run the script:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Requires a C compiler like &lt;code&gt;gcc&lt;/code&gt; to be installed and in the system's PATH for the C code compilation step.)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0116_ccall_passing_vectors.jl
Compiling C code to libtempsum.so...
Compilation successful.

&lt;span class="nt"&gt;---&lt;/span&gt; Calling C &lt;span class="k"&gt;function &lt;/span&gt;with Julia Vector &lt;span class="nt"&gt;---&lt;/span&gt;
Julia Vector: &lt;span class="o"&gt;[&lt;/span&gt;1.1, 2.2, 3.3, 4.4, 5.5]
Pointer to data: Ptr&lt;span class="o"&gt;{&lt;/span&gt;Float64&lt;span class="o"&gt;}(&lt;/span&gt;0x...&lt;span class="o"&gt;)&lt;/span&gt;
Vector length: 5

Result from C&lt;span class="s1"&gt;'s sum_array: 16.5
Julia'&lt;/span&gt;s &lt;span class="nb"&gt;sum&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;:             16.5
Results approximately equal: &lt;span class="nb"&gt;true

&lt;/span&gt;Removed temporary library: /path/to/libtempsum.so
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(Memory address and exact path will vary. The sums should match.)&lt;/em&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;0117_ccall_passing_structs.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0117_ccall_passing_structs.jl&lt;/span&gt;
&lt;span class="c"&gt;# Demonstrates passing an isbits struct by reference (pointer) to C.&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Libc&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Cdouble&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cvoid&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Libdl&lt;/span&gt;

&lt;span class="c"&gt;# --- Julia Struct Definition ---&lt;/span&gt;

&lt;span class="c"&gt;# 1. Define an immutable 'isbits' struct in Julia.&lt;/span&gt;
&lt;span class="c"&gt;#    Its memory layout will be identical to the corresponding C struct.&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="nc"&gt; Point&lt;/span&gt; &lt;span class="c"&gt;# isbits, 16 bytes&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt; &lt;span class="c"&gt;# 8 bytes&lt;/span&gt;
    &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt; &lt;span class="c"&gt;# 8 bytes&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# --- C Code Simulation ---&lt;/span&gt;
&lt;span class="c"&gt;# C struct equivalent:&lt;/span&gt;
&lt;span class="c"&gt;# typedef struct {&lt;/span&gt;
&lt;span class="c"&gt;#     double x;&lt;/span&gt;
&lt;span class="c"&gt;#     double y;&lt;/span&gt;
&lt;span class="c"&gt;# } Point;&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# C function that modifies a Point via pointer:&lt;/span&gt;
&lt;span class="c"&gt;# void move_point(Point* p, double dx, double dy) {&lt;/span&gt;
&lt;span class="c"&gt;#     p-&amp;gt;x += dx;&lt;/span&gt;
&lt;span class="c"&gt;#     p-&amp;gt;y += dy;&lt;/span&gt;
&lt;span class="c"&gt;# }&lt;/span&gt;

&lt;span class="c"&gt;# Compile the C code into a temporary shared library&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;c_code_point&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"""
#include &amp;lt;stddef.h&amp;gt;

typedef struct {
    double x;
    double y;
} Point;

void move_point(Point* p, double dx, double dy) {
    if (p != NULL) { // Basic null check
        p-&amp;gt;x += dx;
        p-&amp;gt;y += dy;
    }
}
"""&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; compile_c_code&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c_code&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lib_name&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;lib_filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lib_name&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="s"&gt;"."&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Libdl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dlext&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;isnothing&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;which&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"gcc"&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"gcc not found. Please install gcc to run this example."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="n"&gt;compile_cmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sb"&gt;`gcc -fPIC -shared -x c -o $lib_filename -`&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Compiling C code to &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;lib_filename..."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;
        &lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;compile_cmd&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"w"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;stdout&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;
            &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c_code&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
        &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Compilation successful."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;abspath&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lib_filename&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
        &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ERROR compiling C code: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;nothing&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;temp_lib_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;compile_c_code&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c_code_point&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"libtemppoint"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;temp_lib_path&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nb"&gt;nothing&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Exiting due to compilation failure."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# --- Julia Data and ccall ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Calling C function with Julia isbits struct ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 2. Create an instance of the Julia struct.&lt;/span&gt;
&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Point&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;10.0&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;20.0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 3. Prepare argument for passing *by pointer* to C.&lt;/span&gt;
&lt;span class="c"&gt;#    The C function expects 'Point*'. We cannot pass 'p' directly,&lt;/span&gt;
&lt;span class="c"&gt;#    as that would pass the 16-byte value itself (pass-by-value).&lt;/span&gt;
&lt;span class="c"&gt;#    We need to pass its *address*.&lt;/span&gt;
&lt;span class="c"&gt;#    The safe way to do this for an isbits value is using 'Ref(value)'.&lt;/span&gt;
&lt;span class="c"&gt;#    'Ref(p)' creates a GC-managed box holding 'p', allowing a stable pointer.&lt;/span&gt;
&lt;span class="n"&gt;p_ref&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Ref&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Type is Base.RefValue{Point}&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Julia Point p: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Boxed Ref(p):  "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p_ref&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Value inside Ref before call: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p_ref&lt;/span&gt;&lt;span class="x"&gt;[])&lt;/span&gt; &lt;span class="c"&gt;# Use [] to get value from Ref&lt;/span&gt;

&lt;span class="c"&gt;# 4. Perform the ccall.&lt;/span&gt;
&lt;span class="c"&gt;#    Map C 'Point*' to Julia 'Ref{Point}' in the ArgTypes tuple.&lt;/span&gt;
&lt;span class="c"&gt;#    ccall automatically uses Base.unsafe_convert(Ptr{Point}, p_ref) internally.&lt;/span&gt;
&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="n"&gt;ccall&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;
        &lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;move_point&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;temp_lib_path&lt;/span&gt;&lt;span class="x"&gt;),&lt;/span&gt; &lt;span class="c"&gt;# Function name and library path&lt;/span&gt;
        &lt;span class="kt"&gt;Cvoid&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt;                       &lt;span class="c"&gt;# Return type: void&lt;/span&gt;
        &lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Ref&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Point&lt;/span&gt;&lt;span class="x"&gt;},&lt;/span&gt; &lt;span class="kt"&gt;Cdouble&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cdouble&lt;/span&gt;&lt;span class="x"&gt;),&lt;/span&gt; &lt;span class="c"&gt;# Arg types: (Point*, double, double)&lt;/span&gt;
        &lt;span class="n"&gt;p_ref&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;5.0&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;5.0&lt;/span&gt;             &lt;span class="c"&gt;# Arg values: pass the Ref object&lt;/span&gt;
    &lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;ccall executed successfully."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;ERROR during ccall: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;false&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# --- Verification and Cleanup ---&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
    &lt;span class="c"&gt;# 5. Check the value *inside* the Ref object after the call.&lt;/span&gt;
    &lt;span class="c"&gt;#    The C function modified the data held within the Ref.&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Value inside Ref after call:  "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p_ref&lt;/span&gt;&lt;span class="x"&gt;[])&lt;/span&gt;
    &lt;span class="c"&gt;# The original immutable 'p' variable is *unchanged*.&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Original variable 'p' (immutable) is unchanged: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="n"&gt;rm&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;temp_lib_path&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Removed temporary library: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;temp_lib_path&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Warning: Could not remove temporary library '&lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;temp_lib_path': "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script demonstrates how to pass a Julia &lt;strong&gt;&lt;code&gt;isbits struct&lt;/code&gt;&lt;/strong&gt; (like our immutable &lt;code&gt;Point&lt;/code&gt;) &lt;strong&gt;by reference (as a pointer)&lt;/strong&gt; to a C function that expects to receive and potentially modify a C &lt;code&gt;struct&lt;/code&gt; via a pointer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Core Concept: Identical Memory Layout &amp;amp; Passing Pointers
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;isbits struct&lt;/code&gt; Layout:&lt;/strong&gt; As established in Module 9, an immutable Julia &lt;code&gt;struct&lt;/code&gt; containing only &lt;code&gt;isbits&lt;/code&gt; fields (like &lt;code&gt;Point&lt;/code&gt; with its &lt;code&gt;Float64&lt;/code&gt;s) has a &lt;strong&gt;memory layout identical&lt;/strong&gt; to its corresponding C &lt;code&gt;struct&lt;/code&gt;. This allows direct memory sharing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;C Expects Pointers:&lt;/strong&gt; C functions often modify structs passed to them by taking a &lt;strong&gt;pointer&lt;/strong&gt; (&lt;code&gt;Point* p&lt;/code&gt;) rather than receiving the struct by value (&lt;code&gt;Point p&lt;/code&gt;). Passing by pointer allows the C function to modify the original struct data in the caller's memory.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia &lt;code&gt;Ref{T}&lt;/code&gt; for &lt;code&gt;T*&lt;/code&gt;:&lt;/strong&gt; When a C function expects a pointer &lt;code&gt;T*&lt;/code&gt; where &lt;code&gt;T&lt;/code&gt; is an &lt;code&gt;isbits&lt;/code&gt; type (like &lt;code&gt;Point*&lt;/code&gt;), the idiomatic and safe way to pass a Julia value &lt;code&gt;p&lt;/code&gt; of type &lt;code&gt;T&lt;/code&gt; is:

&lt;ol&gt;
&lt;li&gt; Wrap the Julia value in a &lt;code&gt;Ref&lt;/code&gt;: &lt;code&gt;p_ref = Ref(p)&lt;/code&gt;. This creates a small, GC-managed object on the heap that contains the &lt;code&gt;isbits&lt;/code&gt; data (&lt;code&gt;p&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt; Specify &lt;code&gt;Ref{Point}&lt;/code&gt; as the corresponding Julia type in the &lt;code&gt;ccall&lt;/code&gt; &lt;code&gt;ArgTypes&lt;/code&gt; tuple.&lt;/li&gt;
&lt;li&gt; Pass the &lt;code&gt;p_ref&lt;/code&gt; object itself as the argument value to &lt;code&gt;ccall&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Behind the Scenes:&lt;/strong&gt; &lt;code&gt;ccall&lt;/code&gt; recognizes the &lt;code&gt;Ref{Point}&lt;/code&gt; argument type. It uses the internal function &lt;code&gt;Base.unsafe_convert(Ptr{Point}, p_ref)&lt;/code&gt; (as seen in lesson 0091) to get a stable, GC-safe &lt;code&gt;Ptr{Point}&lt;/code&gt; pointing to the data &lt;em&gt;inside&lt;/em&gt; the &lt;code&gt;Ref&lt;/code&gt; object. This raw pointer is then passed to the C function.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How Modification Works
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The C function &lt;code&gt;move_point&lt;/code&gt; receives the &lt;code&gt;Ptr{Point}&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;It dereferences the pointer (&lt;code&gt;p-&amp;gt;x&lt;/code&gt;, &lt;code&gt;p-&amp;gt;y&lt;/code&gt;) and modifies the bytes &lt;strong&gt;at that memory address&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;This memory address belongs to the data stored &lt;em&gt;inside&lt;/em&gt; the Julia &lt;code&gt;Ref&lt;/code&gt; object (&lt;code&gt;p_ref&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;After the &lt;code&gt;ccall&lt;/code&gt; returns, the data within &lt;code&gt;p_ref&lt;/code&gt; has been changed by the C code. We can observe this by accessing the value using &lt;code&gt;p_ref[]&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Immutability Note:&lt;/strong&gt; The original immutable variable &lt;code&gt;p&lt;/code&gt; remains unchanged. The &lt;code&gt;Ref(p)&lt;/code&gt; constructor copied the &lt;em&gt;value&lt;/em&gt; of &lt;code&gt;p&lt;/code&gt; into the mutable &lt;code&gt;Ref&lt;/code&gt; container. The C function modified the data &lt;em&gt;inside the container&lt;/em&gt;, not the original immutable &lt;code&gt;p&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This &lt;code&gt;Ref{T}&lt;/code&gt; mechanism provides a safe and standard way to bridge Julia's value types (&lt;code&gt;isbits struct&lt;/code&gt;) with C's common pattern of passing structs by pointer for modification.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Calling C and Fortran Code", "Passing Pointers for Modifying Inputs":&lt;/strong&gt; Explains the use of &lt;code&gt;Ref{T}&lt;/code&gt; for passing pointers to &lt;code&gt;isbits&lt;/code&gt; types to C for modification.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Base Documentation, &lt;code&gt;Ref&lt;/code&gt;:&lt;/strong&gt; "Used to pass references to objects..."&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;To run the script:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Requires &lt;code&gt;gcc&lt;/code&gt; available.)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0117_ccall_passing_structs.jl
Compiling C code to libtemppoint.so...
Compilation successful.

&lt;span class="nt"&gt;---&lt;/span&gt; Calling C &lt;span class="k"&gt;function &lt;/span&gt;with Julia isbits struct &lt;span class="nt"&gt;---&lt;/span&gt;
Julia Point p: Point&lt;span class="o"&gt;(&lt;/span&gt;10.0, 20.0&lt;span class="o"&gt;)&lt;/span&gt;
Boxed Ref&lt;span class="o"&gt;(&lt;/span&gt;p&lt;span class="o"&gt;)&lt;/span&gt;:  Base.RefValue&lt;span class="o"&gt;{&lt;/span&gt;Point&lt;span class="o"&gt;}(&lt;/span&gt;Point&lt;span class="o"&gt;(&lt;/span&gt;10.0, 20.0&lt;span class="o"&gt;))&lt;/span&gt;
Value inside Ref before call: Point&lt;span class="o"&gt;(&lt;/span&gt;10.0, 20.0&lt;span class="o"&gt;)&lt;/span&gt;

ccall executed successfully.
Value inside Ref after call:  Point&lt;span class="o"&gt;(&lt;/span&gt;15.0, 15.0&lt;span class="o"&gt;)&lt;/span&gt;
Original variable &lt;span class="s1"&gt;'p'&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;immutable&lt;span class="o"&gt;)&lt;/span&gt; is unchanged: Point&lt;span class="o"&gt;(&lt;/span&gt;10.0, 20.0&lt;span class="o"&gt;)&lt;/span&gt;

Removed temporary library: /path/to/libtemppoint.so
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(Path and memory addresses will vary. The key is that &lt;code&gt;p_ref[]&lt;/code&gt; shows the modified values.)&lt;/em&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;0118_ccall_callbacks.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0118_ccall_callbacks.jl&lt;/span&gt;
&lt;span class="c"&gt;# Demonstrates passing a Julia function TO C as a callback pointer.&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Libc&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Cint&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cvoid&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Libdl&lt;/span&gt;

&lt;span class="c"&gt;# --- C Code Simulation ---&lt;/span&gt;
&lt;span class="c"&gt;# C code defining a function pointer type 'compare_func' and&lt;/span&gt;
&lt;span class="c"&gt;# a function 'do_comparison' that accepts and calls such a pointer.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# // C typedef for a function pointer: takes two ints, returns int&lt;/span&gt;
&lt;span class="c"&gt;# typedef int (*compare_func)(int a, int b);&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# // C function that uses the callback&lt;/span&gt;
&lt;span class="c"&gt;# int do_comparison(int a, int b, compare_func func_ptr) {&lt;/span&gt;
&lt;span class="c"&gt;#     if (func_ptr == NULL) return -999; // Basic error check&lt;/span&gt;
&lt;span class="c"&gt;#     return func_ptr(a, b); // Call the function pointer&lt;/span&gt;
&lt;span class="c"&gt;# }&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;c_code_callback&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"""
#include &amp;lt;stddef.h&amp;gt; // For NULL

typedef int (*compare_func)(int a, int b);

int do_comparison(int a, int b, compare_func func_ptr) {
    if (func_ptr == NULL) return -999;
    // Call the function provided by Julia
    return func_ptr(a, b);
}
"""&lt;/span&gt;

&lt;span class="c"&gt;# Compile the C code into a temporary shared library&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; compile_c_code&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c_code&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lib_name&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;lib_filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lib_name&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="s"&gt;"."&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;Libdl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dlext&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;isnothing&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;which&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"gcc"&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"gcc not found. Please install gcc to run this example."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="n"&gt;compile_cmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sb"&gt;`gcc -fPIC -shared -x c -o $lib_filename -`&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Compiling C code to &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;lib_filename..."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;
        &lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;compile_cmd&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"w"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;stdout&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;
            &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c_code&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
        &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Compilation successful."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;abspath&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lib_filename&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
        &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ERROR compiling C code: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;nothing&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;temp_lib_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;compile_c_code&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c_code_callback&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"libtempcallback"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;temp_lib_path&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nb"&gt;nothing&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Exiting due to compilation failure."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# --- Julia Callback and ccall ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Calling C function with Julia Callback ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 1. Define the Julia function to be used as a callback.&lt;/span&gt;
&lt;span class="c"&gt;#    CRITICAL: The argument types and return type MUST exactly match&lt;/span&gt;
&lt;span class="c"&gt;#    the C function pointer typedef, using Julia's C-compatible types.&lt;/span&gt;
&lt;span class="c"&gt;#    C 'int' maps to Julia 'Cint'.&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; julia_comparator&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Cint&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Cint&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Cint&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Julia Callback 'julia_comparator' Executing ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"    Received: a=&lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;a, b=&lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;b"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kt"&gt;Cint&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elseif&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kt"&gt;Cint&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kt"&gt;Cint&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 2. Create a C-callable function pointer using '@cfunction'.&lt;/span&gt;
&lt;span class="c"&gt;#    Syntax: @cfunction(julia_function_name, ReturnType, (ArgType1, ...))&lt;/span&gt;
&lt;span class="c"&gt;#    This generates a GC-safe pointer that C code can invoke.&lt;/span&gt;
&lt;span class="n"&gt;c_func_ptr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;@cfunction&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;julia_comparator&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cint&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Cint&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cint&lt;/span&gt;&lt;span class="x"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Generated C function pointer: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c_func_ptr&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Prints the Ptr{Cvoid} address&lt;/span&gt;

&lt;span class="c"&gt;# 3. Perform the ccall to the C function 'do_comparison'.&lt;/span&gt;
&lt;span class="c"&gt;#    - C function pointer 'compare_func' maps to 'Ptr{Cvoid}' in ArgTypes.&lt;/span&gt;
&lt;span class="c"&gt;#    - Pass the 'c_func_ptr' obtained from @cfunction as the argument value.&lt;/span&gt;
&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="n"&gt;ccall&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;
        &lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;do_comparison&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;temp_lib_path&lt;/span&gt;&lt;span class="x"&gt;),&lt;/span&gt; &lt;span class="c"&gt;# C function name and library&lt;/span&gt;
        &lt;span class="kt"&gt;Cint&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt;                            &lt;span class="c"&gt;# Return type: int -&amp;gt; Cint&lt;/span&gt;
        &lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Cint&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cint&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Ptr&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;Cvoid&lt;/span&gt;&lt;span class="x"&gt;}),&lt;/span&gt;        &lt;span class="c"&gt;# Arg types: (int, int, compare_func)&lt;/span&gt;
        &lt;span class="kt"&gt;Cint&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="x"&gt;),&lt;/span&gt; &lt;span class="kt"&gt;Cint&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="x"&gt;),&lt;/span&gt; &lt;span class="n"&gt;c_func_ptr&lt;/span&gt;    &lt;span class="c"&gt;# Arg values: pass ints and the function pointer&lt;/span&gt;
    &lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ERROR during ccall: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="kt"&gt;Cint&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;999&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Error value&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# --- Verification and Cleanup ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;ccall to 'do_comparison' finished."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Result returned from C (via Julia callback): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Should be 1&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="n"&gt;rm&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;temp_lib_path&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Removed temporary library: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;temp_lib_path&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Warning: Could not remove temporary library '&lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;temp_lib_path': "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script demonstrates a powerful feature of Julia's C interoperability: passing a &lt;strong&gt;Julia function&lt;/strong&gt; to a C library that expects a &lt;strong&gt;function pointer&lt;/strong&gt; (often called a &lt;strong&gt;callback&lt;/strong&gt;). This allows C code to call back into your Julia code, enabling patterns like event handling or custom comparison functions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Core Concept: C Function Pointers and Callbacks
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;C Function Pointers:&lt;/strong&gt; In C, you can store the memory address of a function in a variable (a function pointer). This pointer can then be passed to other functions, which can invoke the original function via the pointer. &lt;code&gt;typedef int (*compare_func)(int a, int b);&lt;/code&gt; defines &lt;code&gt;compare_func&lt;/code&gt; as a type representing a pointer to a function that takes two &lt;code&gt;int&lt;/code&gt;s and returns an &lt;code&gt;int&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Callbacks:&lt;/strong&gt; This mechanism is frequently used for callbacks. A library function (like C's &lt;code&gt;qsort&lt;/code&gt; or our &lt;code&gt;do_comparison&lt;/code&gt;) takes a function pointer as an argument. The library function performs some generic operation but calls the user-provided function pointer at specific points to customize behavior (e.g., to compare elements during sorting or to handle an event).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Julia's Solution: &lt;code&gt;@cfunction&lt;/code&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Bridge:&lt;/strong&gt; Julia provides the &lt;strong&gt;&lt;code&gt;@cfunction&lt;/code&gt;&lt;/strong&gt; macro to bridge the gap between Julia functions and C function pointers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Syntax:&lt;/strong&gt; &lt;code&gt;@cfunction(julia_function_name, ReturnType, (ArgType1, ...))&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;julia_function_name&lt;/code&gt;: The name of the Julia function you want C to call.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ReturnType&lt;/code&gt;: The Julia C-compatible type corresponding to the C function pointer's return type (e.g., &lt;code&gt;Cint&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;(ArgType1, ...)&lt;/code&gt;: A &lt;code&gt;Tuple&lt;/code&gt; of Julia C-compatible types corresponding to the C function pointer's argument types (e.g., &lt;code&gt;(Cint, Cint)&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Return Value:&lt;/strong&gt; &lt;code&gt;@cfunction&lt;/code&gt; returns a &lt;code&gt;Ptr{Cvoid}&lt;/code&gt; (equivalent to &lt;code&gt;void*&lt;/code&gt;), which is the raw function pointer address that C code can understand and call.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Type Safety:&lt;/strong&gt; The &lt;code&gt;ReturnType&lt;/code&gt; and &lt;code&gt;ArgTypes&lt;/code&gt; provided to &lt;code&gt;@cfunction&lt;/code&gt; &lt;strong&gt;must exactly match&lt;/strong&gt; the signature expected by the C code (defined by the &lt;code&gt;typedef&lt;/code&gt; or function prototype). Mismatches will lead to crashes. Your Julia function (&lt;code&gt;julia_comparator&lt;/code&gt;) must also adhere to this signature.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;GC Safety:&lt;/strong&gt; Pointers generated by &lt;code&gt;@cfunction&lt;/code&gt; are &lt;strong&gt;safe with respect to Julia's Garbage Collector&lt;/strong&gt;. Julia ensures that the underlying Julia function (&lt;code&gt;julia_comparator&lt;/code&gt;) and the necessary runtime context will &lt;strong&gt;not&lt;/strong&gt; be garbage collected as long as the C function pointer might still be used by C code. &lt;code&gt;@cfunction&lt;/code&gt; handles the complex details of generating a "trampoline" or "thunk" that C calls, which then sets up the Julia environment correctly before calling your Julia code.&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;ccall&lt;/code&gt; with Function Pointers
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;When calling a C function (like &lt;code&gt;do_comparison&lt;/code&gt;) that expects a function pointer argument (like &lt;code&gt;compare_func&lt;/code&gt;), the corresponding Julia type in the &lt;code&gt;ccall&lt;/code&gt; &lt;code&gt;ArgTypes&lt;/code&gt; tuple is typically &lt;strong&gt;&lt;code&gt;Ptr{Cvoid}&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;You pass the pointer generated by &lt;code&gt;@cfunction&lt;/code&gt; (&lt;code&gt;c_func_ptr&lt;/code&gt;) as the value for that argument.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Use Cases (HFT Context)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Asynchronous Event Handling:&lt;/strong&gt; Network libraries or market data APIs often use callbacks. They might require you to register a function pointer (&lt;code&gt;on_order_update&lt;/code&gt;, &lt;code&gt;on_market_data&lt;/code&gt;) that the library will call when a specific event occurs. You implement the handler logic in Julia and use &lt;code&gt;@cfunction&lt;/code&gt; to pass it to the C library.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom Sorting/Comparison:&lt;/strong&gt; C library functions like &lt;code&gt;qsort&lt;/code&gt; require a comparison function pointer. You can provide a Julia function for custom sorting logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integrating with C Frameworks:&lt;/strong&gt; Many C frameworks use function pointers for plugins or extensions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;@cfunction&lt;/code&gt; provides a safe and efficient way for Julia code to respond to events or customize behavior within native C libraries.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Calling C and Fortran Code", "Passing C-compatible Function Pointers":&lt;/strong&gt; Explains &lt;code&gt;@cfunction&lt;/code&gt; and its usage for callbacks.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;To run the script:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Requires &lt;code&gt;gcc&lt;/code&gt; available.)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0118_ccall_callbacks.jl
Compiling C code to libtempcallback.so...
Compilation successful.

&lt;span class="nt"&gt;---&lt;/span&gt; Calling C &lt;span class="k"&gt;function &lt;/span&gt;with Julia Callback &lt;span class="nt"&gt;---&lt;/span&gt;
Generated C &lt;span class="k"&gt;function &lt;/span&gt;pointer: Ptr&lt;span class="o"&gt;{&lt;/span&gt;Cvoid&lt;span class="o"&gt;}(&lt;/span&gt;0x...&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nt"&gt;---&lt;/span&gt; Julia Callback &lt;span class="s1"&gt;'julia_comparator'&lt;/span&gt; Executing &lt;span class="nt"&gt;---&lt;/span&gt;
    Received: &lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;10, &lt;span class="nv"&gt;b&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5
ccall to &lt;span class="s1"&gt;'do_comparison'&lt;/span&gt; finished.
Result returned from C &lt;span class="o"&gt;(&lt;/span&gt;via Julia callback&lt;span class="o"&gt;)&lt;/span&gt;: 1

Removed temporary library: /path/to/libtempcallback.so
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(Memory address and path will vary. The output confirms that the C code successfully called the Julia function.)&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Operating System Interaction
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;0119_libc_calls.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0119_libc_calls.jl&lt;/span&gt;
&lt;span class="c"&gt;# Demonstrates using the Libc standard library for C functions.&lt;/span&gt;

&lt;span class="c"&gt;# 1. Import the Libc module and specific names.&lt;/span&gt;
&lt;span class="c"&gt;#    Libc contains wrappers for many standard C library functions&lt;/span&gt;
&lt;span class="c"&gt;#    and C-compatible types (already imported in previous lessons).&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Libc&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;malloc&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;free&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt; &lt;span class="c"&gt;# Import specific function wrappers&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Libc&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Clong&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cvoid&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;C_NULL&lt;/span&gt; &lt;span class="c"&gt;# Import needed types&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Using Libc Wrappers ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 2. Calling simple wrapped functions.&lt;/span&gt;
&lt;span class="c"&gt;#    Instead of 'ccall(:time, ...)', we can call 'Libc.time()'.&lt;/span&gt;
&lt;span class="c"&gt;#    This wrapper handles the ccall internally.&lt;/span&gt;
&lt;span class="n"&gt;current_time_t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Libc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Libc.time(): "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;current_time_t&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# --- Manual Memory Management with Libc.malloc/free ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Manual Memory Management (Outside GC) ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 3. Allocate memory directly from the C heap using 'Libc.malloc'.&lt;/span&gt;
&lt;span class="c"&gt;#    This memory is *NOT* tracked by Julia's Garbage Collector.&lt;/span&gt;
&lt;span class="n"&gt;bytes_to_alloc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;sizeof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Request space for 10 doubles&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Allocating &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;bytes_to_alloc bytes using Libc.malloc..."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Libc.malloc returns Ptr{Cvoid} (like void*). Returns C_NULL on failure.&lt;/span&gt;
&lt;span class="n"&gt;ptr_void&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Libc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;malloc&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes_to_alloc&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ptr_void&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;C_NULL&lt;/span&gt;
    &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Libc.malloc failed to allocate memory."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Received raw pointer: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ptr_void&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 4. Convert the raw pointer to a typed pointer.&lt;/span&gt;
&lt;span class="n"&gt;ptr_float&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;convert&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Ptr&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;&lt;span class="x"&gt;},&lt;/span&gt; &lt;span class="n"&gt;ptr_void&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Typed pointer: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ptr_float&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 5. Use the allocated memory (e.g., via unsafe_store!).&lt;/span&gt;
&lt;span class="c"&gt;#    We are responsible for ensuring we stay within the allocated bounds.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Writing values using unsafe_store!..."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;
    &lt;span class="n"&gt;unsafe_store!&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ptr_float&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Float64&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;1.1&lt;/span&gt;&lt;span class="x"&gt;),&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 6. Read back values using unsafe_load.&lt;/span&gt;
&lt;span class="n"&gt;val5&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;unsafe_load&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ptr_float&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;val10&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;unsafe_load&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ptr_float&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Value at index 5: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;val5&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Value at index 10: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;val10&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 7. CRITICAL: Manually free the memory using 'Libc.free'.&lt;/span&gt;
&lt;span class="c"&gt;#    Failure to do this results in a memory leak, as the GC doesn't know&lt;/span&gt;
&lt;span class="c"&gt;#    about this memory.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Freeing manually allocated memory using Libc.free..."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Libc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;free&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ptr_void&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Pass the original Ptr{Cvoid}&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Memory freed."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Attempting to access ptr_float now would be undefined behavior (use after free).&lt;/span&gt;
&lt;span class="c"&gt;# val_after_free = unsafe_load(ptr_float, 1) # DO NOT DO THIS&lt;/span&gt;

&lt;span class="c"&gt;# --- Alternative: Using unsafe_wrap with own=true ---&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Managing malloc'd Memory with unsafe_wrap(..., own=true) ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 8. Allocate again.&lt;/span&gt;
&lt;span class="n"&gt;ptr_void_2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Libc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;malloc&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes_to_alloc&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ptr_void_2&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;C_NULL&lt;/span&gt;&lt;span class="x"&gt;;&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"malloc failed"&lt;/span&gt;&lt;span class="x"&gt;);&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="n"&gt;ptr_float_2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;convert&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Ptr&lt;/span&gt;&lt;span class="x"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;&lt;span class="x"&gt;},&lt;/span&gt; &lt;span class="n"&gt;ptr_void_2&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Allocated second block at: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ptr_float_2&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 9. Use unsafe_wrap with 'own=true'.&lt;/span&gt;
&lt;span class="c"&gt;#    This creates a Julia Vector view and transfers ownership to the GC.&lt;/span&gt;
&lt;span class="c"&gt;#    The GC will call 'Libc.free(ptr_void_2)' when 'owned_array' is finalized.&lt;/span&gt;
&lt;span class="n"&gt;owned_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;unsafe_wrap&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Array&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ptr_float_2&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="x"&gt;;&lt;/span&gt; &lt;span class="n"&gt;own&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 10. Use the array normally.&lt;/span&gt;
&lt;span class="n"&gt;owned_array&lt;/span&gt; &lt;span class="o"&gt;.=&lt;/span&gt; &lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;2.2&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="x"&gt;]&lt;/span&gt; &lt;span class="c"&gt;# Initialize using broadcasting&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Owned wrapped array: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;owned_array&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 11. DO NOT manually free ptr_void_2. The GC handles it via 'own=true'.&lt;/span&gt;
&lt;span class="c"&gt;# Libc.free(ptr_void_2) # WRONG - would cause double-free later.&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GC will free the memory for 'owned_array' when it's no longer reachable."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces the &lt;code&gt;Libc&lt;/code&gt; standard library module, which provides convenient Julia wrappers for many common C standard library functions, most notably memory management functions like &lt;code&gt;malloc&lt;/code&gt; and &lt;code&gt;free&lt;/code&gt;. It demonstrates how to allocate and manage memory &lt;strong&gt;outside&lt;/strong&gt; of Julia's garbage collector control.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;Libc&lt;/code&gt; Module: Convenience Wrappers
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Purpose:&lt;/strong&gt; Instead of writing &lt;code&gt;ccall((:time, :libc), Clong, ...)&lt;/code&gt; repeatedly, the &lt;code&gt;Libc&lt;/code&gt; module pre-defines wrappers like &lt;code&gt;Libc.time()&lt;/code&gt;. These wrappers handle the correct &lt;code&gt;ccall&lt;/code&gt; signature internally, providing a more Julian interface to standard C functions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Usage:&lt;/strong&gt; &lt;code&gt;import Base.Libc&lt;/code&gt; or import specific functions like &lt;code&gt;import Base.Libc: malloc, free&lt;/code&gt;. You can then call them directly (e.g., &lt;code&gt;Libc.malloc(...)&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Manual Memory Management: &lt;code&gt;Libc.malloc&lt;/code&gt; and &lt;code&gt;Libc.free&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;This is the most critical feature demonstrated here, relevant for specific low-level performance and interoperability scenarios.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;Libc.malloc(size::Integer)&lt;/code&gt;:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Allocates a block of &lt;code&gt;size&lt;/code&gt; bytes directly from the &lt;strong&gt;C heap&lt;/strong&gt; (using the system's &lt;code&gt;malloc&lt;/code&gt; implementation).&lt;/li&gt;
&lt;li&gt;Returns a &lt;code&gt;Ptr{Cvoid}&lt;/code&gt; (like &lt;code&gt;void*&lt;/code&gt;) pointing to the start of the block, or &lt;code&gt;C_NULL&lt;/code&gt; if allocation fails.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Crucially:&lt;/strong&gt; This memory is &lt;strong&gt;NOT tracked by Julia's Garbage Collector (GC)&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Using the Memory:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;You typically &lt;code&gt;convert&lt;/code&gt; the &lt;code&gt;Ptr{Cvoid}&lt;/code&gt; to a typed pointer (e.g., &lt;code&gt;Ptr{Float64}&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;You can then read/write using &lt;code&gt;unsafe_load&lt;/code&gt;/&lt;code&gt;unsafe_store!&lt;/code&gt; (as shown) or create a view using &lt;code&gt;unsafe_wrap&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;You are &lt;strong&gt;entirely responsible&lt;/strong&gt; for managing the bounds of this memory block.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;Libc.free(ptr::Ptr{Cvoid})&lt;/code&gt;:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Explicitly releases&lt;/strong&gt; the memory block pointed to by &lt;code&gt;ptr&lt;/code&gt; (which &lt;em&gt;must&lt;/em&gt; have been previously allocated by &lt;code&gt;Libc.malloc&lt;/code&gt; or a compatible C allocator) back to the C heap.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mandatory:&lt;/strong&gt; If you allocate with &lt;code&gt;Libc.malloc&lt;/code&gt;, you &lt;strong&gt;must&lt;/strong&gt; ensure &lt;code&gt;Libc.free&lt;/code&gt; is called exactly once on that pointer when the memory is no longer needed. Failure to do so results in a &lt;strong&gt;memory leak&lt;/strong&gt;. Calling &lt;code&gt;free&lt;/code&gt; more than once (double-free) or on an invalid pointer leads to heap corruption and crashes.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Managing &lt;code&gt;malloc&lt;/code&gt;'d Memory with &lt;code&gt;unsafe_wrap(..., own=true)&lt;/code&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;As seen in Module 9, &lt;code&gt;unsafe_wrap&lt;/code&gt; provides a convenient way to manage &lt;code&gt;malloc&lt;/code&gt;'d memory by transferring ownership to Julia's GC.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;unsafe_wrap(Array, ptr, dims; own = true)&lt;/code&gt; creates a Julia &lt;code&gt;Array&lt;/code&gt; view onto the memory at &lt;code&gt;ptr&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;own = true&lt;/code&gt; flag tells the GC: "When this array object is finalized, call &lt;code&gt;Libc.free&lt;/code&gt; on the original &lt;code&gt;ptr&lt;/code&gt;."&lt;/li&gt;
&lt;li&gt;This automates the &lt;code&gt;free&lt;/code&gt; call, reducing the risk of memory leaks or double-frees compared to purely manual management. &lt;strong&gt;This is generally the preferred way&lt;/strong&gt; to work with &lt;code&gt;malloc&lt;/code&gt;'d memory that you intend to use primarily through a Julia &lt;code&gt;Array&lt;/code&gt; interface.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Use Manual Memory Management? (HFT Context)
&lt;/h2&gt;

&lt;p&gt;While generally discouraged in favor of letting Julia's GC manage memory, direct &lt;code&gt;malloc&lt;/code&gt;/&lt;code&gt;free&lt;/code&gt; (often managed via &lt;code&gt;unsafe_wrap(..., own=true)&lt;/code&gt;) is sometimes necessary in high-performance or systems-level code for:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Interfacing with C libraries:&lt;/strong&gt; C APIs might require you to pass pointers to memory allocated via &lt;code&gt;malloc&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Avoiding GC Pauses:&lt;/strong&gt; For extremely latency-sensitive operations, you might allocate critical large buffers (e.g., for network packets or market data snapshots) using &lt;code&gt;malloc&lt;/code&gt; to ensure the GC &lt;em&gt;never&lt;/em&gt; scans, moves, or pauses due to those specific buffers. You would typically use &lt;code&gt;unsafe_wrap(..., own=false)&lt;/code&gt; to create temporary views into these long-lived, manually managed buffers.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Custom Allocators:&lt;/strong&gt; Integrating with specialized memory allocators.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Use manual memory management sparingly and carefully, with &lt;code&gt;unsafe_wrap(..., own=true)&lt;/code&gt; being the safer option when feasible.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Standard Library, &lt;code&gt;Libc&lt;/code&gt;:&lt;/strong&gt; Lists available C standard library functions and types.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;C Standard Library Documentation (e.g., man pages for &lt;code&gt;malloc&lt;/code&gt;, &lt;code&gt;free&lt;/code&gt;):&lt;/strong&gt; Defines the behavior of the underlying C functions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Base Documentation, &lt;code&gt;unsafe_wrap&lt;/code&gt;:&lt;/strong&gt; Explains the &lt;code&gt;own&lt;/code&gt; parameter for managing externally allocated memory.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;To run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0119_libc_calls.jl
&lt;span class="nt"&gt;---&lt;/span&gt; Using Libc Wrappers &lt;span class="nt"&gt;---&lt;/span&gt;
Libc.time&lt;span class="o"&gt;()&lt;/span&gt;: 1.761134061489851e9

&lt;span class="nt"&gt;---&lt;/span&gt; Manual Memory Management &lt;span class="o"&gt;(&lt;/span&gt;Outside GC&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
Allocating 80 bytes using Libc.malloc...
Received raw pointer: Ptr&lt;span class="o"&gt;{&lt;/span&gt;Nothing&lt;span class="o"&gt;}(&lt;/span&gt;0x000000003ace19a0&lt;span class="o"&gt;)&lt;/span&gt;
Typed pointer: Ptr&lt;span class="o"&gt;{&lt;/span&gt;Float64&lt;span class="o"&gt;}(&lt;/span&gt;0x000000003ace19a0&lt;span class="o"&gt;)&lt;/span&gt;
Writing values using unsafe_store!...
Value at index 5: 5.5
Value at index 10: 11.0
Freeing manually allocated memory using Libc.free...
Memory freed.

&lt;span class="nt"&gt;---&lt;/span&gt; Managing malloc&lt;span class="s1"&gt;'d Memory with unsafe_wrap(..., own=true) ---
Allocated second block at: Ptr{Float64}(0x000000003ace19a0)
Owned wrapped array: [2.2, 4.4, 6.6000000000000005, 8.8, 11.0, 13.200000000000001, 15.400000000000002, 17.6, 19.8, 22.0]
GC will free the memory for '&lt;/span&gt;owned_array&lt;span class="s1"&gt;' when it'&lt;/span&gt;s no longer reachable.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(Memory addresses will vary.)&lt;/em&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;0120_cpu_affinity.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0120_cpu_affinity.jl&lt;/span&gt;
&lt;span class="c"&gt;# Demonstrates pinning Julia threads to specific CPU cores using ThreadPinning.jl.&lt;/span&gt;
&lt;span class="c"&gt;# Requires the ThreadPinning.jl package and running Julia with multiple threads.&lt;/span&gt;

&lt;span class="c"&gt;# 1. Import the package. See Explanation for installation.&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ThreadPinning&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ERROR: ThreadPinning.jl not found."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Please install it: Open Julia REPL, type ']', then 'add ThreadPinning'"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Threads&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nd"&gt;@spawn&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;threadid&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nthreads&lt;/span&gt;

&lt;span class="c"&gt;# 2. Check if multi-threading is enabled.&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;nthreads&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"WARNING: Multi-threading is DISABLED (Threads.nthreads() == &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;(nthreads()))."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Restart Julia with '-t N' (N &amp;gt;= 2) to run this demo."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- CPU Affinity Demo using ThreadPinning.jl ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Total Julia threads available: "&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nthreads&lt;/span&gt;&lt;span class="x"&gt;())&lt;/span&gt;

&lt;span class="c"&gt;# 3. Display initial system topology and thread placement (optional but informative).&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Initial State ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# threadinfo() provides a visual overview of cores, sockets, NUMA nodes,&lt;/span&gt;
&lt;span class="c"&gt;# and where Julia threads are currently allowed to run (or currently are).&lt;/span&gt;
&lt;span class="c"&gt;# By default, threads usually aren't pinned and can run anywhere.&lt;/span&gt;
&lt;span class="n"&gt;ThreadPinning&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;threadinfo&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;

&lt;span class="c"&gt;# 4. Pin threads using a predefined strategy.&lt;/span&gt;
&lt;span class="c"&gt;#    ':cores' attempts to pin each Julia thread to a distinct physical core,&lt;/span&gt;
&lt;span class="c"&gt;#    avoiding hyperthreads if possible. Other options include :sockets, :numa,&lt;/span&gt;
&lt;span class="c"&gt;#    or explicit core IDs (e.g., 0:3).&lt;/span&gt;
&lt;span class="n"&gt;pinning_strategy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;cores&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Pinning threads with strategy: &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;pinning_strategy ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="n"&gt;ThreadPinning&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pinthreads&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pinning_strategy&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Pinning successful (using pinthreads)."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ERROR during pinning: &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;e"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Ensure you have appropriate permissions (may require admin/root on some systems)."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# Continue without pinning if it fails&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 5. Display the state *after* pinning.&lt;/span&gt;
&lt;span class="c"&gt;#    threadinfo() should now show each Julia thread restricted to specific cores.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- State After Pinning ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ThreadPinning&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;threadinfo&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;

&lt;span class="c"&gt;# (Optional: Add work here using @spawn or @threads to see tasks running on pinned threads)&lt;/span&gt;

&lt;span class="c"&gt;# 6. Unpin threads to restore default OS scheduling.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Unpinning threads ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="n"&gt;ThreadPinning&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unpinthreads&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Unpinning successful."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ERROR during unpinning: &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;e"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 7. Display the state after unpinning.&lt;/span&gt;
&lt;span class="c"&gt;#    Should revert towards the initial state where threads can run on any core,&lt;/span&gt;
&lt;span class="c"&gt;#    though the OS might keep them somewhat localized initially.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- State After Unpinning ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ThreadPinning&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;threadinfo&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Affinity demo finished."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script demonstrates &lt;strong&gt;CPU core pinning&lt;/strong&gt; (also known as setting thread affinity), a crucial technique in low-latency systems to ensure predictable performance by controlling which CPU core(s) a specific thread can run on. It uses the &lt;strong&gt;&lt;code&gt;ThreadPinning.jl&lt;/code&gt;&lt;/strong&gt; package.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Installation Note:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ThreadPinning.jl&lt;/code&gt; is an external package. You need to add it to your project environment once.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Start the Julia REPL: &lt;code&gt;julia&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt; Enter Pkg mode: &lt;code&gt;]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt; Add the package: &lt;code&gt;add ThreadPinning&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt; Exit Pkg mode: Press Backspace or &lt;code&gt;Ctrl+C&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; You can now run this script (remembering to start Julia with multiple threads). Note that pinning functionality is primarily supported on &lt;strong&gt;Linux&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Core Concept: Thread Affinity and Performance Jitter
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Default OS Scheduling:&lt;/strong&gt; By default, the operating system's scheduler is free to &lt;strong&gt;migrate&lt;/strong&gt; a running thread between different CPU cores.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Problem: Cache Invalidation &amp;amp; Jitter:&lt;/strong&gt; When a thread moves from Core A to Core B, data in Core A's L1/L2 caches becomes useless for that thread. The thread must repopulate Core B's caches, causing a significant, unpredictable &lt;strong&gt;performance stall&lt;/strong&gt; or &lt;strong&gt;latency spike (jitter)&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Low-Latency Impact:&lt;/strong&gt; In HFT and other real-time systems, unpredictable jitter is unacceptable. Consistent, low latency is paramount.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Solution: Core Pinning (&lt;code&gt;ThreadPinning.jl&lt;/code&gt;)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CPU Affinity:&lt;/strong&gt; This refers to the set of CPU cores on which a thread is &lt;em&gt;allowed&lt;/em&gt; to run. Core pinning involves explicitly setting a thread's affinity, often to a single, specific core or a limited set.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ThreadPinning.jl&lt;/code&gt;:&lt;/strong&gt; Provides functions to control thread affinity:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ThreadPinning.threadinfo(; kwargs...)&lt;/code&gt;:&lt;/strong&gt; Displays a detailed visualization of the system topology (sockets, cores, hyperthreads, NUMA domains) and shows where Julia threads are currently placed or allowed to run. Indispensable for verifying pinning.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ThreadPinning.pinthreads(strategy; kwargs...)&lt;/code&gt;:&lt;/strong&gt; Pins Julia threads according to a specified &lt;code&gt;strategy&lt;/code&gt;. Common strategies include:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;:cores&lt;/code&gt;: Pin threads sequentially to physical cores, avoiding hyperthreads if possible.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;:sockets&lt;/code&gt;: Distribute threads round-robin across CPU sockets.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;:numa&lt;/code&gt;: Distribute threads round-robin across NUMA memory domains.&lt;/li&gt;
&lt;li&gt;Explicit Core IDs: Pass a vector or range of OS core IDs (e.g., &lt;code&gt;0:3&lt;/code&gt; or &lt;code&gt;[0, 2, 4]&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;&lt;code&gt;ThreadPinning.unpinthreads()&lt;/code&gt;:&lt;/strong&gt; Removes pinning restrictions for all Julia threads, restoring the default OS scheduling behavior.&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Benefits of Pinning:&lt;/strong&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Eliminates Migration:&lt;/strong&gt; Prevents OS scheduler-induced moves.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Maximizes Cache Locality:&lt;/strong&gt; Keeps thread data hot in specific L1/L2 caches.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Reduces Jitter:&lt;/strong&gt; Leads to more predictable, lower-latency execution.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Reduces Interference:&lt;/strong&gt; Isolates critical threads from other processes competing for the same core.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Typical HFT Architecture
&lt;/h2&gt;

&lt;p&gt;A common pattern is dedicating specific threads (pinned to specific cores) to distinct tasks (Network I/O, Strategy A, Strategy B, Order Management) to maximize cache efficiency and minimize interference.&lt;/p&gt;

&lt;h2&gt;
  
  
  Important Notes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Permissions:&lt;/strong&gt; Setting thread affinity might require specific OS permissions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Platform:&lt;/strong&gt; &lt;code&gt;ThreadPinning.jl&lt;/code&gt;'s pinning functions work primarily on &lt;strong&gt;Linux&lt;/strong&gt;. Querying functions like &lt;code&gt;threadinfo&lt;/code&gt; may work elsewhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Core Indexing:&lt;/strong&gt; OS core/CPU IDs are typically &lt;strong&gt;0-indexed&lt;/strong&gt;. Be mindful when providing explicit lists. &lt;code&gt;ThreadPinning.jl&lt;/code&gt;'s documentation clarifies its indexing conventions. The &lt;code&gt;threadinfo&lt;/code&gt; output mapping clarifies which Julia thread ID maps to which OS core ID.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Core pinning is an advanced but essential technique for optimizing latency-sensitive applications by taking control of thread placement from the OS scheduler.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ThreadPinning.jl&lt;/code&gt; Documentation:&lt;/strong&gt; (&lt;a href="https://github.com/carstenbauer/ThreadPinning.jl" rel="noopener noreferrer"&gt;https://github.com/carstenbauer/ThreadPinning.jl&lt;/a&gt;). The primary source for usage and available strategies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Operating System Documentation (Linux &lt;code&gt;sched_setaffinity&lt;/code&gt;):&lt;/strong&gt; Describes the underlying OS system calls.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;To run the script:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Requires &lt;code&gt;ThreadPinning.jl&lt;/code&gt; installed and Julia started with multiple threads, e.g., &lt;code&gt;julia -t 4 0120_cpu_affinity.jl&lt;/code&gt;. Output indicates an Intel i9-13900HX with 24 CPU-threads.)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia &lt;span class="nt"&gt;-t&lt;/span&gt; 4 0120_cpu_affinity.jl
&lt;span class="nt"&gt;---&lt;/span&gt; CPU Affinity Demo using ThreadPinning.jl &lt;span class="nt"&gt;---&lt;/span&gt;
Total Julia threads available: 4

&lt;span class="nt"&gt;---&lt;/span&gt; Initial State &lt;span class="nt"&gt;---&lt;/span&gt;
Hostname:       a8b1b1c0bbc3
CPU&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt;:         1 x 13th Gen Intel&lt;span class="o"&gt;(&lt;/span&gt;R&lt;span class="o"&gt;)&lt;/span&gt; Core&lt;span class="o"&gt;(&lt;/span&gt;TM&lt;span class="o"&gt;)&lt;/span&gt; i9-13900HX
CPU target:     alderlake
Cores:          24 &lt;span class="o"&gt;(&lt;/span&gt;24 CPU-threads&lt;span class="o"&gt;)&lt;/span&gt;
Core kinds:     16 &lt;span class="s2"&gt;"efficiency cores"&lt;/span&gt;, 8 &lt;span class="s2"&gt;"performance cores"&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
NUMA domains:   1 &lt;span class="o"&gt;(&lt;/span&gt;24 cores each&lt;span class="o"&gt;)&lt;/span&gt;

Julia threads:  4

CPU socket 1
  0,1,2,3,4,5,6,7,8,9 &lt;span class="o"&gt;(&lt;/span&gt;J1&lt;span class="o"&gt;)&lt;/span&gt;,10,11,12,13,14,15,
  16,17 &lt;span class="o"&gt;(&lt;/span&gt;J2&lt;span class="o"&gt;)&lt;/span&gt;,18,19,20,21 &lt;span class="o"&gt;(&lt;/span&gt;J3&lt;span class="o"&gt;)&lt;/span&gt;,22 &lt;span class="o"&gt;(&lt;/span&gt;J4&lt;span class="o"&gt;)&lt;/span&gt;,23
&lt;span class="c"&gt;# ... Legend ...&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;Mapping: 1 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 9, 2 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 17, 3 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 21, 4 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 22,&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Initial OS placement&lt;/span&gt;

&lt;span class="nt"&gt;---&lt;/span&gt; Pinning threads with strategy: cores &lt;span class="nt"&gt;---&lt;/span&gt;
Pinning successful &lt;span class="o"&gt;(&lt;/span&gt;using pinthreads&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="nt"&gt;---&lt;/span&gt; State After Pinning &lt;span class="nt"&gt;---&lt;/span&gt;
Hostname:       a8b1b1c0bbc3
CPU&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt;:         1 x 13th Gen Intel&lt;span class="o"&gt;(&lt;/span&gt;R&lt;span class="o"&gt;)&lt;/span&gt; Core&lt;span class="o"&gt;(&lt;/span&gt;TM&lt;span class="o"&gt;)&lt;/span&gt; i9-13900HX
CPU target:     alderlake
Cores:          24 &lt;span class="o"&gt;(&lt;/span&gt;24 CPU-threads&lt;span class="o"&gt;)&lt;/span&gt;
Core kinds:     16 &lt;span class="s2"&gt;"efficiency cores"&lt;/span&gt;, 8 &lt;span class="s2"&gt;"performance cores"&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
NUMA domains:   1 &lt;span class="o"&gt;(&lt;/span&gt;24 cores each&lt;span class="o"&gt;)&lt;/span&gt;

Julia threads:  4

CPU socket 1
  0 &lt;span class="o"&gt;(&lt;/span&gt;J1&lt;span class="o"&gt;)&lt;/span&gt;,1 &lt;span class="o"&gt;(&lt;/span&gt;J2&lt;span class="o"&gt;)&lt;/span&gt;,2 &lt;span class="o"&gt;(&lt;/span&gt;J3&lt;span class="o"&gt;)&lt;/span&gt;,3 &lt;span class="o"&gt;(&lt;/span&gt;J4&lt;span class="o"&gt;)&lt;/span&gt;,4,5,6,7,8,9,10,11,12,13,14,15,
  16,17,18,19,20,21,22,23
&lt;span class="c"&gt;# ... Legend ...&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;Mapping: 1 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 0, 2 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 1, 3 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 2, 4 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 3,&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Pinned to first cores&lt;/span&gt;

&lt;span class="nt"&gt;---&lt;/span&gt; Unpinning threads &lt;span class="nt"&gt;---&lt;/span&gt;
Unpinning successful.

&lt;span class="nt"&gt;---&lt;/span&gt; State After Unpinning &lt;span class="nt"&gt;---&lt;/span&gt;
Hostname:       a8b1b1c0bbc3
CPU&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt;:         1 x 13th Gen Intel&lt;span class="o"&gt;(&lt;/span&gt;R&lt;span class="o"&gt;)&lt;/span&gt; Core&lt;span class="o"&gt;(&lt;/span&gt;TM&lt;span class="o"&gt;)&lt;/span&gt; i9-13900HX
CPU target:     alderlake
Cores:          24 &lt;span class="o"&gt;(&lt;/span&gt;24 CPU-threads&lt;span class="o"&gt;)&lt;/span&gt;
Core kinds:     16 &lt;span class="s2"&gt;"efficiency cores"&lt;/span&gt;, 8 &lt;span class="s2"&gt;"performance cores"&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
NUMA domains:   1 &lt;span class="o"&gt;(&lt;/span&gt;24 cores each&lt;span class="o"&gt;)&lt;/span&gt;

Julia threads:  4

CPU socket 1
  0 &lt;span class="o"&gt;(&lt;/span&gt;J2&lt;span class="o"&gt;)&lt;/span&gt;,1 &lt;span class="o"&gt;(&lt;/span&gt;J1&lt;span class="o"&gt;)&lt;/span&gt;,2,3 &lt;span class="o"&gt;(&lt;/span&gt;J3&lt;span class="o"&gt;)&lt;/span&gt;,4 &lt;span class="o"&gt;(&lt;/span&gt;J4&lt;span class="o"&gt;)&lt;/span&gt;,5,6,7,8,9,10,11,12,13,14,15, &lt;span class="c"&gt;# Example OS placement&lt;/span&gt;
  16,17,18,19,20,21,22,23
&lt;span class="c"&gt;# ... Legend ...&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;Mapping: 1 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 1, 2 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 0, 3 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 3, 4 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 4,&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Example after unpinning&lt;/span&gt;

Affinity demo finished.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Profiling Performance
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;0121_profiler_basics.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0121_profiler_basics.jl&lt;/span&gt;
&lt;span class="c"&gt;# Introduces the built-in Profile standard library.&lt;/span&gt;

&lt;span class="c"&gt;# 1. Import the Profile module (part of Julia's standard library).&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Profile&lt;/span&gt;

&lt;span class="c"&gt;# 2. Define some functions with varying amounts of "work".&lt;/span&gt;
&lt;span class="c"&gt;#    (Using simple loops; real work would be more complex).&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; work_level_1&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="x"&gt;;&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;sin&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sqrt&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;float&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="x"&gt;)));&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; work_level_2&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# Calls level 1 multiple times&lt;/span&gt;
    &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;
        &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;work_level_1&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;÷&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="c"&gt;# Add some work at this level too&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;÷&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="x"&gt;);&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;cos&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;float&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="x"&gt;));&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; main_computation&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Starting main computation..."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# Call the intermediate function&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;work_level_2&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Main computation finished."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# --- Profiling ---&lt;/span&gt;

&lt;span class="c"&gt;# 3. Warmup Run (CRITICAL!)&lt;/span&gt;
&lt;span class="c"&gt;#    We MUST run the code once *before* profiling to ensure&lt;/span&gt;
&lt;span class="c"&gt;#    all functions are compiled by the JIT. Profiling the first&lt;/span&gt;
&lt;span class="c"&gt;#    run would incorrectly measure compilation time.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Warming up (compiling) functions ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;warmup_n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1_000_000&lt;/span&gt;
&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;main_computation&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;warmup_n&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# Discard result using '_'&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Warmup finished."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 4. Clear any previous profiling data.&lt;/span&gt;
&lt;span class="n"&gt;Profile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;clear&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;

&lt;span class="c"&gt;# 5. Run the code under the profiler using 'Profile.@profile'.&lt;/span&gt;
&lt;span class="c"&gt;#    Need to qualify '@profile' since we used 'import Profile'.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Running computation under @profile ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;profile_n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5_000_000&lt;/span&gt; &lt;span class="c"&gt;# Use a larger N for profiling&lt;/span&gt;
&lt;span class="n"&gt;Profile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nd"&gt;@profile&lt;/span&gt; &lt;span class="n"&gt;main_computation&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;profile_n&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Profiling finished."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 6. Print the profiling results to the console.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Displaying Profile Results (Text Format) ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# 'Profile.print()' displays the collected stack traces.&lt;/span&gt;
&lt;span class="c"&gt;# Options like 'format=:flat' or 'sortedby=:count' exist.&lt;/span&gt;
&lt;span class="n"&gt;Profile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="o"&gt;=:&lt;/span&gt;&lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sortedby&lt;/span&gt;&lt;span class="o"&gt;=:&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Optional: Clear data after printing if you intend to profile something else later.&lt;/span&gt;
&lt;span class="c"&gt;# Profile.clear()&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- End of Script ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script introduces Julia's built-in &lt;strong&gt;statistical profiler&lt;/strong&gt;, available through the &lt;code&gt;Profile&lt;/code&gt; standard library. Profiling is essential for identifying &lt;strong&gt;performance bottlenecks&lt;/strong&gt; – the specific parts of your code where the most execution time is spent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Core Concept: Statistical (Sampling) Profiling
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;How it Works:&lt;/strong&gt; Julia's profiler is a &lt;strong&gt;sampling&lt;/strong&gt; profiler. It periodically interrupts the program's execution and records the &lt;strong&gt;stack trace&lt;/strong&gt; – the sequence of functions currently being executed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Statistical Inference:&lt;/strong&gt; By collecting many such samples, it builds a statistical picture of where the program spends its time. Functions that appear frequently at the &lt;em&gt;top&lt;/em&gt; of the recorded stack traces are likely the "hot spots" consuming the most CPU time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Low Overhead:&lt;/strong&gt; Sampling profilers generally have low overhead, making them suitable for analyzing performance-critical code.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Using the Profiler
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;import Profile&lt;/code&gt;:&lt;/strong&gt; Load the standard library module.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Warmup (Critical):&lt;/strong&gt; Run your code at least once &lt;em&gt;before&lt;/em&gt; profiling to ensure JIT compilation is complete. Profiling the first run measures compilation time, not execution performance.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;Profile.clear()&lt;/code&gt;:&lt;/strong&gt; Clear any pre-existing profiling data before starting a new measurement.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;Profile.@profile expression&lt;/code&gt;:&lt;/strong&gt; (Note the qualification &lt;code&gt;Profile.@profile&lt;/code&gt; because we used &lt;code&gt;import Profile&lt;/code&gt;). This macro enables sampling, executes the &lt;code&gt;expression&lt;/code&gt;, and stops sampling. Data is stored internally.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;Profile.print(...)&lt;/code&gt;:&lt;/strong&gt; Analyzes collected samples and prints a formatted report. Key options include:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;format=:tree&lt;/code&gt; (default): Hierarchical call stack view.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;format=:flat&lt;/code&gt;: Flat list sorted by time spent &lt;em&gt;in&lt;/em&gt; the function itself.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sortedby=:count&lt;/code&gt; (default): Sorts by sample frequency.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;C=true&lt;/code&gt;: Include calls into C libraries.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;noisefloor=...&lt;/code&gt;: Hide entries below a percentage threshold.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Interpreting the Tree Output
&lt;/h2&gt;

&lt;p&gt;The default tree format shows stack traces. Read from &lt;strong&gt;bottom to top&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Count File:Line Function                    # Example Line
--------------------------------------------------------------
[100] ... main_computation                 # 100 samples total in this call stack
 [98] ... work_level_2                     # 98 samples were within work_level_2 or its children
  [90] ... work_level_1                    # 90 samples were further down inside work_level_1 (the hot spot)
  [8]  ... work_level_2                    # 8 samples were directly in work_level_2's own code
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Counts/Percentages:&lt;/strong&gt; High counts/percentages, especially deep in the indentation (leaves of the tree), indicate functions consuming significant time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Identifying Bottlenecks:&lt;/strong&gt; Look for the widest bars (highest counts) deepest in the call tree. In the example output provided previously, &lt;code&gt;work_level_1&lt;/code&gt; and the math functions it calls (&lt;code&gt;sin&lt;/code&gt;, &lt;code&gt;sqrt&lt;/code&gt;, &lt;code&gt;float&lt;/code&gt;) were clearly identified as the primary consumers of time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Profiling is iterative: profile, identify, optimize, profile again.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Manual, "Profiling":&lt;/strong&gt; Main guide to using the &lt;code&gt;Profile&lt;/code&gt; module.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Standard Library, &lt;code&gt;Profile&lt;/code&gt;:&lt;/strong&gt; Documents &lt;code&gt;@profile&lt;/code&gt;, &lt;code&gt;Profile.print&lt;/code&gt;, &lt;code&gt;Profile.clear&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;To run the script:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Ensure you're running with Julia 1.0 or later)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0121_profiler_basics.jl
&lt;span class="nt"&gt;---&lt;/span&gt; Warming up &lt;span class="o"&gt;(&lt;/span&gt;compiling&lt;span class="o"&gt;)&lt;/span&gt; functions &lt;span class="nt"&gt;---&lt;/span&gt;
Starting main computation...
Main computation finished.
Warmup finished.

&lt;span class="nt"&gt;---&lt;/span&gt; Running computation under @profile &lt;span class="nt"&gt;---&lt;/span&gt;
Starting main computation...
Main computation finished.
Profiling finished.

&lt;span class="nt"&gt;---&lt;/span&gt; Displaying Profile Results &lt;span class="o"&gt;(&lt;/span&gt;Text Format&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
Overhead ╎ &lt;span class="o"&gt;[&lt;/span&gt;+additional indent] Count File:Line  Function
&lt;span class="o"&gt;=========================================================&lt;/span&gt;
  ╎60  @Base/client.jl:550  _start&lt;span class="o"&gt;()&lt;/span&gt;
  ╎ 60  @Base/client.jl:317  exec_options&lt;span class="o"&gt;(&lt;/span&gt;opts::Base.JLOptions&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="c"&gt;# ... (Rest of the detailed profile tree as shown in your output) ...&lt;/span&gt;
  &lt;span class="c"&gt;# ... showing significant time spent within work_level_1 and its calls ...&lt;/span&gt;

&lt;span class="nt"&gt;---&lt;/span&gt; End of Script &lt;span class="nt"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;0122_profiler_flamegraphs.jl&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# 0122_profiler_flamegraphs.jl&lt;/span&gt;
&lt;span class="c"&gt;# Visualizing Profile data by saving to a file for use with 'pprof'.&lt;/span&gt;

&lt;span class="c"&gt;# 1. Import Profile module and PProf package. See Explanation for installation.&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Profile&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="c"&gt;# PProf is needed for the pprof() function to save the data.&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;PProf&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ERROR: PProf.jl not found."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Please install it: Open Julia REPL, type ']', then 'add PProf'"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Viewing the output file requires the external 'pprof' tool (Go)."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# 2. Reuse the functions from the previous lesson.&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; work_level_1&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="x"&gt;;&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;sin&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sqrt&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;float&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="x"&gt;)));&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; work_level_2&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;
        &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;work_level_1&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;÷&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;÷&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="x"&gt;);&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;cos&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;float&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="x"&gt;));&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; main_computation&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Starting main computation..."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;work_level_2&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Main computation finished."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# --- Profiling ---&lt;/span&gt;

&lt;span class="c"&gt;# 3. Warmup Run (as before).&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Warming up (compiling) functions ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;warmup_n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1_000_000&lt;/span&gt;
&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;main_computation&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;warmup_n&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Warmup finished."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 4. Clear existing profile data.&lt;/span&gt;
&lt;span class="n"&gt;Profile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;clear&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;

&lt;span class="c"&gt;# 5. Run the code under the profiler.&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Running computation under Profile.@profile ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;profile_n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5_000_000&lt;/span&gt;
&lt;span class="n"&gt;Profile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nd"&gt;@profile&lt;/span&gt; &lt;span class="n"&gt;main_computation&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;profile_n&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Profiling finished."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 6. Save the profile data to a file using PProf.jl.&lt;/span&gt;
&lt;span class="n"&gt;output_filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"profile.pb.gz"&lt;/span&gt;
&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Saving profile data to '&lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;output_filename' using PProf.jl ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="c"&gt;# PProf.pprof() reads data collected by 'Profile' and saves it&lt;/span&gt;
    &lt;span class="c"&gt;# to the specified file when 'out=' is used.&lt;/span&gt;
    &lt;span class="n"&gt;PProf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pprof&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;output_filename&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Profile data saved successfully."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Viewing Instructions (Requires External Tools) ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"1. Install 'go' (golang.dev/doc/install)."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"2. Install 'pprof': go install github.com/google/pprof@latest"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"3. Install 'graphviz' (system package manager, e.g., apt, brew)."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"4. Ensure '&lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;HOME/go/bin' is in your PATH."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"5. Run from terminal: pprof -http=:8080 &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;output_filename"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"6. Open http://localhost:8080 in browser and select 'Flame Graph'."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"(Note: Author did not test the viewing steps.)"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
     &lt;span class="c"&gt;# Catch potential errors during saving, including the "Unexpected 0" warning.&lt;/span&gt;
     &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Error/Warning during profile data saving using PProf: &lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;e"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
     &lt;span class="c"&gt;# Check if file was still created despite warning&lt;/span&gt;
     &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;isfile&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output_filename&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"'&lt;/span&gt;&lt;span class="si"&gt;$&lt;/span&gt;&lt;span class="s"&gt;output_filename' was created, but may contain issues (see warning above)."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Viewing instructions still apply, but results might be affected."&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
     &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;# Note: For VS Code users, the Julia extension provides '@profview',&lt;/span&gt;
&lt;span class="c"&gt;# which displays an interactive flame graph directly within the editor&lt;/span&gt;
&lt;span class="c"&gt;# after running Profile.@profile, without needing PProf.jl or external tools.&lt;/span&gt;

&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- End of Script ---"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;This script demonstrates how to &lt;strong&gt;visualize&lt;/strong&gt; the data collected by Julia's &lt;code&gt;Profile&lt;/code&gt; module using &lt;strong&gt;flame graphs&lt;/strong&gt; by saving the data to a file compatible with Google's &lt;strong&gt;&lt;code&gt;pprof&lt;/code&gt;&lt;/strong&gt; tool, using the &lt;code&gt;PProf.jl&lt;/code&gt; package. Viewing requires installing external tools.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Installation Note (PProf.jl &amp;amp; Viewer):&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Install &lt;code&gt;PProf.jl&lt;/code&gt;:&lt;/strong&gt; Add via Julia's Pkg mode (&lt;code&gt;] add PProf&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Install Viewer (&lt;code&gt;pprof&lt;/code&gt; + &lt;code&gt;graphviz&lt;/code&gt;):&lt;/strong&gt; To &lt;em&gt;view&lt;/em&gt; the saved file later, you need external tools:

&lt;ul&gt;
&lt;li&gt;Install the Go language (&lt;code&gt;go&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Install &lt;code&gt;pprof&lt;/code&gt; via &lt;code&gt;go install github.com/google/pprof@latest&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Install &lt;code&gt;graphviz&lt;/code&gt; via your system package manager.&lt;/li&gt;
&lt;li&gt;Ensure the &lt;code&gt;go&lt;/code&gt; binary path is in your system &lt;code&gt;PATH&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Why Visualize? Flame Graphs
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Text Output Limitations:&lt;/strong&gt; &lt;code&gt;Profile.print()&lt;/code&gt; can be hard to interpret visually.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flame Graphs:&lt;/strong&gt; Provide an intuitive visualization of sampled stack trace data.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Y-Axis:&lt;/strong&gt; Call stack depth.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;X-Axis (Width):&lt;/strong&gt; Proportion of samples where a function appeared. &lt;strong&gt;Wider bars = more time spent&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Identifying Bottlenecks:&lt;/strong&gt; Look for &lt;strong&gt;wide plateaus&lt;/strong&gt; at the top of the graph, indicating functions consuming significant CPU time directly.&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Saving Profile Data (&lt;code&gt;PProf.pprof&lt;/code&gt;)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Collect Data:&lt;/strong&gt; Use &lt;code&gt;Profile.@profile expression&lt;/code&gt; (after warmup and &lt;code&gt;Profile.clear()&lt;/code&gt;) to collect sampling data internally.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Save Data:&lt;/strong&gt; &lt;code&gt;PProf.pprof(out=filename)&lt;/code&gt; accesses the data collected by &lt;code&gt;Profile&lt;/code&gt; and exports it into the compressed protobuf format (&lt;code&gt;.pb.gz&lt;/code&gt;), saving it to &lt;code&gt;filename&lt;/code&gt;. (Note: This function might print warnings like "Unexpected 0 in data" but often still saves a usable file).&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Viewing Saved Data with &lt;code&gt;pprof&lt;/code&gt; (External Tool)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Run &lt;code&gt;pprof&lt;/code&gt;:&lt;/strong&gt; After running the Julia script and generating &lt;code&gt;profile.pb.gz&lt;/code&gt;, open your terminal &lt;em&gt;in the same directory&lt;/em&gt; and run (assuming &lt;code&gt;pprof&lt;/code&gt; is installed and in your &lt;code&gt;PATH&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pprof &lt;span class="nt"&gt;-http&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;:8080 profile.pb.gz
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  * `-http=:8080`: Starts a web server on port 8080.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Open Browser:&lt;/strong&gt; Navigate to &lt;code&gt;http://localhost:8080&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Explore:&lt;/strong&gt; Use the "View" menu to select &lt;strong&gt;"Flame Graph"&lt;/strong&gt;. Interact with the visualization.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Shutdown:&lt;/strong&gt; Press &lt;code&gt;Ctrl+C&lt;/code&gt; in the terminal running &lt;code&gt;pprof&lt;/code&gt; to stop its server.
&lt;em&gt;(Disclaimer: The author did not perform these viewing steps.)&lt;/em&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Alternative: VS Code &lt;code&gt;@profview&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;If using VS Code with the Julia extension:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Add &lt;code&gt;using ProfileView&lt;/code&gt; (might need &lt;code&gt;Pkg.add("ProfileView")&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt; Run &lt;code&gt;Profile.@profile main_computation(profile_n)&lt;/code&gt; as before.&lt;/li&gt;
&lt;li&gt; Run &lt;code&gt;@profview()&lt;/code&gt; &lt;em&gt;after&lt;/em&gt; the &lt;code&gt;@profile&lt;/code&gt; call.&lt;/li&gt;
&lt;li&gt; An interactive flame graph appears directly within a VS Code panel, no external tools needed.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Saving profile data provides a standard way to analyze performance offline or share results, while integrated options offer convenience.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;References:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Julia Official Documentation, Standard Library, &lt;code&gt;Profile&lt;/code&gt;:&lt;/strong&gt; Documents &lt;code&gt;Profile.@profile&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;PProf.jl&lt;/code&gt; Documentation:&lt;/strong&gt; (&lt;a href="https://github.com/JuliaPerf/PProf.jl" rel="noopener noreferrer"&gt;https://github.com/JuliaPerf/PProf.jl&lt;/a&gt;) Explains the &lt;code&gt;pprof()&lt;/code&gt; function, including the &lt;code&gt;out&lt;/code&gt; argument.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;pprof&lt;/code&gt; Documentation (Google):&lt;/strong&gt; (&lt;a href="https://github.com/google/pprof" rel="noopener noreferrer"&gt;https://github.com/google/pprof&lt;/a&gt;) Explains the command-line tool and web UI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Brendan Gregg's Flame Graphs Page:&lt;/strong&gt; (&lt;a href="https://www.brendangregg.com/flamegraphs.html" rel="noopener noreferrer"&gt;https://www.brendangregg.com/flamegraphs.html&lt;/a&gt;) Definitive guide to flame graphs.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;To run the script:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Requires &lt;code&gt;PProf.jl&lt;/code&gt; installed. Run after warmup.)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;julia 0122_profiler_flamegraphs.jl
&lt;span class="nt"&gt;---&lt;/span&gt; Warming up &lt;span class="o"&gt;(&lt;/span&gt;compiling&lt;span class="o"&gt;)&lt;/span&gt; functions &lt;span class="nt"&gt;---&lt;/span&gt;
Starting main computation...
Main computation finished.
Warmup finished.

&lt;span class="nt"&gt;---&lt;/span&gt; Running computation under Profile.@profile &lt;span class="nt"&gt;---&lt;/span&gt;
Starting main computation...
Main computation finished.
Profiling finished.

&lt;span class="nt"&gt;---&lt;/span&gt; Saving profile data to &lt;span class="s1"&gt;'profile.pb.gz'&lt;/span&gt; using PProf.jl &lt;span class="nt"&gt;---&lt;/span&gt;
┌ Error: Unexpected 0 &lt;span class="k"&gt;in &lt;/span&gt;data, please file an issue. &lt;span class="c"&gt;# This warning might appear&lt;/span&gt;
│   idx &lt;span class="o"&gt;=&lt;/span&gt; XXXX
└ @ PProf ...
Profile data saved successfully.

&lt;span class="nt"&gt;---&lt;/span&gt; Viewing Instructions &lt;span class="o"&gt;(&lt;/span&gt;Requires External Tools&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
1. Install &lt;span class="s1"&gt;'go'&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;golang.dev/doc/install&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
2. Install &lt;span class="s1"&gt;'pprof'&lt;/span&gt;: go &lt;span class="nb"&gt;install &lt;/span&gt;github.com/google/pprof@latest
3. Install &lt;span class="s1"&gt;'graphviz'&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;system package manager, e.g., apt, brew&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
4. Ensure &lt;span class="s1"&gt;'$HOME/go/bin'&lt;/span&gt; is &lt;span class="k"&gt;in &lt;/span&gt;your PATH.
5. Run from terminal: pprof &lt;span class="nt"&gt;-http&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;:8080 profile.pb.gz
6. Open http://localhost:8080 &lt;span class="k"&gt;in &lt;/span&gt;browser and &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="s1"&gt;'Flame Graph'&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;Note: Author did not &lt;span class="nb"&gt;test &lt;/span&gt;the viewing steps.&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nt"&gt;---&lt;/span&gt; End of Script &lt;span class="nt"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(After running, you should find &lt;code&gt;profile.pb.gz&lt;/code&gt;. Use the separate &lt;code&gt;pprof&lt;/code&gt; command to view.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;NOTE: I did not test pprof visualization. &lt;/p&gt;




</description>
      <category>julialang</category>
      <category>tutorial</category>
      <category>systems</category>
      <category>performance</category>
    </item>
    <item>
      <title>Part 06: Building a Sovereign Software Factory: SonarQube Quality Gates</title>
      <dc:creator>Warren Jitsing</dc:creator>
      <pubDate>Sat, 20 Dec 2025 08:12:34 +0000</pubDate>
      <link>https://dev.to/warren_jitsing_dd1c1d6fc6/part-06-building-a-sovereign-software-factory-sonarqube-quality-gates-5499</link>
      <guid>https://dev.to/warren_jitsing_dd1c1d6fc6/part-06-building-a-sovereign-software-factory-sonarqube-quality-gates-5499</guid>
      <description>&lt;p&gt;GitHub: &lt;a href="https://github.com/InfiniteConsult/0010_cicd_part06_sonarqube" rel="noopener noreferrer"&gt;https://github.com/InfiniteConsult/0010_cicd_part06_sonarqube&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; In this installment, we solve the "Blind Spot" where our pipeline builds code without inspecting it. We deploy &lt;strong&gt;SonarQube&lt;/strong&gt; as our &lt;strong&gt;"Inspector,"&lt;/strong&gt; but first, we must navigate the &lt;strong&gt;"Abstraction Leak"&lt;/strong&gt; of Elasticsearch by tuning the host kernel's &lt;code&gt;vm.max_map_count&lt;/code&gt; via an architect script. We build a custom Docker image to inject our Root CA into the Java Truststore, bridging the "Trust Gap," and implement a strict &lt;strong&gt;Quality Gate&lt;/strong&gt; that stops the factory line if our polyglot code (C++, Rust, Python) fails to meet security or coverage standards.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Sovereign Software Factory Series:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Part 01:&lt;/strong&gt; Building a Sovereign Software Factory: Docker Networking &amp;amp; Persistence&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 02:&lt;/strong&gt; Building a Sovereign Software Factory: The Local Root CA &amp;amp; Trust Chains&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 03:&lt;/strong&gt; Building a Sovereign Software Factory: Self-Hosted GitLab &amp;amp; Secrets Management&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 04:&lt;/strong&gt; Building a Sovereign Software Factory: Jenkins Configuration as Code (JCasC)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 05:&lt;/strong&gt; Building a Sovereign Software Factory: Artifactory &amp;amp; The "Strict TLS" Trap&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 06: Building a Sovereign Software Factory: SonarQube Quality Gates&lt;/strong&gt; (You are here)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 07:&lt;/strong&gt; Building a Sovereign Software Factory: ChatOps with Mattermost&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 08:&lt;/strong&gt; Building a Sovereign Software Factory: Observability with the ELK Stack&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 09:&lt;/strong&gt; Building a Sovereign Software Factory: Monitoring with Prometheus &amp;amp; Grafana&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 10:&lt;/strong&gt; Building a Sovereign Software Factory: The Python API Package (Capstone)&lt;/li&gt;
&lt;/ol&gt;




&lt;h1&gt;
  
  
  Chapter 1: The Challenge - Quantity vs. Quality
&lt;/h1&gt;

&lt;h2&gt;
  
  
  1.1 The "Green Build" Fallacy
&lt;/h2&gt;

&lt;p&gt;In our previous session, we completed the construction of the &lt;strong&gt;"Software Supply Chain."&lt;/strong&gt; We successfully integrated &lt;strong&gt;GitLab&lt;/strong&gt; (The Library), &lt;strong&gt;Jenkins&lt;/strong&gt; (The Factory), and &lt;strong&gt;Artifactory&lt;/strong&gt; (The Warehouse) into a seamless, automated conduit. When a developer pushes code, our city springs to life: webhooks fire, agents are provisioned, code is compiled, and immutable artifacts are delivered to secure storage.&lt;/p&gt;

&lt;p&gt;If you look at your Jenkins dashboard right now, you will likely see a column of green checks. The pipeline works. The artifacts are safe. The system is functioning exactly as designed.&lt;/p&gt;

&lt;p&gt;But this "Green Build" is a lie.&lt;/p&gt;

&lt;p&gt;We have built a system that prioritizes &lt;strong&gt;Quantity over Quality&lt;/strong&gt;. Our factory is incredibly efficient at moving boxes, but it has absolutely no idea what is &lt;em&gt;inside&lt;/em&gt; them. If a developer commits a C++ memory leak, a Python type error, or a Rust panic handler, our pipeline will happily compile it, package it, and ship it to the warehouse with a stamp of approval. We are effectively filling our secure bunker with "Time Bombs"—defective software that will only explode when it reaches production.&lt;/p&gt;

&lt;p&gt;This reveals a critical "Blind Spot" in our architecture. We have established &lt;strong&gt;Continuous Integration&lt;/strong&gt; (merging code) and &lt;strong&gt;Continuous Delivery&lt;/strong&gt; (shipping code), but we have completely neglected &lt;strong&gt;Continuous Inspection&lt;/strong&gt;. We have no way to measure the &lt;em&gt;health&lt;/em&gt; of our codebase. We don't know if our test coverage is improving or degrading. We don't know if our cyclomatic complexity is spiraling out of control. We are flying blind, trusting that "if it compiles, it works."&lt;/p&gt;

&lt;p&gt;In a high-assurance environment—like the one we are simulating—this is unacceptable. A build that compiles but introduces a critical security vulnerability is not a success; it is a containment breach. We need a mechanism to detect these flaws &lt;em&gt;before&lt;/em&gt; the artifact is signed and sealed.&lt;/p&gt;

&lt;h2&gt;
  
  
  1.2 The "Quality Gate" Concept
&lt;/h2&gt;

&lt;p&gt;To solve this, we must introduce a new entity to our city: the &lt;strong&gt;"Inspector."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Architecturally, this Inspector sits between the Factory (Jenkins) and the Warehouse (Artifactory). Its role is not to build code, but to analyze it. It must disassemble the "box" our factory produced, x-ray the contents, measure the tolerances, and verify that the product meets our engineering standards.&lt;/p&gt;

&lt;p&gt;But inspection alone is passive. A report that says "Your code has 50 bugs" is useless if the pipeline has already shipped that code to the warehouse.&lt;/p&gt;

&lt;p&gt;We need to implement a &lt;strong&gt;Quality Gate&lt;/strong&gt;. This is a binary decision point in our pipeline. It transforms our "Inspector" from a passive observer into an active gatekeeper. The Inspector must have the authority to &lt;strong&gt;"Stop the Line"&lt;/strong&gt; (the famous Toyota "Andon Cord" principle).&lt;/p&gt;

&lt;p&gt;If the code coverage drops below 80%, the line stops. If a new security vulnerability is detected, the line stops. If the technical debt ratio exceeds 5%, the line stops.&lt;/p&gt;

&lt;p&gt;When the line stops, the build fails. The artifact is rejected. It never reaches the Warehouse. This ensures that every single artifact in Artifactory is not just "built," but "certified."&lt;/p&gt;

&lt;h2&gt;
  
  
  1.3 The Solution: SonarQube Community Build
&lt;/h2&gt;

&lt;p&gt;To fulfill this role, we will deploy &lt;strong&gt;SonarQube&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;SonarQube is the industry standard for static code analysis. It provides a centralized dashboard that tracks code health over time, visualizing metrics like duplication, complexity, and test coverage.&lt;/p&gt;

&lt;p&gt;However, we must navigate a specific constraint. We are deploying the &lt;strong&gt;SonarQube Community Build&lt;/strong&gt; (specifically version 25.x). This free version is powerful, but it comes with architectural limitations that distinguish it from the paid Enterprise editions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;No Native C or C++ Analysis:&lt;/strong&gt; Out of the box, the Community Build ignores both C and C++ files entirely. Since our "Hero Project" is a true polyglot implementation—containing distinct, idiomatic C23 code &lt;em&gt;and&lt;/em&gt; C++23 code—this is a major blocker. We will have to engineer a "First Principles" workaround using community plugins to "unlock" analysis for these compiled languages.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Branch Analysis Limitations:&lt;/strong&gt; It generally only analyzes the &lt;code&gt;main&lt;/code&gt; branch, limiting our ability to decorate Pull Requests directly.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Despite these constraints, it is the perfect tool for our "Inspector." Our goal is to deploy it into our secure &lt;code&gt;cicd-net&lt;/code&gt;, force it to trust our internal PKI, and integrate it with Jenkins to enforce a strict Quality Gate on our C, C++, Rust, and Python code.&lt;/p&gt;

&lt;h1&gt;
  
  
  Chapter 2: Architecture - The Host &amp;amp; The Kernel
&lt;/h1&gt;

&lt;h2&gt;
  
  
  2.1 The Hidden Dependency: Elasticsearch &amp;amp; The Kernel
&lt;/h2&gt;

&lt;p&gt;To understand why deploying SonarQube is fundamentally different from deploying Jenkins or GitLab, we must first deconstruct its internal architecture.&lt;/p&gt;

&lt;p&gt;When you launch a Jenkins container, you are essentially launching a standard Java Web Archive (WAR) inside a Jetty web server. It is a monolithic, CPU-bound application. If you give it enough RAM, it runs.&lt;/p&gt;

&lt;p&gt;SonarQube, however, is not a single application; it is a distributed system packaged into a single binary. When the container starts, a master Java process spawns three distinct child processes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;The Web Server:&lt;/strong&gt; Serves the UI and API.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The Compute Engine:&lt;/strong&gt; Processes the heavy analysis reports sent by scanners.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The Search Engine (Elasticsearch):&lt;/strong&gt; Indexes every line of code, every issue, and every metric for instant retrieval.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It is this third component—&lt;strong&gt;Elasticsearch&lt;/strong&gt;—that introduces a critical "Abstraction Leak" in our Docker environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Mechanics of &lt;code&gt;mmapfs&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Elasticsearch is built on top of &lt;strong&gt;Apache Lucene&lt;/strong&gt;, a high-performance search library. To achieve the incredible speed required to search through millions of lines of code in milliseconds, Lucene relies heavily on a file system feature called &lt;strong&gt;Memory Mapped Files (&lt;code&gt;mmap&lt;/code&gt;)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In a standard file read, the operating system copies data from the disk into a kernel buffer, and then copies it again into the application's memory. This "double copy" is slow.&lt;/p&gt;

&lt;p&gt;With &lt;code&gt;mmap&lt;/code&gt;, Lucene tells the Operating System to map the physical file on the disk directly into the process's virtual memory address space. The application can then read the file as if it were essentially a massive array in RAM. The OS handles the complexity of paging data in from the disk only when it is accessed. This eliminates the system call overhead and allows Elasticsearch to perform near-memory-speed searches on datasets that are larger than the available RAM.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Abstraction Leak
&lt;/h3&gt;

&lt;p&gt;This architecture relies on the Operating System allowing a process to create thousands upon thousands of these memory maps—one for every index segment.&lt;/p&gt;

&lt;p&gt;Here lies the conflict with Docker.&lt;/p&gt;

&lt;p&gt;While Docker provides excellent isolation for the &lt;strong&gt;Filesystem&lt;/strong&gt; (via UnionFS) and &lt;strong&gt;Process Namespace&lt;/strong&gt; (PID), it shares the &lt;strong&gt;Kernel&lt;/strong&gt; with the host machine. Kernel-level tunables, such as memory management limits, are enforced globally by the host kernel.&lt;/p&gt;

&lt;p&gt;By default, most Linux distributions (including Debian 12) are tuned for desktop or standard server workloads. They conservatively limit the maximum number of memory map areas a process can have (&lt;code&gt;vm.max_map_count&lt;/code&gt;) to approximately &lt;strong&gt;65,530&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For a web server, this is plenty. For Elasticsearch, it is suffocating. SonarQube requires a minimum of &lt;strong&gt;262,144&lt;/strong&gt; (and recommends &lt;strong&gt;524,288&lt;/strong&gt; for larger instances) to function correctly.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Failure Mode
&lt;/h3&gt;

&lt;p&gt;If we attempt to run &lt;code&gt;sonarqube:community&lt;/code&gt; on a standard Linux host without modification, we encounter a particularly frustrating failure mode:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; The container starts successfully (Docker sees no error).&lt;/li&gt;
&lt;li&gt; The SonarQube wrapper process launches.&lt;/li&gt;
&lt;li&gt; The Elasticsearch child process attempts to initialize its indices.&lt;/li&gt;
&lt;li&gt; The Host Kernel denies the &lt;code&gt;mmap&lt;/code&gt; request because the limit (65,530) is exceeded.&lt;/li&gt;
&lt;li&gt; Elasticsearch crashes silently or throws a "bootstrap check failure."&lt;/li&gt;
&lt;li&gt; The SonarQube wrapper sees its child process die, shuts down the web server, and kills the container.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To the user, the container simply enters a "Restart Loop" with no obvious error message unless you dig deep into the specific &lt;code&gt;es.log&lt;/code&gt; file inside the container volume. To build a stable "Inspector," we must proactively reconfigure the Host Kernel to support this workload.&lt;/p&gt;

&lt;h2&gt;
  
  
  2.2 The "Architect" Script (&lt;code&gt;01-setup-sonarqube.sh&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;To solve the "Abstraction Leak" described above, we cannot rely on Docker. We must intervene at the Host Operating System level.&lt;/p&gt;

&lt;p&gt;We will assign this responsibility to our &lt;strong&gt;"Architect"&lt;/strong&gt; script. Unlike previous setup scripts that primarily focused on generating configuration files or SSL certificates, this script acts as a &lt;strong&gt;Host State Enforcer&lt;/strong&gt;. It must inspect the physical machine it is running on, determine if it is capable of supporting our workload, and apply the necessary kernel modifications if it finds the host lacking.&lt;/p&gt;

&lt;p&gt;This requires a two-pronged strategy to ensure stability across time:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;The Runtime Fix (&lt;code&gt;sysctl -w&lt;/code&gt;):&lt;/strong&gt; This command writes directly to the kernel's parameters in memory (specifically &lt;code&gt;/proc/sys/vm/max_map_count&lt;/code&gt;). This change takes effect instantly, allowing us to launch the container immediately without restarting the host machine. However, because it is in memory, it is volatile; it will vanish if the server reboots.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The Persistence Fix (&lt;code&gt;/etc/sysctl.conf&lt;/code&gt;):&lt;/strong&gt; To survive a reboot, we must write the configuration to the system's control file. On boot, the OS reads this file and re-applies the settings.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A naive script might simply append the configuration to the end of the file. However, in a "First Principles" architecture, we aim for &lt;strong&gt;idempotency&lt;/strong&gt;. If we run the script ten times, it should not add ten identical lines to the configuration file. Our script must be intelligent enough to parse the existing configuration, detect if the limit is already sufficient, and update it &lt;em&gt;in place&lt;/em&gt; using &lt;code&gt;sed&lt;/code&gt; only if necessary.&lt;/p&gt;

&lt;p&gt;Create this file at &lt;code&gt;~/cicd_stack/sonarqube/01-setup-sonarqube.sh&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;#               01-setup-sonarqube.sh&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  This is the "Architect" script for SonarQube.&lt;/span&gt;
&lt;span class="c"&gt;#  It prepares the host environment.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  WARNING: This script requires SUDO privileges to update&lt;/span&gt;
&lt;span class="c"&gt;#           kernel parameters (vm.max_map_count) in /etc/sysctl.conf&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  1. Kernel Check: Enforces vm.max_map_count &amp;gt;= 524288&lt;/span&gt;
&lt;span class="c"&gt;#     (Strict requirement for the embedded Elasticsearch).&lt;/span&gt;
&lt;span class="c"&gt;#  2. Secrets: Verifies Database passwords exist in cicd.env.&lt;/span&gt;
&lt;span class="c"&gt;#  3. Env: Generates the scoped 'sonarqube.env' for Docker.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="c"&gt;# --- 1. Define Paths ---&lt;/span&gt;
&lt;span class="nv"&gt;HOST_CICD_ROOT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/cicd_stack"&lt;/span&gt;
&lt;span class="nv"&gt;SONAR_BASE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOST_CICD_ROOT&lt;/span&gt;&lt;span class="s2"&gt;/sonarqube"&lt;/span&gt;
&lt;span class="nv"&gt;MASTER_ENV_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOST_CICD_ROOT&lt;/span&gt;&lt;span class="s2"&gt;/cicd.env"&lt;/span&gt;
&lt;span class="nv"&gt;SCOPED_ENV_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SONAR_BASE&lt;/span&gt;&lt;span class="s2"&gt;/sonarqube.env"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Starting SonarQube 'Architect' Setup..."&lt;/span&gt;

&lt;span class="c"&gt;# --- 2. Kernel Prerequisites (Elasticsearch) ---&lt;/span&gt;
&lt;span class="c"&gt;# SonarQube uses embedded Elasticsearch which requires high mmap counts.&lt;/span&gt;
&lt;span class="c"&gt;# We must ensure the host allows this.&lt;/span&gt;

&lt;span class="nv"&gt;REQUIRED_MAX_MAP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;524288
&lt;span class="nv"&gt;SYSCTL_CONF&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/etc/sysctl.conf"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Phase 1: Checking Kernel Parameters ---"&lt;/span&gt;

&lt;span class="c"&gt;# A. Runtime Check (Immediate Fix)&lt;/span&gt;
&lt;span class="c"&gt;# We read the current value from the kernel&lt;/span&gt;
&lt;span class="nv"&gt;CURRENT_RUNTIME_MAP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;sysctl &lt;span class="nt"&gt;-n&lt;/span&gt; vm.max_map_count&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CURRENT_RUNTIME_MAP&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-lt&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$REQUIRED_MAX_MAP&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Limit too low (&lt;/span&gt;&lt;span class="nv"&gt;$CURRENT_RUNTIME_MAP&lt;/span&gt;&lt;span class="s2"&gt;). Updating immediately..."&lt;/span&gt;
    &lt;span class="c"&gt;# Apply the fix to the live kernel&lt;/span&gt;
    &lt;span class="nb"&gt;sudo &lt;/span&gt;sysctl &lt;span class="nt"&gt;-w&lt;/span&gt; vm.max_map_count&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$REQUIRED_MAX_MAP&lt;/span&gt;
&lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Runtime limit is sufficient (&lt;/span&gt;&lt;span class="nv"&gt;$CURRENT_RUNTIME_MAP&lt;/span&gt;&lt;span class="s2"&gt;)."&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# B. Persistence Check (sysctl.conf)&lt;/span&gt;
&lt;span class="c"&gt;# We ensure the setting survives a reboot&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"    Checking persistence in &lt;/span&gt;&lt;span class="nv"&gt;$SYSCTL_CONF&lt;/span&gt;&lt;span class="s2"&gt;..."&lt;/span&gt;

&lt;span class="c"&gt;# Check if entry exists (handling optional leading whitespace)&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s2"&gt;"^&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="s2"&gt;*vm.max_map_count"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SYSCTL_CONF&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="c"&gt;# Value exists, extract it (handling spaces around '=')&lt;/span&gt;
    &lt;span class="nv"&gt;STORED_VAL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"^&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="s2"&gt;*vm.max_map_count"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SYSCTL_CONF&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'{print $2}'&lt;/span&gt; | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'[:space:]'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# Ensure we captured a number&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$STORED_VAL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;~ ^[0-9]+&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Could not parse stored value. Appending correct config..."&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"vm.max_map_count=&lt;/span&gt;&lt;span class="nv"&gt;$REQUIRED_MAX_MAP&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SYSCTL_CONF&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$STORED_VAL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-lt&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$REQUIRED_MAX_MAP&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Stored value (&lt;/span&gt;&lt;span class="nv"&gt;$STORED_VAL&lt;/span&gt;&lt;span class="s2"&gt;) is too low. Updating config..."&lt;/span&gt;
        &lt;span class="c"&gt;# Use regex to replace the line regardless of spacing&lt;/span&gt;
        &lt;span class="nb"&gt;sudo sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"s/^&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="s2"&gt;*vm.max_map_count.*/vm.max_map_count=&lt;/span&gt;&lt;span class="nv"&gt;$REQUIRED_MAX_MAP&lt;/span&gt;&lt;span class="s2"&gt;/"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SYSCTL_CONF&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;else
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Stored configuration is sufficient (&lt;/span&gt;&lt;span class="nv"&gt;$STORED_VAL&lt;/span&gt;&lt;span class="s2"&gt;)."&lt;/span&gt;
    &lt;span class="k"&gt;fi
else&lt;/span&gt;
    &lt;span class="c"&gt;# Value missing, append it&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Value missing. Appending to config..."&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"vm.max_map_count=&lt;/span&gt;&lt;span class="nv"&gt;$REQUIRED_MAX_MAP&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SYSCTL_CONF&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null
&lt;span class="k"&gt;fi&lt;/span&gt;


&lt;span class="c"&gt;# --- 3. Directory Setup ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Phase 2: Directory Scaffolding ---"&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SONAR_BASE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# --- 4. Secrets Validation ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Phase 3: Secrets Management ---"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: Master env file not found at &lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Load Master Secrets&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt;
&lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; +a

&lt;span class="c"&gt;# Verify Database Password exists (Created in Article 9)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SONARQUBE_DB_PASSWORD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: SONARQUBE_DB_PASSWORD not found in cicd.env"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Please run the Database Setup (Article 9) first."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# --- 5. Generate Scoped Environment File ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Phase 4: Generating 'sonarqube.env' ---"&lt;/span&gt;

&lt;span class="c"&gt;# We map our master secrets to the specific env vars SonarQube expects.&lt;/span&gt;
&lt;span class="c"&gt;# We also inject the IPv4 fix here to ensure stable internal Docker networking.&lt;/span&gt;

&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt; "&lt;/span&gt;&lt;span class="nv"&gt;$SCOPED_ENV_FILE&lt;/span&gt;&lt;span class="sh"&gt;"
# Scoped Environment for SonarQube Container
# Auto-generated by 01-setup-sonarqube.sh

# Database Connection
# We use the internal Docker DNS name for Postgres
SONAR_JDBC_URL=jdbc:postgresql://postgres.cicd.local:5432/sonarqube
SONAR_JDBC_USERNAME=sonarqube
SONAR_JDBC_PASSWORD=&lt;/span&gt;&lt;span class="nv"&gt;$SONARQUBE_DB_PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;

# Network / JVM Fixes
# Forces the JVM to use IPv4 to prevent connection issues within the container
# (e.g. timeout connecting to localhost)
SONAR_WEB_JAVAADDITIONALOPTS=-Djava.net.preferIPv4Stack=true
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# Secure the file&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;600 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SCOPED_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Setup complete."&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Kernel configured."&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Secrets injected into &lt;/span&gt;&lt;span class="nv"&gt;$SCOPED_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Ready to run 02-build-image.sh"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deconstructing the Architect
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. The Host Surgery (Phase 1)&lt;/strong&gt;&lt;br&gt;
This section breaks the "Container Isolation" rule out of necessity. We use &lt;code&gt;sudo&lt;/code&gt; not to manage Docker, but to manage the Linux Kernel. The script is defensive: it checks &lt;code&gt;grep "^\s*vm.max_map_count"&lt;/code&gt; with a regex that tolerates leading whitespace, ensuring that if a human administrator manually indented the config file, our script won't blindly append a duplicate entry.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The Dependency Guard (Phase 3)&lt;/strong&gt;&lt;br&gt;
We explicitly check for &lt;code&gt;SONARQUBE_DB_PASSWORD&lt;/code&gt;. This reinforces the dependency chain we built in &lt;strong&gt;Article 9&lt;/strong&gt;. We are &lt;em&gt;not&lt;/em&gt; creating a database here. We are assuming the centralized "Water Treatment Plant" (PostgreSQL) is operational. If the password isn't in the environment file, the script fails fast, preventing us from launching a container that would immediately crash on a connection error.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The IPv4 Mandate (Phase 4)&lt;/strong&gt;&lt;br&gt;
In the generated environment file, we inject &lt;code&gt;SONAR_WEB_JAVAADDITIONALOPTS=-Djava.net.preferIPv4Stack=true&lt;/code&gt;. This is a critical stability fix for Java applications running inside Docker. By default, the JVM may attempt to bind to IPv6 addresses (&lt;code&gt;::1&lt;/code&gt;), while Docker's internal networking often routes exclusively via IPv4. This mismatch can cause inter-process communication (IPC) between the SonarQube Web Server and Compute Engine to time out, leading to a "zombie" state where the UI never loads. This flag forces the JVM to speak the language of the container network.&lt;/p&gt;
&lt;h2&gt;
  
  
  2.3 Secrets &amp;amp; The "Scoped Environment"
&lt;/h2&gt;

&lt;p&gt;With the host operating system prepared, we turn our attention to the container's configuration. In previous articles, such as Jenkins and Artifactory, we relied heavily on bind-mounting configuration files (&lt;code&gt;jenkins.yaml&lt;/code&gt; or &lt;code&gt;system.yaml&lt;/code&gt;) from the host into the container.&lt;/p&gt;

&lt;p&gt;For SonarQube, we are changing tactics. We will adopt the &lt;strong&gt;Twelve-Factor App&lt;/strong&gt; methodology, which mandates storing configuration in the &lt;strong&gt;Environment&lt;/strong&gt;, not in files.&lt;/p&gt;

&lt;p&gt;While SonarQube &lt;em&gt;does&lt;/em&gt; support a &lt;code&gt;sonar.properties&lt;/code&gt; file, modifying it inside a Docker deployment is an anti-pattern. It requires either:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Bind-mounting the file:&lt;/strong&gt; This overrides the default configuration inside the image, forcing us to maintain a complex file on the host that might drift from the container's internal defaults during an upgrade.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Building a custom image:&lt;/strong&gt; copying a static file into the image creates a hard-coded secret risk.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Instead, the official SonarQube Docker image is designed to read specific environment variables and inject them into its internal configuration at runtime. Our "Architect" script acts as a bridge, translating our master secrets into these specific variables.&lt;/p&gt;

&lt;p&gt;We generate a &lt;strong&gt;Scoped Environment File&lt;/strong&gt; (&lt;code&gt;sonarqube.env&lt;/code&gt;). This file serves a specific security purpose: &lt;strong&gt;Least Privilege&lt;/strong&gt;. We do not pass our entire &lt;code&gt;cicd.env&lt;/code&gt; (which contains GitLab tokens, Jenkins secrets, and Root CA passwords) to the SonarQube container. We generate a file containing &lt;em&gt;only&lt;/em&gt; the three variables it needs to access the database:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;SONAR_JDBC_URL&lt;/code&gt;&lt;/strong&gt;: We point this to &lt;code&gt;jdbc:postgresql://postgres.cicd.local:5432/sonarqube&lt;/code&gt;. Note that we use the internal DNS name established in Article 5.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;SONAR_JDBC_USERNAME&lt;/code&gt;&lt;/strong&gt;: &lt;code&gt;sonarqube&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;SONAR_JDBC_PASSWORD&lt;/code&gt;&lt;/strong&gt;: The high-entropy string we generated in Article 9.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach keeps our container's environment clean and audit-friendly.&lt;/p&gt;
&lt;h2&gt;
  
  
  2.4 The Dependency Chain
&lt;/h2&gt;

&lt;p&gt;Finally, our architecture enforces a strict &lt;strong&gt;Dependency Chain&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In a novice setup, a &lt;code&gt;docker-compose.yml&lt;/code&gt; file might attempt to spin up a database and the application simultaneously. This often leads to race conditions where the application crashes because the database is not yet ready to accept connections.&lt;/p&gt;

&lt;p&gt;In our "City Planning" model, we treat the database as &lt;strong&gt;Public Utility Infrastructure&lt;/strong&gt;. We established the PostgreSQL service in &lt;strong&gt;Article 9&lt;/strong&gt;. It is a permanent fixture of our city, running independently of the applications that consume it.&lt;/p&gt;

&lt;p&gt;Our &lt;code&gt;01-setup-sonarqube.sh&lt;/code&gt; script enforces this relationship. Before it writes a single configuration line, it checks for the existence of &lt;code&gt;SONARQUBE_DB_PASSWORD&lt;/code&gt; in the master environment file.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;If the variable is missing:&lt;/strong&gt; It means the database infrastructure has not been provisioned. The script fails fast with a clear error: &lt;code&gt;Please run the Database Setup (Article 9) first.&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If the variable exists:&lt;/strong&gt; It assumes the utility is online and proceeds to provision the application.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This logic prevents "Orphaned Services"—applications deployed without a backend—and ensures that our infrastructure is built in the correct, layered order: &lt;strong&gt;Network $\rightarrow$ Storage $\rightarrow$ Database $\rightarrow$ Application.&lt;/strong&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  Chapter 3: The Trust Gap - A Familiar Foe
&lt;/h1&gt;
&lt;h2&gt;
  
  
  3.1 The Connectivity Paradox (and a Confession)
&lt;/h2&gt;

&lt;p&gt;In our initial attempt to deploy this service, we fell into a trap. We launched the official &lt;code&gt;sonarqube:community&lt;/code&gt; image, updated our host's &lt;code&gt;/etc/hosts&lt;/code&gt; file to resolve &lt;code&gt;sonarqube.cicd.local&lt;/code&gt; to &lt;code&gt;127.0.0.1&lt;/code&gt;, and successfully accessed the UI on port 9000. The dashboard loaded, the database connected, and the logs were clean. We felt victorious.&lt;/p&gt;

&lt;p&gt;Then, we tried to configure the &lt;strong&gt;GitLab&lt;/strong&gt; integration. We entered our GitLab URL (&lt;code&gt;https://gitlab.cicd.local:10300/api/v4&lt;/code&gt;) and clicked "Save."&lt;/p&gt;

&lt;p&gt;The result was an immediate crash: &lt;code&gt;PKIX path building failed&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We must make a confession: even after ten articles of preaching "First Principles," we got lulled into a false sense of security by the &lt;strong&gt;Community Build's&lt;/strong&gt; primary limitation. Because this version of SonarQube does not support &lt;em&gt;inbound&lt;/em&gt; HTTPS (forcing us to run it as an HTTP service), we mentally categorized it as an "insecure" service.&lt;/p&gt;

&lt;p&gt;This was a mistake. While SonarQube listens on HTTP, it acts as a &lt;strong&gt;Client&lt;/strong&gt; to other services in our city. To import projects, it must talk to GitLab. To authenticate users, it might need to talk to LDAP. To send webhooks, it must talk to Jenkins.&lt;/p&gt;

&lt;p&gt;All of these internal services are secured by our &lt;strong&gt;Local Root CA&lt;/strong&gt;. The official SonarQube container, running a standard OpenJDK runtime, has no knowledge of this CA. It is an island. When it attempts to handshake with GitLab, it sees an unknown certificate issuer and terminates the connection to protect itself.&lt;/p&gt;

&lt;p&gt;We cannot simply "turn off SSL verification" in SonarQube without compromising the integrity of our entire architecture. We must fix the root of trust.&lt;/p&gt;
&lt;h2&gt;
  
  
  3.2 The "Builder" Pattern (Revisited)
&lt;/h2&gt;

&lt;p&gt;We faced this exact problem with the &lt;strong&gt;Jenkins Controller&lt;/strong&gt; in Article 8. The solution remains the same.&lt;/p&gt;

&lt;p&gt;We reject the "quick fix" of bind-mounting the host's &lt;code&gt;/etc/ssl/certs/java/cacerts&lt;/code&gt; file into the container. This is fragile; if the container's Java version (e.g., Java 17) differs from the host's Java version (e.g., Java 11), the binary keystore format may be incompatible, leading to cryptic startup failures.&lt;/p&gt;

&lt;p&gt;Instead, we will apply the &lt;strong&gt;Builder Pattern&lt;/strong&gt;. We will create a &lt;code&gt;Dockerfile&lt;/code&gt; that extends the official image, briefly switches to the &lt;code&gt;root&lt;/code&gt; user, and uses the native Java &lt;code&gt;keytool&lt;/code&gt; utility to "bake" our "Passport Office" license (&lt;code&gt;ca.pem&lt;/code&gt;) directly into the container's immutable system trust store.&lt;/p&gt;

&lt;p&gt;This ensures that any container spawned from this image carries our trust relationships with it, making it a first-class citizen of our secure city.&lt;/p&gt;
&lt;h2&gt;
  
  
  3.3 The Implementation (&lt;code&gt;02-build-image.sh&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;To execute this, we need two files: the &lt;code&gt;Dockerfile&lt;/code&gt; blueprint and a builder script to orchestrate the context.&lt;/p&gt;

&lt;p&gt;There is a specific nuance here regarding &lt;strong&gt;User Context&lt;/strong&gt;. SonarQube runs as a non-privileged user (&lt;code&gt;sonarqube&lt;/code&gt;, UID 1000). However, modifying the system-wide Java keystore requires root privileges. Our Dockerfile must explicitly handle this privilege escalation and then de-escalate back to the correct user to ensure runtime permissions match our volume mounts.&lt;/p&gt;

&lt;p&gt;Create the &lt;strong&gt;&lt;code&gt;Dockerfile&lt;/code&gt;&lt;/strong&gt; in &lt;code&gt;~/cicd_stack/sonarqube/&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Start from the official Community Edition&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; sonarqube:community&lt;/span&gt;

&lt;span class="c"&gt;# 2. Switch to root to perform system administration (Certificate Import)&lt;/span&gt;
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; root&lt;/span&gt;

&lt;span class="c"&gt;# 3. Copy the Local CA from the build context (Current Directory)&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; ca.pem /tmp/ca.pem&lt;/span&gt;

&lt;span class="c"&gt;# 4. Import the CA into the JVM Truststore&lt;/span&gt;
&lt;span class="c"&gt;#    We use the $JAVA_HOME environment variable provided by the base image.&lt;/span&gt;
&lt;span class="c"&gt;#    The default password for the java truststore is 'changeit'.&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;keytool &lt;span class="nt"&gt;-importcert&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nt"&gt;-file&lt;/span&gt; /tmp/ca.pem &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nt"&gt;-keystore&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JAVA_HOME&lt;/span&gt;&lt;span class="s2"&gt;/lib/security/cacerts"&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nt"&gt;-alias&lt;/span&gt; &lt;span class="s2"&gt;"CICD-Root-CA"&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nt"&gt;-storepass&lt;/span&gt; changeit &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nt"&gt;-noprompt&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; /tmp/ca.pem

&lt;span class="c"&gt;# 5. Switch back to the unprivileged sonarqube user&lt;/span&gt;
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; sonarqube&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, create the builder script, &lt;strong&gt;&lt;code&gt;02-build-image.sh&lt;/code&gt;&lt;/strong&gt;. This script handles the "Build Context" trap we encountered in previous articles by copying the CA certificate into the current directory before building.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;#               02-build-image.sh&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  This is the "Builder" script for SonarQube.&lt;/span&gt;
&lt;span class="c"&gt;#  It creates a custom image that trusts our Local Root CA.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  1. Staging: Copies ca.pem from the CA directory to CURRENT dir.&lt;/span&gt;
&lt;span class="c"&gt;#  2. Build: Creates 'sonarqube-custom:latest' from context '.'.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="c"&gt;# --- 1. Define Paths ---&lt;/span&gt;
&lt;span class="nv"&gt;CA_SOURCE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/cicd_stack/ca/pki/certs/ca.pem"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🚀 Starting SonarQube Custom Build..."&lt;/span&gt;

&lt;span class="c"&gt;# --- 2. Stage Assets ---&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CA_SOURCE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: CA certificate not found at &lt;/span&gt;&lt;span class="nv"&gt;$CA_SOURCE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Copying CA certificate to build context (current dir)..."&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CA_SOURCE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; ./ca.pem

&lt;span class="c"&gt;# --- 3. Build Image ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Building 'sonarqube-custom:latest'..."&lt;/span&gt;

&lt;span class="c"&gt;# We build from the current directory where the Dockerfile resides&lt;/span&gt;
docker build &lt;span class="nt"&gt;-t&lt;/span&gt; sonarqube-custom:latest &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# Cleanup staged file&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; ./ca.pem

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✅ Build complete."&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Image: sonarqube-custom:latest"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Ready to run 03-deploy-sonarqube.sh"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Chapter 4: Deployment - Launching the Inspector
&lt;/h1&gt;

&lt;h2&gt;
  
  
  4.1 The "Launcher" Script (&lt;code&gt;03-deploy-sonarqube.sh&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;With our host kernel tuned by the "Architect" and our trust store baked by the "Builder," we are finally ready to execute the deployment.&lt;/p&gt;

&lt;p&gt;We will use our standard &lt;strong&gt;"Launcher"&lt;/strong&gt; pattern. This script is not just a wrapper for &lt;code&gt;docker run&lt;/code&gt;; it is an enforcement mechanism for our infrastructure's state. It ensures that every deployment begins with a "Clean Slate," preventing the configuration drift that plagues manual server management.&lt;/p&gt;

&lt;p&gt;For SonarQube specifically, this script has an additional responsibility: &lt;strong&gt;Persistence Management&lt;/strong&gt;. Unlike Jenkins, which can technically survive with a single volume, SonarQube requires a strict separation of concerns for its storage to survive upgrades and plugin installations. We must verify that three distinct "Storage Lockers" (Named Volumes) exist before the application starts.&lt;/p&gt;

&lt;p&gt;Create this file at &lt;code&gt;~/cicd_stack/sonarqube/03-deploy-sonarqube.sh&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;#               03-deploy-sonarqube.sh&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  This is the "Launcher" script for SonarQube.&lt;/span&gt;
&lt;span class="c"&gt;#  It performs a clean-slate deployment of the container.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  1. Clean Slate: Stops/Removes existing container.&lt;/span&gt;
&lt;span class="c"&gt;#  2. Volumes: Ensures data, extensions, and logs volumes exist.&lt;/span&gt;
&lt;span class="c"&gt;#  3. Launch: Runs sonarqube-custom:latest with strict networking.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="c"&gt;# --- 1. Define Paths ---&lt;/span&gt;
&lt;span class="nv"&gt;HOST_CICD_ROOT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/cicd_stack"&lt;/span&gt;
&lt;span class="nv"&gt;SONAR_BASE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOST_CICD_ROOT&lt;/span&gt;&lt;span class="s2"&gt;/sonarqube"&lt;/span&gt;
&lt;span class="c"&gt;# We use the scoped environment file generated by the Architect script&lt;/span&gt;
&lt;span class="nv"&gt;SCOPED_ENV_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SONAR_BASE&lt;/span&gt;&lt;span class="s2"&gt;/sonarqube.env"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Starting SonarQube Deployment..."&lt;/span&gt;

&lt;span class="c"&gt;# --- 2. Prerequisite Checks ---&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SCOPED_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: Scoped env file not found at &lt;/span&gt;&lt;span class="nv"&gt;$SCOPED_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Please run 01-setup-sonarqube.sh first."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# --- 3. Clean Slate Protocol ---&lt;/span&gt;
&lt;span class="c"&gt;# We destroy the container to ensure configuration changes (env vars, mounts)&lt;/span&gt;
&lt;span class="c"&gt;# are applied fresh. We never just 'restart' a stale container.&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;sonarqube&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Stopping existing 'sonarqube' container..."&lt;/span&gt;
    docker stop sonarqube
&lt;span class="k"&gt;fi
if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;-aq&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;sonarqube&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Removing existing 'sonarqube' container..."&lt;/span&gt;
    docker &lt;span class="nb"&gt;rm &lt;/span&gt;sonarqube
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# --- 4. Volume Management ---&lt;/span&gt;
&lt;span class="c"&gt;# We ensure all three required storage lockers exist.&lt;/span&gt;
&lt;span class="c"&gt;# - data: The heavy Elasticsearch indices and embedded DB (if used)&lt;/span&gt;
&lt;span class="c"&gt;# - extensions: Plugins (like sonar-cxx)&lt;/span&gt;
&lt;span class="c"&gt;# - logs: Critical for debugging Elasticsearch startup failures&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Verifying Docker volumes..."&lt;/span&gt;
docker volume create sonarqube-data &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null
docker volume create sonarqube-extensions &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null
docker volume create sonarqube-logs &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Volumes verified."&lt;/span&gt;

&lt;span class="c"&gt;# --- 5. Deploy Container ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Launching SonarQube (Custom Image)..."&lt;/span&gt;

&lt;span class="c"&gt;# Notes on Configuration:&lt;/span&gt;
&lt;span class="c"&gt;# - Image: We use 'sonarqube-custom:latest' (built in Step 02)&lt;/span&gt;
&lt;span class="c"&gt;#          to ensure the JVM trusts our Local CA.&lt;/span&gt;
&lt;span class="c"&gt;# - Net:   We connect to 'cicd-net' so we can reach Postgres/GitLab.&lt;/span&gt;
&lt;span class="c"&gt;# - Port:  We bind STRICTLY to 127.0.0.1 to prevent external access.&lt;/span&gt;

docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; sonarqube &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--restart&lt;/span&gt; always &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network&lt;/span&gt; cicd-net &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--hostname&lt;/span&gt; sonarqube.cicd.local &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--publish&lt;/span&gt; 127.0.0.1:9000:9000 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--env-file&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SCOPED_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; sonarqube-data:/opt/sonarqube/data &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; sonarqube-extensions:/opt/sonarqube/extensions &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; sonarqube-logs:/opt/sonarqube/logs &lt;span class="se"&gt;\&lt;/span&gt;
  sonarqube-custom:latest

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"SonarQube container started."&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   It will take 2-3 minutes to initialize Elasticsearch."&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Monitor logs with: docker logs -f sonarqube"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Wait for: 'SonarQube is operational'"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Then access: http://sonarqube.cicd.local:9000"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   (Don't forget to add '127.0.0.1 sonarqube.cicd.local' to your /etc/hosts file)"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deconstructing the Launcher
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. The Persistence Trinity (Volume Management)&lt;/strong&gt;&lt;br&gt;
We explicitly provision three volumes. While &lt;code&gt;data&lt;/code&gt; and &lt;code&gt;extensions&lt;/code&gt; are standard, the &lt;strong&gt;&lt;code&gt;sonarqube-logs&lt;/code&gt;&lt;/strong&gt; volume is often overlooked in beginner tutorials. This is a mistake.&lt;br&gt;
SonarQube's startup sequence is complex. If the Elasticsearch engine fails (due to memory limits or corruption), the container will die immediately. If you do not persist the logs, the error message vanishes with the container. By mounting &lt;code&gt;/opt/sonarqube/logs&lt;/code&gt;, we ensure that we can perform forensics on a crashed container even after it has been removed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The Configuration Injection (&lt;code&gt;--env-file&lt;/code&gt;)&lt;/strong&gt;&lt;br&gt;
We inject the &lt;code&gt;sonarqube.env&lt;/code&gt; file we generated in Chapter 2. This keeps our &lt;code&gt;docker run&lt;/code&gt; command clean and audit-safe. It ensures that the database credentials and the critical &lt;code&gt;SONAR_WEB_JAVAADDITIONALOPTS&lt;/code&gt; flag are present in the runtime environment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The Custom Image (&lt;code&gt;sonarqube-custom:latest&lt;/code&gt;)&lt;/strong&gt;&lt;br&gt;
Critically, we do &lt;strong&gt;not&lt;/strong&gt; run &lt;code&gt;sonarqube:community&lt;/code&gt;. We run the custom image we built in Chapter 3. This is the difference between a working system and a broken one. If we reverted to the official image here, our database connection would work, but our GitLab integration would silently fail with SSL handshake errors later. We strictly enforce the use of our "Trusted" image.&lt;/p&gt;
&lt;h2&gt;
  
  
  4.2 Network Security &amp;amp; The "Localhost Binding"
&lt;/h2&gt;

&lt;p&gt;In our &lt;code&gt;docker run&lt;/code&gt; command, you might have noticed a specific nuance in the port mapping flag: &lt;code&gt;--publish 127.0.0.1:9000:9000&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is not a stylistic choice; it is a critical security control necessitated by the architecture of the &lt;strong&gt;SonarQube Community Build&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Unlike GitLab (which bundles Nginx) or Jenkins (which runs on Jetty and supports native keystores), the Community Build of SonarQube has a hard architectural constraint: &lt;strong&gt;it does not support inbound HTTPS.&lt;/strong&gt; The application server (Tomcat) is configured to speak only plain text HTTP.&lt;/p&gt;

&lt;p&gt;In a traditional "Happy Path" tutorial, you might see instructions to map ports like &lt;code&gt;-p 9000:9000&lt;/code&gt;. This is dangerous. When you omit the IP address, Docker binds the port to &lt;code&gt;0.0.0.0&lt;/code&gt;—all network interfaces. This means your unencrypted SonarQube instance, carrying sensitive code metrics and potentially authentication tokens, would be instantly accessible to anyone on your local Wi-Fi network or corporate LAN.&lt;/p&gt;

&lt;p&gt;We reject this exposure. By explicitly prepending &lt;code&gt;127.0.0.1&lt;/code&gt;, we force Docker to bind the port &lt;strong&gt;only&lt;/strong&gt; to the host's loopback interface.&lt;/p&gt;

&lt;p&gt;This creates a "Poor Man's Firewall."&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;The Host:&lt;/strong&gt; You can access the UI via &lt;code&gt;localhost:9000&lt;/code&gt; (or &lt;code&gt;sonarqube.cicd.local&lt;/code&gt; via your hosts file) because you are on the machine.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The City:&lt;/strong&gt; Jenkins and GitLab can access it because they share the private &lt;code&gt;cicd-net&lt;/code&gt; bridge network.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The World:&lt;/strong&gt; Anyone else on your network sees a closed port.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This mimics the "Air Gapped" architecture of high-security environments where internal tools are never exposed directly to the user network without an intervening Reverse Proxy or VPN.&lt;/p&gt;
&lt;h2&gt;
  
  
  4.3 The "Zombie State" Smoke Test (&lt;code&gt;04-verify-sonarqube.py&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;Launching the container is not the same as starting the service. Because SonarQube is a distributed system (Web + Compute + Search), it has a complex startup sequence that can take several minutes.&lt;/p&gt;

&lt;p&gt;During this initialization phase, the Web Server port (9000) might accept TCP connections, but the application is not ready. It enters a "Zombie State" where it serves a "Maintenance Mode" page or simply hangs while waiting for Elasticsearch to index the database.&lt;/p&gt;

&lt;p&gt;If we try to script against it too early, our automation will fail. A simple &lt;code&gt;curl&lt;/code&gt; or &lt;code&gt;nc -z&lt;/code&gt; check is insufficient because it only checks the TCP layer, not the Application layer.&lt;/p&gt;

&lt;p&gt;We need a "Smoke Test" that verifies the &lt;strong&gt;System Status&lt;/strong&gt;, ensuring that all three internal engines are green. We will write a Python script to poll the &lt;code&gt;api/system/status&lt;/code&gt; endpoint from &lt;em&gt;inside&lt;/em&gt; our control center.&lt;/p&gt;

&lt;p&gt;Create this file at &lt;code&gt;~/Documents/FromFirstPrinciples/articles/0010_cicd_part06_sonarqube/04-verify-sonarqube.py&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env python3
&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;urllib.request&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;urllib.error&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;

&lt;span class="c1"&gt;# --- Configuration ---
# We verify from the perspective of the 'dev-container'
# using the internal Docker DNS name.
&lt;/span&gt;&lt;span class="n"&gt;TARGET_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://sonarqube.cicd.local:9000/api/system/status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;MAX_RETRIES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;
&lt;span class="n"&gt;WAIT_SECONDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;verify_sonarqube&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--- Starting SonarQube Verification ---&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Target: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;TARGET_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MAX_RETRIES&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Attempt &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;MAX_RETRIES&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flush&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="c1"&gt;# Make the request
&lt;/span&gt;            &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TARGET_URL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;FAILED (HTTP &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="k"&gt;continue&lt;/span&gt;

                &lt;span class="c1"&gt;# Parse JSON
&lt;/span&gt;                &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
                &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CONNECTED&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;    System Status: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;UP&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;✅ SUCCESS: SonarQube is fully operational.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
                &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;STARTING&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;⏳ WAITING: SonarQube is still initializing (Elasticsearch loading)...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DB_MIGRATION_NEEDED&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;⚠️  WARNING: Database migration required.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
                &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;⚠️  Unknown Status: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;

        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;URLError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;FAILED (&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;    (Check if &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sonarqube.cicd.local&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; resolves or if the container is running)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ERROR: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;MAX_RETRIES&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;WAIT_SECONDS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;❌ FAILURE: Could not verify SonarQube status after multiple attempts.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;verify_sonarqube&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deconstructing the Smoke Test
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. The Target (&lt;code&gt;sonarqube.cicd.local&lt;/code&gt;)&lt;/strong&gt;&lt;br&gt;
We run this script from the &lt;code&gt;dev-container&lt;/code&gt;. It uses the internal Docker DNS name. This validates that our &lt;code&gt;cicd-net&lt;/code&gt; is functioning and that the container is reachable on its internal port 9000.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The Logic (&lt;code&gt;api/system/status&lt;/code&gt;)&lt;/strong&gt;&lt;br&gt;
We do not scrape HTML. We hit the official status API.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;STARTING&lt;/code&gt;&lt;/strong&gt;: The web server is up, but Elasticsearch is still loading indices from disk. The script knows to wait and retry.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;UP&lt;/code&gt;&lt;/strong&gt;: All child processes are healthy, and the database is connected. Only &lt;em&gt;now&lt;/em&gt; is the system ready for login.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Zero Dependencies&lt;/strong&gt;&lt;br&gt;
We intentionally use Python's built-in &lt;code&gt;urllib&lt;/code&gt; instead of &lt;code&gt;requests&lt;/code&gt;. This ensures the script runs immediately inside our &lt;code&gt;dev-container&lt;/code&gt; (or any minimal Python environment) without requiring a &lt;code&gt;pip install&lt;/code&gt; step.&lt;/p&gt;

&lt;p&gt;To run this verification:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Enter your control center: &lt;code&gt;./dev-container.sh&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt; Run the script: &lt;code&gt;python3 articles/0010_cicd_part06_sonarqube/04-verify-sonarqube.py&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  4.4 Verification: The "First Login"
&lt;/h2&gt;

&lt;p&gt;Once the smoke test returns &lt;code&gt;✅ SUCCESS&lt;/code&gt;, we know the application is physically running. However, we must now verify that it is accessible from our &lt;strong&gt;Host Machine&lt;/strong&gt; in a way that respects our networking architecture.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Article 7&lt;/strong&gt;, we established a pattern of using "Split-Horizon DNS" via the &lt;code&gt;/etc/hosts&lt;/code&gt; file. We mapped &lt;code&gt;gitlab.cicd.local&lt;/code&gt; to &lt;code&gt;127.0.0.1&lt;/code&gt; so our browser would send the correct &lt;code&gt;Host&lt;/code&gt; header, preventing CORS issues and redirection loops. We must apply the same rigor here.&lt;/p&gt;

&lt;p&gt;Although we &lt;em&gt;could&lt;/em&gt; access the server via &lt;code&gt;http://localhost:9000&lt;/code&gt;, doing so creates an inconsistency between how &lt;em&gt;we&lt;/em&gt; see the server and how &lt;em&gt;Jenkins&lt;/em&gt; sees the server (as &lt;code&gt;sonarqube.cicd.local&lt;/code&gt;). To align these perspectives, we update our host's DNS resolution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Update Hosts File (Host Machine):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/hosts
&lt;span class="c"&gt;# Add the following line (or append to existing):&lt;/span&gt;
127.0.0.1   sonarqube.cicd.local
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Access the UI:&lt;/strong&gt;&lt;br&gt;
Open your web browser to &lt;strong&gt;&lt;code&gt;http://sonarqube.cicd.local:9000&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You should be greeted by the login screen. Because we are running the Community Build, you will notice the connection is &lt;strong&gt;Not Secure&lt;/strong&gt; (HTTP). This is expected and acceptable &lt;em&gt;only&lt;/em&gt; because we explicitly bound the port to &lt;code&gt;127.0.0.1&lt;/code&gt; in our Launcher script. We are connecting over the loopback interface, which is considered a trusted path in our "Air Gapped" simulation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The Credential Handshake:&lt;/strong&gt;&lt;br&gt;
Log in with the factory default credentials:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Login:&lt;/strong&gt; &lt;code&gt;admin&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Password:&lt;/strong&gt; &lt;code&gt;admin&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Upon the first successful authentication, SonarQube will force you to update the password. Choose a strong password.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Critical Step: Record the Secret&lt;/strong&gt;&lt;br&gt;
Since we did not generate this password programmatically, it is not currently stored in our secrets file. To prevent locking yourself out of your own city, manually add this new password to your master environment file now.&lt;/p&gt;

&lt;p&gt;Edit &lt;code&gt;~/cicd_stack/cicd.env&lt;/code&gt; and add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;SONARQUBE_ADMIN_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;your_new_password&amp;gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you see the empty "Projects" dashboard, the deployment is a success. We have a running Inspector, backed by a tuned kernel, secure persistence, and a trusted communication channel. Now, we must connect it to the rest of the city.&lt;/p&gt;

&lt;h1&gt;
  
  
  Chapter 5: The Bridge - Bidirectional Identity
&lt;/h1&gt;

&lt;h2&gt;
  
  
  5.1 The "Handshake" Protocol
&lt;/h2&gt;

&lt;p&gt;We have a running Factory (Jenkins) and a running Inspector (SonarQube). They share the same network (&lt;code&gt;cicd-net&lt;/code&gt;) and the same trust root (Local CA), but they are currently strangers. To establish a functioning Quality Gate, we must build a bridge between them.&lt;/p&gt;

&lt;p&gt;This bridge is not a simple, one-way road. It is a &lt;strong&gt;bidirectional handshake&lt;/strong&gt; necessitated by the asynchronous architecture of static analysis.&lt;/p&gt;

&lt;p&gt;When a build runs, the interaction happens in two distinct phases:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Outbound Push (Jenkins $\rightarrow$ SonarQube):&lt;/strong&gt;&lt;br&gt;
The Jenkins Agent runs the &lt;code&gt;sonar-scanner&lt;/code&gt;. This tool scans the code, packages the raw data, and uploads it to the SonarQube server. To do this securely, the "Factory" needs a key to the "Inspector's" office. We cannot use a username and password here; we need a revocable, scoped &lt;strong&gt;User Token&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Asynchronous Gap:&lt;/strong&gt;&lt;br&gt;
Once the scanner uploads the report, its job is done. It disconnects. However, the analysis is &lt;em&gt;not&lt;/em&gt; complete. The SonarQube &lt;strong&gt;Compute Engine&lt;/strong&gt; takes over, processing the report in the background. This can take anywhere from a few seconds to several minutes depending on the project size.&lt;br&gt;
During this gap, Jenkins is blind. It doesn't know if the project passed or failed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Inbound Callback (SonarQube $\rightarrow$ Jenkins):&lt;/strong&gt;&lt;br&gt;
When the Compute Engine finishes, it calculates the Quality Gate status (Green or Red). It must then pick up the "Red Phone" and call the Factory back to report the result. This requires a &lt;strong&gt;Webhook&lt;/strong&gt;. SonarQube becomes the client, sending an HTTPS POST request to Jenkins to wake up the paused pipeline.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We must configure both sides of this relationship. If we miss the Token, the scan fails. If we miss the Webhook, the pipeline hangs forever, waiting for a call that never comes.&lt;/p&gt;

&lt;h2&gt;
  
  
  5.2 Identity Creation (UI &amp;amp; Secrets)
&lt;/h2&gt;

&lt;p&gt;We begin by creating the credentials for the &lt;strong&gt;Outbound&lt;/strong&gt; connection. Jenkins needs a key to access the SonarQube API.&lt;/p&gt;

&lt;p&gt;In a fully mature "Configuration as Code" setup, we might provision this using a bootstrap script against the SonarQube API. However, because this is the "First Run" of our Inspector, we face a bootstrapping paradox: we need an admin token to use the API to create tokens. To resolve this, we will perform this specific setup manually in the UI, treating it as a one-time "City Key" generation event.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Generate the Jenkins Token (SonarQube UI):&lt;/strong&gt;&lt;br&gt;
Log in to SonarQube as &lt;code&gt;admin&lt;/code&gt;. Navigate to &lt;strong&gt;User Profile&lt;/strong&gt; (top right) &amp;gt; &lt;strong&gt;My Account&lt;/strong&gt; &amp;gt; &lt;strong&gt;Security&lt;/strong&gt;.&lt;br&gt;
Generate a new token with the following attributes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;jenkins-admin-token&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type:&lt;/strong&gt; &lt;strong&gt;User Token&lt;/strong&gt;. We choose this over a "Project Analysis Token" because Jenkins acts as a global orchestrator. It needs permissions to create projects, trigger scans, and configure webhooks across the entire system.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expiration:&lt;/strong&gt; For this lab, "No expiration." In a real enterprise environment, you would establish a rotation policy here.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Secure the Token (Host):&lt;/strong&gt;&lt;br&gt;
Copy the token immediately. We must now persist this secret in our "Control Center" so our automation scripts can access it.&lt;br&gt;
Open your master secrets file &lt;code&gt;~/cicd_stack/cicd.env&lt;/code&gt; and add the token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;SONAR_ADMIN_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;paste_your_token_here&amp;gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Configure the Library Link (GitLab ALM):&lt;/strong&gt;&lt;br&gt;
While we are in the UI, we must also introduce the Inspector to the Library. This allows SonarQube to decorate Pull Requests and import repositories.&lt;br&gt;
Navigate to &lt;strong&gt;Administration&lt;/strong&gt; &amp;gt; &lt;strong&gt;Configuration&lt;/strong&gt; &amp;gt; &lt;strong&gt;General Settings&lt;/strong&gt; &amp;gt; &lt;strong&gt;DevOps Platform Integrations&lt;/strong&gt; &amp;gt; &lt;strong&gt;GitLab&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Configuration Name:&lt;/strong&gt; &lt;code&gt;gitlab-cicd&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitLab API URL:&lt;/strong&gt; &lt;code&gt;https://gitlab.cicd.local:10300/api/v4&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Personal Access Token:&lt;/strong&gt; Use the &lt;code&gt;GITLAB_API_TOKEN&lt;/code&gt; you generated in Article 7.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note the URL. We are using the internal &lt;strong&gt;HTTPS&lt;/strong&gt; address. This connection will only succeed because we built our Custom Image in Chapter 3. If we were using the vanilla image, saving this configuration would trigger a certificate error.&lt;/p&gt;
&lt;h2&gt;
  
  
  5.3 The "Configuration Helper" (&lt;code&gt;update_jcasc_sonar.py&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;With the credentials secured, we face the integration challenge on the Jenkins side. We need to configure the &lt;strong&gt;SonarQube Scanner&lt;/strong&gt; plugin.&lt;/p&gt;

&lt;p&gt;In a manual setup, you would click through "Manage Jenkins" menus. In our "Configuration as Code" (JCasC) architecture, we must define this in &lt;code&gt;jenkins.yaml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;However, the JCasC schema for plugins is notoriously brittle. The documentation is often sparse, and incorrect indentation or nesting will cause Jenkins to crash on boot. To solve this safely, we use our &lt;strong&gt;Python Helper&lt;/strong&gt; pattern. Instead of using &lt;code&gt;sed&lt;/code&gt; or &lt;code&gt;cat&lt;/code&gt; to hack text into the YAML file, we treat the configuration as a structured data object.&lt;/p&gt;

&lt;p&gt;We write a script that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Parses&lt;/strong&gt; the existing &lt;code&gt;jenkins.yaml&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Injects&lt;/strong&gt; the &lt;code&gt;sonar-admin-token&lt;/code&gt; credential into the global credentials block.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Injects&lt;/strong&gt; the &lt;code&gt;sonarGlobalConfiguration&lt;/code&gt; block under the &lt;code&gt;unclassified&lt;/code&gt; root key.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Writes&lt;/strong&gt; the valid YAML back to disk.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Create this file at &lt;code&gt;~/cicd_stack/jenkins/config/update_jcasc_sonar.py&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env python3
&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;yaml&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="c1"&gt;# Target the LIVE configuration in the cicd_stack
&lt;/span&gt;&lt;span class="n"&gt;JCAS_FILE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expanduser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;~/cicd_stack/jenkins/config/jenkins.yaml&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update_jcasc&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[INFO] Reading JCasC file: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;JCAS_FILE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JCAS_FILE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;jcasc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;yaml&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;safe_load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;FileNotFoundError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[ERROR] File not found: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;JCAS_FILE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 1. Add SonarQube Admin Token Credential
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[INFO] Injecting SonarQube Admin Token credential...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;credentials&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;jcasc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;jcasc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;credentials&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;system&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;domainCredentials&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;credentials&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]}]}}&lt;/span&gt;

    &lt;span class="n"&gt;sonar_cred&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;string&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sonar-admin-token&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;scope&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;GLOBAL&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;description&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;SonarQube Admin Token&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;secret&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;${SONAR_AUTH_TOKEN}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;# Safe navigation to credentials list
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;system&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;jcasc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;credentials&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="n"&gt;jcasc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;credentials&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;system&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;domainCredentials&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;credentials&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]}]}&lt;/span&gt;

    &lt;span class="n"&gt;domain_creds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jcasc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;credentials&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;system&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;domainCredentials&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;domain_creds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;domain_creds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;credentials&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]})&lt;/span&gt;

    &lt;span class="n"&gt;creds_list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;domain_creds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;credentials&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;creds_list&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;creds_list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="n"&gt;domain_creds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;credentials&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;creds_list&lt;/span&gt;

    &lt;span class="c1"&gt;# Idempotency Check
&lt;/span&gt;    &lt;span class="n"&gt;exists&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cred&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;creds_list&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;string&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cred&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;cred&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;string&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sonar-admin-token&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;exists&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;creds_list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sonar_cred&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[INFO] Credential &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sonar-admin-token&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; added.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[INFO] Credential &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sonar-admin-token&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; already exists. Skipping.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 2. Add SonarQube Global Configuration
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[INFO] Injecting SonarQube Server configuration...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;unclassified&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;jcasc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;jcasc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;unclassified&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="c1"&gt;# The 'sonarGlobalConfiguration' block configures the SonarScanner plugin
&lt;/span&gt;    &lt;span class="n"&gt;jcasc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;unclassified&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sonarGlobalConfiguration&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;buildWrapperEnabled&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;installations&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;SonarQube&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;serverUrl&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;http://sonarqube.cicd.local:9000&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;credentialsId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sonar-admin-token&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;webhookSecretId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;''&lt;/span&gt; &lt;span class="c1"&gt;# Optional: We will configure webhooks later
&lt;/span&gt;        &lt;span class="p"&gt;}]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;# 3. Write back to file
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[INFO] Writing updated JCasC file...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JCAS_FILE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;w&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;yaml&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jcasc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default_flow_style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sort_keys&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[INFO] JCasC update complete.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;update_jcasc&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deconstructing the Helper
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;credentialsId: 'sonar-admin-token'&lt;/code&gt;&lt;/strong&gt;: This links the server configuration to the credential we just injected.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;serverUrl: 'http://sonarqube.cicd.local:9000'&lt;/code&gt;&lt;/strong&gt;: We use the internal Docker DNS name. Note that we use &lt;strong&gt;HTTP&lt;/strong&gt; here, because we deliberately bound SonarQube to port 9000 without an SSL proxy for simplicity. Jenkins communicates over the private bridge network, so this traffic is isolated from the physical LAN.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;${SONAR_AUTH_TOKEN}&lt;/code&gt;&lt;/strong&gt;: We do not hardcode the token in the YAML. We use a variable placeholder, which Jenkins will resolve at runtime from its environment variables. This is the next step in our integration.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5.4 The "Integrator" Script (&lt;code&gt;05-update-jenkins.sh&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;We now have the configuration helper logic defined, but we need a mechanism to execute it safely. We cannot simply run the Python script and hope for the best; we must ensure the environment variables it references (&lt;code&gt;${SONAR_AUTH_TOKEN}&lt;/code&gt;) actually exist in the container's runtime.&lt;/p&gt;

&lt;p&gt;This requires an orchestration script that bridges the gap between our Host credentials and the Container's environment.&lt;/p&gt;

&lt;p&gt;Create this file at &lt;code&gt;~/cicd_stack/sonarqube/05-update-jenkins.sh&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;#               05-update-jenkins.sh&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  This script integrates Jenkins with SonarQube.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  1. Secrets: Reads SONAR_ADMIN_TOKEN (host) -&amp;gt; Injects SONAR_AUTH_TOKEN (jenkins.env).&lt;/span&gt;
&lt;span class="c"&gt;#  2. JCasC:   Runs local python helper to patch jenkins.yaml in cicd_stack.&lt;/span&gt;
&lt;span class="c"&gt;#  3. Apply:   Triggers Jenkins redeployment from the Jenkins article dir.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="c"&gt;# --- Paths ---&lt;/span&gt;
&lt;span class="nv"&gt;CICD_ROOT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/cicd_stack"&lt;/span&gt;
&lt;span class="c"&gt;# The module where the Jenkins deployment logic lives&lt;/span&gt;
&lt;span class="nv"&gt;JENKINS_MODULE_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/Documents/FromFirstPrinciples/articles/0008_cicd_part04_jenkins"&lt;/span&gt;
&lt;span class="nv"&gt;JENKINS_ENV_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JENKINS_MODULE_DIR&lt;/span&gt;&lt;span class="s2"&gt;/jenkins.env"&lt;/span&gt;
&lt;span class="nv"&gt;DEPLOY_SCRIPT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JENKINS_MODULE_DIR&lt;/span&gt;&lt;span class="s2"&gt;/03-deploy-controller.sh"&lt;/span&gt;

&lt;span class="c"&gt;# Local python helper&lt;/span&gt;
&lt;span class="nv"&gt;PY_HELPER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"./update_jcasc_sonar.py"&lt;/span&gt;
&lt;span class="nv"&gt;MASTER_ENV&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CICD_ROOT&lt;/span&gt;&lt;span class="s2"&gt;/cicd.env"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Starting Jenkins &amp;lt;-&amp;gt; SonarQube Integration..."&lt;/span&gt;

&lt;span class="c"&gt;# --- 1. Secret Injection ---&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: Master environment file not found: &lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Load SONAR_ADMIN_TOKEN&lt;/span&gt;
&lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SONAR_ADMIN_TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: SONAR_ADMIN_TOKEN not found in cicd.env."&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"       Please generate a User Token in SonarQube (My Account &amp;gt; Security)"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"       and save it to ~/cicd_stack/cicd.env"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JENKINS_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: Jenkins env file not found at: &lt;/span&gt;&lt;span class="nv"&gt;$JENKINS_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Injecting SonarQube secrets into jenkins.env..."&lt;/span&gt;

&lt;span class="c"&gt;# Idempotency check using grep to prevent duplicate entries&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s2"&gt;"SONAR_AUTH_TOKEN"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JENKINS_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt;&amp;gt; "&lt;/span&gt;&lt;span class="nv"&gt;$JENKINS_ENV_FILE&lt;/span&gt;&lt;span class="sh"&gt;"

# --- SonarQube Integration ---
SONAR_AUTH_TOKEN=&lt;/span&gt;&lt;span class="nv"&gt;$SONAR_ADMIN_TOKEN&lt;/span&gt;&lt;span class="sh"&gt;
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Secrets injected."&lt;/span&gt;
&lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Secrets already present."&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# --- 2. Update JCasC ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Updating JCasC configuration..."&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PY_HELPER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: Python helper script not found at &lt;/span&gt;&lt;span class="nv"&gt;$PY_HELPER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

&lt;/span&gt;python3 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PY_HELPER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# --- 3. Re-Deploy Jenkins ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Triggering Jenkins Re-deployment (Container Recreate)..."&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-x&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DEPLOY_SCRIPT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: Deploy script not found or not executable: &lt;/span&gt;&lt;span class="nv"&gt;$DEPLOY_SCRIPT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Execute the deploy script from its own directory context&lt;/span&gt;
&lt;span class="c"&gt;# This ensures it finds the Dockerfile and other assets correctly&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JENKINS_MODULE_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; ./03-deploy-controller.sh&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Integration update complete."&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Jenkins is restarting. Wait for initialization."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deconstructing the Integrator
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. The Token Handshake (Step 1)&lt;/strong&gt;&lt;br&gt;
This section solves the secret management problem. We read the &lt;code&gt;SONAR_ADMIN_TOKEN&lt;/code&gt; from our master &lt;code&gt;cicd.env&lt;/code&gt; file (where we manually saved it) and inject it into the specific &lt;code&gt;jenkins.env&lt;/code&gt; file that the Jenkins container consumes. Note the variable renaming: we map &lt;code&gt;SONAR_ADMIN_TOKEN&lt;/code&gt; (Host Name) to &lt;code&gt;SONAR_AUTH_TOKEN&lt;/code&gt; (Container Name). This matches the &lt;code&gt;${SONAR_AUTH_TOKEN}&lt;/code&gt; placeholder we wrote in our JCasC file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The Directory Context Switch (Step 3)&lt;/strong&gt;&lt;br&gt;
This is a subtle but critical piece of shell scripting. The Jenkins deployment script (&lt;code&gt;03-deploy-controller.sh&lt;/code&gt;) expects to be run from its own directory (because it references relative paths like &lt;code&gt;Dockerfile&lt;/code&gt;). We use a subshell &lt;code&gt;(cd ... &amp;amp;&amp;amp; ./...)&lt;/code&gt; to temporarily switch contexts, execute the deployment logic we wrote in &lt;strong&gt;Article 8&lt;/strong&gt;, and return. This allows us to trigger a rebuild of the "Factory" from the "Inspector's" directory, maintaining loose coupling between our modules.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The "Re-Deploy" Philosophy&lt;/strong&gt;&lt;br&gt;
We do not use &lt;code&gt;docker restart&lt;/code&gt;. Simply restarting a container does &lt;em&gt;not&lt;/em&gt; reload environment variables from the host file. By triggering the full deployment script (which performs &lt;code&gt;docker stop&lt;/code&gt; / &lt;code&gt;docker rm&lt;/code&gt; / &lt;code&gt;docker run&lt;/code&gt;), we force Docker to read the updated &lt;code&gt;jenkins.env&lt;/code&gt; file and inject the new token into the fresh container instance.&lt;/p&gt;
&lt;h2&gt;
  
  
  5.5 The Return Path (Webhook Setup)
&lt;/h2&gt;

&lt;p&gt;We have successfully handed the "Foreman" (Jenkins) the keys to the "Inspector's" office. Jenkins can now authenticate with SonarQube and upload analysis reports.&lt;/p&gt;

&lt;p&gt;However, the conversation is currently one-sided. When SonarQube finishes analyzing a report—a process that can take minutes for a large codebase—it has no way to tell Jenkins the result. Without this return signal, our pipeline’s &lt;code&gt;waitForQualityGate&lt;/code&gt; step will simply hang until it times out, failing the build regardless of the code quality.&lt;/p&gt;

&lt;p&gt;We must configure the &lt;strong&gt;Inbound&lt;/strong&gt; connection: the &lt;strong&gt;Webhook&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Because this is a configuration setting inside the SonarQube application (application logic), not the server infrastructure, we must configure it via the UI (or API). We cannot configure this using Jenkins JCasC.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Action: Configure the Webhook (SonarQube UI)&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Log in to SonarQube (&lt;code&gt;http://sonarqube.cicd.local:9000&lt;/code&gt;) as &lt;code&gt;admin&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; Navigate to &lt;strong&gt;Administration&lt;/strong&gt; &amp;gt; &lt;strong&gt;Configuration&lt;/strong&gt; &amp;gt; &lt;strong&gt;Webhooks&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; Click the &lt;strong&gt;Create&lt;/strong&gt; button.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Name:&lt;/strong&gt; Enter &lt;code&gt;jenkins-webhook&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;URL:&lt;/strong&gt; Enter &lt;code&gt;https://jenkins.cicd.local:10400/sonarqube-webhook/&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Critical Architectural Details:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Protocol (&lt;code&gt;https://&lt;/code&gt;):&lt;/strong&gt; We are strictly using HTTPS. Jenkins is configured to reject insecure HTTP connections on its primary port. This connection is only possible because we built our &lt;strong&gt;Custom SonarQube Image&lt;/strong&gt; in Chapter 3. If we were using the vanilla image, SonarQube would reject Jenkins' SSL certificate, and the webhook would fail silently.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Address (&lt;code&gt;jenkins.cicd.local&lt;/code&gt;):&lt;/strong&gt; We are using the internal Docker DNS name. This traffic never leaves our &lt;code&gt;cicd-net&lt;/code&gt; bridge network; it travels directly from container to container, completely isolated from the host network.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Endpoint (&lt;code&gt;/sonarqube-webhook/&lt;/code&gt;):&lt;/strong&gt; This specific endpoint is exposed by the &lt;strong&gt;SonarQube Scanner for Jenkins&lt;/strong&gt; plugin we installed in Article 8. It listens specifically for the JSON payload that SonarQube sends when a background task completes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Create&lt;/strong&gt;. The bridge is now complete. Traffic can flow from Factory to Inspector and back again. We are ready to test the flow.&lt;/p&gt;
&lt;h1&gt;
  
  
  Chapter 6: The Toolchain Crisis - When Versions Collide
&lt;/h1&gt;
&lt;h2&gt;
  
  
  6.1 The Incident (The "Green Build" Breaks)
&lt;/h2&gt;

&lt;p&gt;With our connectivity established and our project created in SonarQube, we were ready to attempt our first full "Quality Gate" build. We needed to transform our pipeline from a simple builder into an analytical engine.&lt;/p&gt;

&lt;p&gt;To do this, we first had to adapt our coverage generation logic. Our existing &lt;code&gt;run-coverage.sh&lt;/code&gt; was designed for humans, generating graphical HTML reports. SonarQube, however, is a machine; it requires structured data formats like &lt;strong&gt;LCOV&lt;/strong&gt; (for C/C++ and Rust) and &lt;strong&gt;Cobertura XML&lt;/strong&gt; (for Python).&lt;/p&gt;

&lt;p&gt;We created a dedicated CI script, &lt;code&gt;run-coverage-cicd.sh&lt;/code&gt;, to generate these specific artifacts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# run-coverage-cicd.sh&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="c"&gt;# --- 1. C/C++ Coverage (LCOV) ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Running C/C++ Tests &amp;amp; Coverage ---"&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; build_debug
    &lt;span class="nb"&gt;cd &lt;/span&gt;build_debug &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;exit&lt;/span&gt;

    &lt;span class="c"&gt;# Ensure Debug build for coverage flags&lt;/span&gt;
    cmake &lt;span class="nt"&gt;-DCMAKE_BUILD_TYPE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Debug ..
    cmake &lt;span class="nt"&gt;--build&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;-j&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;nproc&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# Reset counters&lt;/span&gt;
    lcov &lt;span class="nt"&gt;--directory&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--zerocounters&lt;/span&gt;

    &lt;span class="c"&gt;# Run Tests&lt;/span&gt;
    ctest &lt;span class="nt"&gt;--output-on-failure&lt;/span&gt;

    &lt;span class="c"&gt;# Capture Coverage&lt;/span&gt;
    lcov &lt;span class="nt"&gt;--capture&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
         &lt;span class="nt"&gt;--directory&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
         &lt;span class="nt"&gt;--output-file&lt;/span&gt; coverage.info

    &lt;span class="c"&gt;# Filter Artifacts&lt;/span&gt;
    lcov &lt;span class="nt"&gt;--remove&lt;/span&gt; coverage.info &lt;span class="se"&gt;\&lt;/span&gt;
         &lt;span class="s1"&gt;'/usr/*'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
         &lt;span class="s1"&gt;'*/_deps/*'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
         &lt;span class="s1"&gt;'*/tests/helpers.h'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
         &lt;span class="s1"&gt;'*/benchmark/*'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
         &lt;span class="s1"&gt;'*/apps/*'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
         &lt;span class="s1"&gt;'*/docs/*'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
         &lt;span class="s1"&gt;'*/cmake/*'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
         &lt;span class="s1"&gt;'*/.cache/*'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
         &lt;span class="nt"&gt;-o&lt;/span&gt; coverage.filtered.info

    &lt;span class="c"&gt;# Move to root for scanner pickup&lt;/span&gt;
    &lt;span class="nb"&gt;mv &lt;/span&gt;coverage.filtered.info ../coverage.cxx.info
&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# ... (Rust and Python sections omitted for brevity) ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we updated our &lt;code&gt;Jenkinsfile&lt;/code&gt;. We replaced the old coverage script with our new CI-specific version and added the &lt;strong&gt;Code Analysis&lt;/strong&gt; stage. Crucially, we wrapped the scanner in the &lt;code&gt;withSonarQubeEnv&lt;/code&gt; block to inject our credentials and added the &lt;code&gt;waitForQualityGate&lt;/code&gt; step to enforce the "Stop the Line" logic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Test &amp;amp; Coverage'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'--- Running Tests &amp;amp; Generating Reports ---'&lt;/span&gt;
                &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'chmod +x ./run-coverage-cicd.sh'&lt;/span&gt;
                &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'./run-coverage-cicd.sh'&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Code Analysis'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// Inject SONAR_HOST_URL and SONAR_AUTH_TOKEN&lt;/span&gt;
                &lt;span class="n"&gt;withSonarQubeEnv&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'SonarQube'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'sonar-scanner'&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;

                &lt;span class="c1"&gt;// Pause pipeline and wait for the Quality Gate webhook&lt;/span&gt;
                &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;time:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;unit:&lt;/span&gt; &lt;span class="s1"&gt;'MINUTES'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;waitForQualityGate&lt;/span&gt; &lt;span class="nl"&gt;abortPipeline:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We pushed this configuration to GitLab, expecting the pipeline to turn green and populate our SonarQube dashboard with rich metrics.&lt;/p&gt;

&lt;p&gt;Instead, the build crashed.&lt;/p&gt;

&lt;h2&gt;
  
  
  6.2 Forensics: CMake vs. LCOV
&lt;/h2&gt;

&lt;p&gt;The Jenkins console output painted a very clear, albeit cryptic, picture of the disaster. Hidden among the standard build noise was this fatal error message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;lcov: ERROR: (version) Incompatible GCC/GCOV version found while processing ...
    Your test was built with 'B42*'.
    You are trying to capture with gcov tool '/usr/local/bin/gcov' which is version 'B52*'.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To a systems programmer, this message is a "smoking gun." The GCOV data format is tightly coupled to the compiler version. The codes &lt;strong&gt;&lt;code&gt;B42&lt;/code&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;code&gt;B52&lt;/code&gt;&lt;/strong&gt; are internal version identifiers for the GCOV format:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;B42&lt;/code&gt; corresponds to GCC 12.&lt;/strong&gt; This is the default system compiler shipped with Debian 12.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;B52&lt;/code&gt; corresponds to GCC 15.&lt;/strong&gt; This is the bleeding-edge compiler we manually compiled and installed in &lt;strong&gt;Article 8&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This error revealed a critical &lt;strong&gt;"Split-Brain"&lt;/strong&gt; condition in our Factory Worker.&lt;/p&gt;

&lt;p&gt;When the pipeline ran, two different tools made two different decisions about which compiler to use, resulting in a binary incompatibility:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;The Builder (CMake):&lt;/strong&gt; When we ran &lt;code&gt;cmake ..&lt;/code&gt;, it scanned the system for a C++ compiler. Because we had not explicitly told it otherwise, it looked for the standard &lt;code&gt;/usr/bin/c++&lt;/code&gt; executable. On Debian, this is a symlink to the system's default &lt;strong&gt;GCC 12&lt;/strong&gt;. It built our binaries using the old compiler.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The Inspector (LCOV):&lt;/strong&gt; When we ran &lt;code&gt;lcov&lt;/code&gt;, it needed to use the &lt;code&gt;gcov&lt;/code&gt; utility to read the coverage files. It searched the system &lt;code&gt;PATH&lt;/code&gt;. Because we had added &lt;code&gt;/usr/local/bin&lt;/code&gt; to the start of the &lt;code&gt;PATH&lt;/code&gt; in our Dockerfile, it found our custom &lt;strong&gt;GCC 15&lt;/strong&gt; &lt;code&gt;gcov&lt;/code&gt; executable first.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The result was a pipeline that built code with one version (v12) and tried to analyze it with another (v15). Since the binary format for coverage artifacts (&lt;code&gt;.gcno&lt;/code&gt;) changes between major GCC versions, the analyzer crashed immediately.&lt;/p&gt;

&lt;p&gt;We had spent hours compiling a custom toolchain to avoid "it works on my machine" issues, yet our automated agent had silently drifted back to system defaults for compilation. To fix this, we had to understand &lt;em&gt;why&lt;/em&gt; the agent behaved differently than our &lt;code&gt;dev-container&lt;/code&gt;, where this setup worked perfectly.&lt;/p&gt;

&lt;h2&gt;
  
  
  6.3 The "Drift": Interactive vs. Non-Interactive Shells
&lt;/h2&gt;

&lt;p&gt;The key to this mystery lay in a subtle distinction between how we use Docker as humans versus how Jenkins uses it as a machine.&lt;/p&gt;

&lt;p&gt;When we, as developers, log into our &lt;code&gt;dev-container&lt;/code&gt;, we typically start a &lt;code&gt;bash&lt;/code&gt; shell. This is an &lt;strong&gt;Interactive Shell&lt;/strong&gt;. When it starts, it reads configuration files like &lt;code&gt;~/.bashrc&lt;/code&gt; to set up the user's environment. In &lt;strong&gt;Article 1&lt;/strong&gt;, we added lines to &lt;code&gt;.bashrc&lt;/code&gt; to export &lt;code&gt;CC=/usr/local/bin/gcc&lt;/code&gt; and &lt;code&gt;CXX=/usr/local/bin/g++&lt;/code&gt;. This ensured that whenever we typed &lt;code&gt;cmake&lt;/code&gt;, our custom compiler was active.&lt;/p&gt;

&lt;p&gt;Jenkins, however, does not log in like a human. When the Jenkins Agent connects to the controller and executes a pipeline step (&lt;code&gt;sh './run-coverage-cicd.sh'&lt;/code&gt;), it runs a &lt;strong&gt;Non-Interactive, Non-Login Shell&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In this mode, &lt;strong&gt;&lt;code&gt;.bashrc&lt;/code&gt; is ignored&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Because the environment variables were defined only in a user configuration file and not at the system level, the Jenkins agent reverted to the default behavior. It ignored our custom compiler settings, found the system default &lt;code&gt;/usr/bin/c++&lt;/code&gt;, and built the project with GCC 12. Meanwhile, our &lt;code&gt;lcov&lt;/code&gt; tool—installed globally in &lt;code&gt;/usr/local/bin&lt;/code&gt;—remained at version 15.&lt;/p&gt;

&lt;p&gt;This "Environmental Drift" is a classic failure mode in CI/CD. It highlights why relying on user-level dotfiles (&lt;code&gt;.bashrc&lt;/code&gt;, &lt;code&gt;.profile&lt;/code&gt;) is dangerous for automation. To fix this, we must move our configuration from the "User Layer" to the "Image Layer."&lt;/p&gt;

&lt;h2&gt;
  
  
  6.4 The Fix: Immutable Environment Variables
&lt;/h2&gt;

&lt;p&gt;To solve this permanently, we must bake our toolchain selection directly into the Docker image metadata, ensuring it applies to &lt;em&gt;every&lt;/em&gt; process, regardless of how it is spawned.&lt;/p&gt;

&lt;p&gt;We will return to &lt;strong&gt;Article 8&lt;/strong&gt; and patch our &lt;code&gt;Dockerfile.agent&lt;/code&gt;. We will use the &lt;code&gt;ENV&lt;/code&gt; instruction to set the &lt;code&gt;CC&lt;/code&gt;, &lt;code&gt;CXX&lt;/code&gt;, and &lt;code&gt;LD_LIBRARY_PATH&lt;/code&gt; variables. Unlike &lt;code&gt;RUN export ...&lt;/code&gt;, which only affects the current build step, &lt;code&gt;ENV&lt;/code&gt; variables persist in the final image.&lt;/p&gt;

&lt;p&gt;We will inject these instructions &lt;em&gt;after&lt;/em&gt; our heavy compilation steps (GCC and Python) but &lt;em&gt;before&lt;/em&gt; the final setup. This preserves our Docker build cache, saving us from recompiling GCC 15 (which takes ~30 minutes).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;File Location:&lt;/strong&gt; &lt;code&gt;~/Documents/FromFirstPrinciples/articles/0008_cicd_part04_jenkins/Dockerfile.agent&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Insert this block after the Python build loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# ... (End of Step 8: Build Python) ...&lt;/span&gt;

&lt;span class="c"&gt;# 8.5. Force CMake to use the custom GCC&lt;/span&gt;
&lt;span class="c"&gt;# We update .bashrc for interactive shells and set ENV for CI pipelines.&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"export CC=/usr/local/bin/gcc"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /root/.bashrc &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"export CXX=/usr/local/bin/g++"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /root/.bashrc &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"export CC=/usr/local/bin/gcc"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /home/jenkins/.bashrc &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"export CXX=/usr/local/bin/g++"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /home/jenkins/.bashrc

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; CC="/usr/local/bin/gcc"&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; CXX="/usr/local/bin/g++"&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; LD_LIBRARY_PATH="/usr/local/lib64:${LD_LIBRARY_PATH}"&lt;/span&gt;

&lt;span class="c"&gt;# ... (Start of Step 9: Install SonarScanner) ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  6.5 Verification
&lt;/h2&gt;

&lt;p&gt;With the patch applied, we must rebuild the agent image.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Rebuild:&lt;/strong&gt; Run the builder script from &lt;strong&gt;Article 8&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/Documents/FromFirstPrinciples/articles/0008_cicd_part04_jenkins
./02-build-images.sh
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Because we inserted the new instructions &lt;em&gt;after&lt;/em&gt; the heavy compilation steps, Docker will use cached layers for GCC and Python. The rebuild should take seconds, not minutes.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Retest:&lt;/strong&gt; Trigger the &lt;code&gt;0004_std_lib_http_client&lt;/code&gt; job in Jenkins again.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The Result:&lt;/strong&gt;&lt;br&gt;
The &lt;code&gt;Incompatible GCC/GCOV version&lt;/code&gt; error vanishes. CMake now correctly picks up our &lt;code&gt;ENV CC&lt;/code&gt; variable, uses GCC 15 for compilation, and produces binaries compatible with our GCOV 15 analyzer.&lt;/p&gt;

&lt;p&gt;However, solving one problem reveals another. The logs now show a new warning: &lt;code&gt;lcov: WARNING: (inconsistent) ...&lt;/code&gt;. We have fixed the toolchain, but now we must tune the analyzer. This leads us to our next challenge.&lt;/p&gt;
&lt;h1&gt;
  
  
  Chapter 7: The "Missing Language" - C/C++ and the Community Plugin
&lt;/h1&gt;
&lt;h2&gt;
  
  
  7.1 The "Paywall" Limitation
&lt;/h2&gt;

&lt;p&gt;With our toolchain crisis resolved, the pipeline successfully completed a full execution. The compiler built our C, C++, Rust, and Python binaries using the correct GCC 15 instruction set. The scanner uploaded the report, and the dashboard populated.&lt;/p&gt;

&lt;p&gt;But a quick audit of the SonarQube dashboard reveals a glaring omission. We see lines of code for &lt;strong&gt;Python&lt;/strong&gt;. We see lines of code for &lt;strong&gt;Rust&lt;/strong&gt;. But our &lt;strong&gt;C&lt;/strong&gt; and &lt;strong&gt;C++&lt;/strong&gt; implementations—which constitute the core performance logic of our "Hero Project"—are completely missing. They are not just reporting zero coverage; they are invisible.&lt;/p&gt;

&lt;p&gt;This is not a configuration error; it is a feature gate.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;SonarQube Community Build&lt;/strong&gt; explicitly excludes C and C++ analysis from its feature set. These languages are reserved for the paid &lt;strong&gt;Developer Edition&lt;/strong&gt;. For a standard enterprise, paying for this feature is often the correct path. However, for our "First Principles" laboratory, we are operating under the constraint of using open-source infrastructure.&lt;/p&gt;

&lt;p&gt;We cannot accept a partial view of our codebase. Our project contains distinct, idiomatic implementations in C23 and C++23. If we cannot inspect them, we cannot guarantee their quality. To solve this without breaking our budget, we must turn to the open-source ecosystem: the &lt;strong&gt;Community C++ Plugin (&lt;code&gt;sonar-cxx&lt;/code&gt;)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This plugin is a robust alternative to the official analyzer. It provides full support for C and C++ parsing, metric calculation, and—crucially for our next chapter—native ingestion of coverage reports. However, installing it into a containerized, immutable architecture presents a new logistical challenge.&lt;/p&gt;
&lt;h2&gt;
  
  
  7.2 The "Hot-Patch" Strategy
&lt;/h2&gt;

&lt;p&gt;In a standard Docker build, we would simply add a &lt;code&gt;ADD&lt;/code&gt; or &lt;code&gt;COPY&lt;/code&gt; instruction to our &lt;code&gt;Dockerfile&lt;/code&gt; to bake the plugin into the image. However, our architecture prevents this.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Article 5&lt;/strong&gt;, we made a deliberate architectural decision to persist the SonarQube extensions directory using a &lt;strong&gt;Named Volume&lt;/strong&gt; (&lt;code&gt;sonarqube-extensions&lt;/code&gt;). This ensures that if we upgrade the container image, we don't lose our installed plugins.&lt;/p&gt;

&lt;p&gt;However, this persistence creates a deployment constraint: &lt;strong&gt;Volume Masking&lt;/strong&gt;.&lt;br&gt;
When Docker mounts a volume at &lt;code&gt;/opt/sonarqube/extensions&lt;/code&gt;, it overlays the volume's contents on top of the container's filesystem. If we baked the plugin into the image at that path, the empty volume would mask it at runtime, effectively making it disappear.&lt;/p&gt;

&lt;p&gt;Therefore, we cannot install this plugin at build time. We must perform a &lt;strong&gt;"Hot-Patch."&lt;/strong&gt; We will download the plugin to our Host machine and inject it directly into the running container's volume stream.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The "Toolkit" Trap&lt;/strong&gt;&lt;br&gt;
Before we script this, we must heed a specific warning from the plugin maintainers. The release artifacts for &lt;code&gt;sonar-cxx&lt;/code&gt; include two JAR files:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;code&gt;sonar-cxx-plugin-x.y.z.jar&lt;/code&gt;: The actual plugin.&lt;/li&gt;
&lt;li&gt; &lt;code&gt;cxx-sslr-toolkit-x.y.z.jar&lt;/code&gt;: A standalone command-line debugging tool.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It is a common mistake to copy &lt;em&gt;both&lt;/em&gt; files into the plugins directory. &lt;strong&gt;Do not do this.&lt;/strong&gt; The toolkit is not a SonarQube plugin; it lacks the required manifest metadata. If you place it in the plugins folder, SonarQube will attempt to load it, fail to find the plugin key, and crash the web server with a &lt;code&gt;java.lang.NullPointerException&lt;/code&gt;. We must be surgical, installing only the plugin JAR.&lt;/p&gt;
&lt;h2&gt;
  
  
  7.3 The Installer Script (&lt;code&gt;06-install-cxx-plugin.sh&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;To execute this "Hot-Patch" reliably, we will encapsulate the logic in a script. This script acts as a bridge between the external open-source ecosystem and our internal, immutable infrastructure.&lt;/p&gt;

&lt;p&gt;It performs a precise sequence of operations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Fetch:&lt;/strong&gt; It downloads the specific, pinned version of the plugin (&lt;code&gt;2.2.1&lt;/code&gt;) to the Host machine. This avoids relying on &lt;code&gt;wget&lt;/code&gt; or &lt;code&gt;curl&lt;/code&gt; being present inside the minimal SonarQube container image.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Clean:&lt;/strong&gt; It removes any existing versions of the plugin from the container's volume. This prevents version conflicts (e.g., having both v2.1 and v2.2 JARs loaded simultaneously), which causes startup crashes.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Inject:&lt;/strong&gt; It uses &lt;code&gt;docker cp&lt;/code&gt; to surgically insert the JAR into the running container's file stream.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Secure:&lt;/strong&gt; It repairs the file permissions. Files copied from the host often arrive owned by &lt;code&gt;root&lt;/code&gt;. SonarQube runs as &lt;code&gt;sonarqube&lt;/code&gt; (UID 1000). If we don't fix this ownership, the application will crash with &lt;code&gt;Permission Denied&lt;/code&gt; when it tries to load the class.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Reload:&lt;/strong&gt; It restarts the container to force the Java Classloader to pick up the new library.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Create this file at &lt;code&gt;~/cicd_stack/sonarqube/06-install-cxx-plugin.sh&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;#               06-install-cxx-plugin.sh&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  Installs the Community C++ Plugin (sonar-cxx) into the&lt;/span&gt;
&lt;span class="c"&gt;#  running SonarQube container.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  We do this post-deployment because the 'extensions'&lt;/span&gt;
&lt;span class="c"&gt;#  directory is a mounted volume, which obscures any&lt;/span&gt;
&lt;span class="c"&gt;#  plugins we might try to bake into the Docker image.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="c"&gt;# --- Configuration ---&lt;/span&gt;
&lt;span class="c"&gt;# We pin the version to ensure reproducibility&lt;/span&gt;
&lt;span class="nv"&gt;PLUGIN_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"2.2.1.1248"&lt;/span&gt;
&lt;span class="nv"&gt;PLUGIN_RELEASE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"cxx-2.2.1"&lt;/span&gt;
&lt;span class="nv"&gt;PLUGIN_JAR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"sonar-cxx-plugin-&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PLUGIN_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.jar"&lt;/span&gt;
&lt;span class="nv"&gt;DOWNLOAD_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://github.com/SonarOpenCommunity/sonar-cxx/releases/download/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PLUGIN_RELEASE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PLUGIN_JAR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="nv"&gt;CONTAINER_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"sonarqube"&lt;/span&gt;
&lt;span class="nv"&gt;CONTAINER_PLUGIN_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/opt/sonarqube/extensions/plugins"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Starting C++ Plugin Installation..."&lt;/span&gt;

&lt;span class="c"&gt;# --- 1. Download Plugin to Host ---&lt;/span&gt;
&lt;span class="c"&gt;# We download to the host first to avoid dependency issues inside the container&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Downloading &lt;/span&gt;&lt;span class="nv"&gt;$PLUGIN_JAR&lt;/span&gt;&lt;span class="s2"&gt;..."&lt;/span&gt;
wget &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;--show-progress&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DOWNLOAD_URL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PLUGIN_JAR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: Download failed."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# --- 2. Remove Old Versions ---&lt;/span&gt;
&lt;span class="c"&gt;# We check if an older version exists in the container and remove it&lt;/span&gt;
&lt;span class="c"&gt;# to prevent classpath conflicts (having two versions of the same plugin).&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Checking for existing C++ plugins..."&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CONTAINER_NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    bash &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"rm -f &lt;/span&gt;&lt;span class="nv"&gt;$CONTAINER_PLUGIN_DIR&lt;/span&gt;&lt;span class="s2"&gt;/sonar-cxx-plugin-*.jar"&lt;/span&gt;

&lt;span class="c"&gt;# --- 3. Install New Plugin ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Installing new plugin..."&lt;/span&gt;
docker &lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PLUGIN_JAR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CONTAINER_NAME&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;$CONTAINER_PLUGIN_DIR&lt;/span&gt;&lt;span class="s2"&gt;/"&lt;/span&gt;

&lt;span class="c"&gt;# --- 4. Fix Permissions ---&lt;/span&gt;
&lt;span class="c"&gt;# The file copied from host arrives owned by root.&lt;/span&gt;
&lt;span class="c"&gt;# SonarQube runs as UID 1000. We must fix this or the app crashes.&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Fixing permissions..."&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; 0 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CONTAINER_NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nb"&gt;chown &lt;/span&gt;1000:1000 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CONTAINER_PLUGIN_DIR&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$PLUGIN_JAR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# --- 5. Cleanup Host File ---&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PLUGIN_JAR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# --- 6. Restart SonarQube ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Restarting SonarQube to load plugin..."&lt;/span&gt;
docker restart &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CONTAINER_NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Plugin installed. Please verify in Administration &amp;gt; Marketplace &amp;gt; Installed."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Execution
&lt;/h3&gt;

&lt;p&gt;Run this script from your host machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x 06-install-cxx-plugin.sh
./06-install-cxx-plugin.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the script completes and SonarQube restarts (which may take 2 minutes), log in to the UI and navigate to &lt;strong&gt;Administration &amp;gt; Marketplace&lt;/strong&gt;. You should see the &lt;strong&gt;CXX (Community)&lt;/strong&gt; plugin listed in the "Installed" tab. The engine is now capable of understanding C++.&lt;/p&gt;

&lt;h2&gt;
  
  
  7.4 Configuration: Enabling the Sensors
&lt;/h2&gt;

&lt;p&gt;With the plugin physically present in the container's volume, we must now instruct the scanner to use it.&lt;/p&gt;

&lt;p&gt;If you were to run the build immediately after the restart, the dashboard would likely remain unchanged. This is because SonarQube scanners operate on a strictly &lt;strong&gt;Opt-In&lt;/strong&gt; basis for file extensions. The default Java scanner claims &lt;code&gt;.java&lt;/code&gt; files; the Python scanner claims &lt;code&gt;.py&lt;/code&gt; files. Since the Community Build has no native C++ scanner, &lt;code&gt;.cpp&lt;/code&gt; and &lt;code&gt;.h&lt;/code&gt; files are currently "orphaned"—they belong to no one, so they are ignored during the indexing phase.&lt;/p&gt;

&lt;p&gt;We must explicitly assign these extensions to our new &lt;code&gt;cxx&lt;/code&gt; plugin.&lt;/p&gt;

&lt;p&gt;We do this by updating our client-side configuration file, &lt;code&gt;sonar-project.properties&lt;/code&gt;. While we will provide a comprehensive breakdown of this file's architecture—including Analysis Scopes and Test definitions—in &lt;strong&gt;Chapter 9&lt;/strong&gt;, for now, we focus on the single property required to wake up the C++ sensors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Action: Update &lt;code&gt;sonar-project.properties&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Add the following line to your configuration file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="c"&gt;# --- LANGUAGE SPECIFICS ---
# The sonar-cxx plugin uses these suffixes to claim ownership of files
&lt;/span&gt;&lt;span class="py"&gt;sonar.cxx.file.suffixes&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;.cxx,.cpp,.cc,.c,.h,.hpp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Verification:&lt;/strong&gt;&lt;br&gt;
Commit this change and trigger the &lt;strong&gt;&lt;code&gt;0004_std_lib_http_client&lt;/code&gt;&lt;/strong&gt; job in Jenkins.&lt;/p&gt;

&lt;p&gt;When the analysis completes, check the SonarQube dashboard. You will notice a significant change:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Lines of Code:&lt;/strong&gt; The total count will jump. You will see a new entry for &lt;strong&gt;C++&lt;/strong&gt; (or "CFamily") in the language breakdown.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Issues:&lt;/strong&gt; You may start seeing "Code Smells" or style warnings generated by the plugin's internal rules.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Coverage:&lt;/strong&gt; This will likely still be &lt;strong&gt;0.0%&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We have solved the &lt;strong&gt;Indexing&lt;/strong&gt; problem (the files are visible), but we have not solved the &lt;strong&gt;Metrics&lt;/strong&gt; problem. The plugin sees the code, but it cannot read the coverage report because the data format we are generating (LCOV) is clashing with the plugin's path resolution logic. This leads us to our next battle: The Data Format War.&lt;/p&gt;
&lt;h1&gt;
  
  
  Chapter 8: The Data Format War - LCOV vs. Cobertura
&lt;/h1&gt;
&lt;h2&gt;
  
  
  8.1 The "Tower of Babel"
&lt;/h2&gt;

&lt;p&gt;We have successfully "jailbroken" our Community Build. By injecting the &lt;code&gt;sonar-cxx&lt;/code&gt; plugin, we forced the scanner to acknowledge the existence of our C++ code. If you look at the dashboard now, you will see our &lt;code&gt;.cpp&lt;/code&gt; files listed in the "Code" tab, and the "Lines of Code" metric finally reflects reality.&lt;/p&gt;

&lt;p&gt;However, a deeper look reveals a new failure. The &lt;strong&gt;Coverage&lt;/strong&gt; column for C++ remains at &lt;strong&gt;0.0%&lt;/strong&gt;, even though our Jenkins logs clearly show &lt;code&gt;lcov&lt;/code&gt; successfully capturing data. We have the files, but we don't have the metrics.&lt;/p&gt;

&lt;p&gt;To understand why, we must look at the scanner logs from our last build. Hidden amongst the success messages is a flood of warnings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WARN  Cannot sanitize file path './../src/c/httpc.c', ignoring coverage measures
WARN  Cannot sanitize file path './../src/cpp/httpcpp.cpp', ignoring coverage measures
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a classic "Tower of Babel" problem. Our builder and our inspector are speaking different languages regarding &lt;strong&gt;File Paths&lt;/strong&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;The Builder's Perspective:&lt;/strong&gt; We executed our tests and coverage capture inside the &lt;code&gt;build_debug&lt;/code&gt; directory. From that vantage point, the source code lives one directory up (&lt;code&gt;../src&lt;/code&gt;). &lt;code&gt;lcov&lt;/code&gt; faithfully recorded these relative paths in the tracefile.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The Inspector's Perspective:&lt;/strong&gt; The SonarQube Scanner runs from the &lt;strong&gt;Project Root&lt;/strong&gt;. It expects all paths to be relative to the root (e.g., &lt;code&gt;src/c/httpc.c&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When the plugin reads the LCOV file, it sees a record for &lt;code&gt;../src/c/httpc.c&lt;/code&gt;. It tries to match this against the file index it built from the root. Since &lt;code&gt;../src&lt;/code&gt; implies a path &lt;em&gt;outside&lt;/em&gt; the project root, the sanitizer rejects it as a security risk or simply fails to map it. The data is discarded to prevent database corruption.&lt;/p&gt;

&lt;p&gt;To fix this, we must align our perspectives. We need a format that is robust enough to handle path translation, or we need to change our execution context so the paths match naturally.&lt;/p&gt;

&lt;h2&gt;
  
  
  8.2 The Converter Strategy
&lt;/h2&gt;

&lt;p&gt;We have two options to solve this.&lt;/p&gt;

&lt;p&gt;The "Hacker" approach would be to use &lt;code&gt;sed&lt;/code&gt; to rewrite the text inside the LCOV file, stripping the &lt;code&gt;../&lt;/code&gt; prefix before the scanner sees it. This is fragile. If we ever change our build directory depth (e.g., &lt;code&gt;build/debug/x86&lt;/code&gt;), our pipeline breaks.&lt;/p&gt;

&lt;p&gt;The "First Principles" approach is to fix the &lt;strong&gt;Execution Context&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The relative paths are being generated because we are running the &lt;code&gt;lcov --capture&lt;/code&gt; command &lt;em&gt;inside&lt;/em&gt; the &lt;code&gt;build_debug&lt;/code&gt; directory. To &lt;code&gt;lcov&lt;/code&gt;, the source files &lt;em&gt;are&lt;/em&gt; literally one level up.&lt;/p&gt;

&lt;p&gt;If we move the execution of the capture command to the &lt;strong&gt;Project Root&lt;/strong&gt;, the perspective changes. From the root, the source files are in &lt;code&gt;src/&lt;/code&gt; and the object files are in &lt;code&gt;build_debug/&lt;/code&gt;. If we tell &lt;code&gt;lcov&lt;/code&gt; to capture from the root, it will generate paths starting with &lt;code&gt;src/&lt;/code&gt;, which is exactly what SonarQube expects.&lt;/p&gt;

&lt;p&gt;Additionally, we will convert this data into &lt;strong&gt;Cobertura XML&lt;/strong&gt;. While the &lt;code&gt;sonar-cxx&lt;/code&gt; plugin supports LCOV, the Cobertura format is the "Lingua Franca" of CI/CD. It is more widely supported by other tools (like Jenkins' own coverage view) and tends to be more robust against path quirks than raw LCOV tracefiles.&lt;/p&gt;

&lt;p&gt;We will use a Python tool called &lt;code&gt;lcov_cobertura&lt;/code&gt; to perform this translation on the fly. This fits perfectly into our polyglot agent, as we already have a Python environment available.&lt;/p&gt;

&lt;h2&gt;
  
  
  8.3 The Patch (&lt;code&gt;run-coverage-cicd.sh&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;We need to rewrite the C++ section of our coverage script to change &lt;em&gt;where&lt;/em&gt; the commands run.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Changes:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Context Switch:&lt;/strong&gt; We move the &lt;code&gt;lcov&lt;/code&gt; capture logic &lt;em&gt;outside&lt;/em&gt; the subshell that enters &lt;code&gt;build_debug&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Base Directory:&lt;/strong&gt; We explicitly tell &lt;code&gt;lcov&lt;/code&gt; that our base directory is the current folder (&lt;code&gt;.&lt;/code&gt;), ensuring paths are calculated relative to the project root.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The Converter:&lt;/strong&gt; We install &lt;code&gt;lcov_cobertura&lt;/code&gt; inside our existing Python virtual environment (to avoid PEP 668 system package errors) and use it to generate the final XML report.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Update your &lt;strong&gt;&lt;code&gt;run-coverage-cicd.sh&lt;/code&gt;&lt;/strong&gt; with the following logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# ... (Header) ...&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="c"&gt;# --- 1. C/C++ Coverage (LCOV) ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Running C/C++ Tests &amp;amp; Coverage ---"&lt;/span&gt;

&lt;span class="c"&gt;# A. Zero Counters (Run from Root, targeting build dir)&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; build_debug
lcov &lt;span class="nt"&gt;--directory&lt;/span&gt; build_debug &lt;span class="nt"&gt;--zerocounters&lt;/span&gt; &lt;span class="nt"&gt;--ignore-errors&lt;/span&gt; inconsistent,unused,negative &lt;span class="nt"&gt;-q&lt;/span&gt;

&lt;span class="c"&gt;# B. Build &amp;amp; Run Tests (Inside build dir)&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="nb"&gt;cd &lt;/span&gt;build_debug &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;exit&lt;/span&gt;
    &lt;span class="c"&gt;# Ensure Debug build for coverage flags&lt;/span&gt;
    cmake &lt;span class="nt"&gt;-DCMAKE_BUILD_TYPE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Debug ..
    cmake &lt;span class="nt"&gt;--build&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;-j&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;nproc&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
    ctest &lt;span class="nt"&gt;--output-on-failure&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# C. Capture &amp;amp; Filter (Run from Root)&lt;/span&gt;
&lt;span class="c"&gt;# We capture from the root so paths like 'src/c/httpc.c' are relative to here.&lt;/span&gt;
lcov &lt;span class="nt"&gt;--capture&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;--directory&lt;/span&gt; build_debug &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;--output-file&lt;/span&gt; coverage.cxx.info &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;--ignore-errors&lt;/span&gt; inconsistent,unused,negative &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;--base-directory&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# Filter Artifacts&lt;/span&gt;
lcov &lt;span class="nt"&gt;--remove&lt;/span&gt; coverage.cxx.info &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="s1"&gt;'/usr/*'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="s1"&gt;'*/_deps/*'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="s1"&gt;'*/tests/helpers.h'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="s1"&gt;'*/benchmark/*'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="s1"&gt;'*/apps/*'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="s1"&gt;'*/docs/*'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="s1"&gt;'*/cmake/*'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="s1"&gt;'*/.cache/*'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;-o&lt;/span&gt; coverage.cxx.filtered.info &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;--ignore-errors&lt;/span&gt; inconsistent,unused,negative

&lt;span class="c"&gt;# Rename for final use&lt;/span&gt;
&lt;span class="nb"&gt;mv &lt;/span&gt;coverage.cxx.filtered.info coverage.cxx.info

&lt;span class="c"&gt;# ... (Rust Section Unchanged) ...&lt;/span&gt;

&lt;span class="c"&gt;# --- 3. Python Coverage (XML) ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Running Python Tests &amp;amp; Installing Tools ---"&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;".venv"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
        &lt;span class="nb"&gt;.&lt;/span&gt; .venv/bin/activate
    &lt;span class="k"&gt;fi
    &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;src/python

    &lt;span class="c"&gt;# Install lcov_cobertura here (inside the venv)&lt;/span&gt;
    python3 &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--editable&lt;/span&gt; .[test] lcov_cobertura &lt;span class="nt"&gt;--quiet&lt;/span&gt;

    &lt;span class="c"&gt;# Run Python Tests&lt;/span&gt;
    pytest &lt;span class="nt"&gt;-sv&lt;/span&gt; &lt;span class="nt"&gt;--cov&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;httppy &lt;span class="nt"&gt;--cov-report&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;xml:../../coverage.python.xml tests
&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✅ Python coverage generated: coverage.python.xml"&lt;/span&gt;


&lt;span class="c"&gt;# --- 4. Convert C++ LCOV to Cobertura XML (From Root) ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Converting C++ LCOV to Cobertura XML ---"&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="c"&gt;# Activate the venv (using relative path from Root) to get access to lcov_cobertura&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"src/python/.venv/bin/activate"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
        &lt;span class="nb"&gt;.&lt;/span&gt; src/python/.venv/bin/activate
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;".venv/bin/activate"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
        &lt;span class="nb"&gt;.&lt;/span&gt; .venv/bin/activate
    &lt;span class="k"&gt;fi&lt;/span&gt;

    &lt;span class="c"&gt;# Run conversion from ROOT so paths remain 'src/c/...' (matching the LCOV input)&lt;/span&gt;
    lcov_cobertura coverage.cxx.info &lt;span class="nt"&gt;--output&lt;/span&gt; coverage.cxx.xml
&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✅ C/C++ Cobertura XML generated: coverage.cxx.xml"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  8.4 Verification: Closing the Loop
&lt;/h2&gt;

&lt;p&gt;We have successfully bridged the "Tower of Babel." Our coverage script now performs a sophisticated translation: it compiles in C++, captures in LCOV, translates to Cobertura XML, and aligns all file paths to the project root.&lt;/p&gt;

&lt;p&gt;Now, we must tell the "Inspector" where to find this translated map.&lt;/p&gt;

&lt;p&gt;We return to our &lt;code&gt;sonar-project.properties&lt;/code&gt; file. Previously, we attempted to use &lt;code&gt;sonar.cxx.lcov.reportPath&lt;/code&gt;. We must now replace this with the Cobertura-specific property supported by the community plugin.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Action: Update &lt;code&gt;sonar-project.properties&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="c"&gt;# ... (Previous Python/Rust config) ...
&lt;/span&gt;
&lt;span class="c"&gt;# 3. C/C++ (Cobertura XML via sonar-cxx plugin)
# We replaced the LCOV property with the Cobertura property
# pointing to the file generated by our Python converter.
&lt;/span&gt;&lt;span class="py"&gt;sonar.cxx.cobertura.reportPaths&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;coverage.cxx.xml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Final Test&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Commit this change and trigger the &lt;strong&gt;&lt;code&gt;0004_std_lib_http_client&lt;/code&gt;&lt;/strong&gt; job in Jenkins one last time.&lt;/p&gt;

&lt;p&gt;When the build completes, open the SonarQube dashboard. You will witness the final piece of the puzzle falling into place:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Lines of Code:&lt;/strong&gt; C++ is present.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Issues:&lt;/strong&gt; C++ code smells are present.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Coverage:&lt;/strong&gt; The "0.0%" is gone. You should see a valid coverage percentage (likely &amp;gt;90% given our test suite) for your &lt;code&gt;.cpp&lt;/code&gt; files.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We have achieved what the official documentation implies is impossible for the Community Build: full, polyglot coverage analysis for C++, Rust, and Python in a single pipeline.&lt;/p&gt;

&lt;h1&gt;
  
  
  Chapter 9: Tuning the Signal - Scopes and Exclusions
&lt;/h1&gt;

&lt;h2&gt;
  
  
  9.1 The "Low Signal" Mystery
&lt;/h2&gt;

&lt;p&gt;With our format wars resolved, we triggered another build. The results on the SonarQube dashboard were a mixed bag of triumph and confusion.&lt;/p&gt;

&lt;p&gt;On one hand, our &lt;strong&gt;C++&lt;/strong&gt; coverage—previously a flat zero—had surged to over 90%, vindicating our efforts with the &lt;code&gt;sonar-cxx&lt;/code&gt; plugin and the Cobertura converter. The scanner was successfully mapping the execution traces back to the source files.&lt;/p&gt;

&lt;p&gt;On the other hand, our &lt;strong&gt;Python&lt;/strong&gt; coverage sat at a dismal &lt;strong&gt;24.8%&lt;/strong&gt;. Even more alarming, our &lt;strong&gt;Rust&lt;/strong&gt; code showed no coverage data at all, despite our logs confirming that &lt;code&gt;cargo-llvm-cov&lt;/code&gt; had run successfully.&lt;/p&gt;

&lt;p&gt;We faced a new problem: &lt;strong&gt;Signal-to-Noise Ratio&lt;/strong&gt;. We were piping data into the system, but the metrics were skewed. To understand why, we performed a forensic audit of the Python metrics.&lt;/p&gt;

&lt;p&gt;Drilling down into the &lt;strong&gt;Code&lt;/strong&gt; tab for the Python module, we found the culprit immediately. Files like &lt;code&gt;src/python/tests/test_httppy.py&lt;/code&gt; were marked with a red bar indicating &lt;strong&gt;0.0% Coverage&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This revealed a fundamental misconfiguration in our &lt;strong&gt;Analysis Scope&lt;/strong&gt;. By default, if you do not explicitly tell SonarQube otherwise, it assumes &lt;em&gt;every&lt;/em&gt; file inside the &lt;code&gt;sonar.sources&lt;/code&gt; directory is production code that must be tested.&lt;/p&gt;

&lt;p&gt;Our pipeline was calculating the coverage &lt;strong&gt;of&lt;/strong&gt; our tests, rather than the coverage &lt;strong&gt;by&lt;/strong&gt; our tests.&lt;/p&gt;

&lt;p&gt;Since test files generally do not test themselves, they report 0% coverage. In a project where the volume of test code roughly equals the volume of application code (a sign of a healthy project), this misconfiguration mathematically halts your coverage score at 50%. To fix this, we must teach the Inspector to distinguish between the &lt;em&gt;Target&lt;/em&gt; (Source) and the &lt;em&gt;Evidence&lt;/em&gt; (Tests).&lt;/p&gt;

&lt;h2&gt;
  
  
  9.2 Defining the Scope (&lt;code&gt;sonar.sources&lt;/code&gt; vs. &lt;code&gt;sonar.tests&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;To fix this, we must explicitly define our &lt;strong&gt;Analysis Scope&lt;/strong&gt; in &lt;code&gt;sonar-project.properties&lt;/code&gt;. We need to decouple what is &lt;em&gt;scanned&lt;/em&gt; from what is &lt;em&gt;measured&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;This requires a clear understanding of two properties that often confuse beginners:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;sonar.sources&lt;/code&gt;&lt;/strong&gt;: This defines your &lt;strong&gt;Production Code&lt;/strong&gt;. SonarQube will scan these files for bugs, code smells, and vulnerabilities. Crucially, these are the files that &lt;em&gt;must&lt;/em&gt; be covered by tests. If a file is here, any uncovered line penalizes your score.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;sonar.tests&lt;/code&gt;&lt;/strong&gt;: This defines your &lt;strong&gt;Test Code&lt;/strong&gt;. SonarQube will scan these files for test-specific issues (e.g., "Assertion in a loop"), but it will &lt;strong&gt;exempt&lt;/strong&gt; them from coverage calculations. It understands that these files exist to &lt;em&gt;provide&lt;/em&gt; coverage, not to &lt;em&gt;consume&lt;/em&gt; it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Our fix is to physically separate our directories into these two buckets.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Action: Update &lt;code&gt;sonar-project.properties&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We will edit our properties file to explicitly list &lt;code&gt;src/python/tests&lt;/code&gt; under &lt;code&gt;sonar.tests&lt;/code&gt;, removing it from the default source scope.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="c"&gt;# --- ANALYSIS SCOPE ---
&lt;/span&gt;
&lt;span class="c"&gt;# 1. Main Sources (Targets for Coverage)
# We include 'src' (code) and 'include' (C++ headers)
&lt;/span&gt;&lt;span class="py"&gt;sonar.sources&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;src,include&lt;/span&gt;

&lt;span class="c"&gt;# 2. Test Sources (Providers of Coverage)
# We explicitly list the top-level 'tests' folder AND the python-specific tests
&lt;/span&gt;&lt;span class="py"&gt;sonar.tests&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;tests, src/python/tests&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By making this distinction, we tell the Inspector: "Analyze &lt;code&gt;src/python/httppy&lt;/code&gt; to see if it is tested. Analyze &lt;code&gt;src/python/tests&lt;/code&gt; to see if the tests themselves are well-written, but do not demand that I write tests for my tests."&lt;/p&gt;

&lt;p&gt;This single change instantly corrects the math, removing thousands of lines of "uncovered" test code from the denominator of our coverage equation.&lt;/p&gt;

&lt;h2&gt;
  
  
  9.3 Awakening Rust
&lt;/h2&gt;

&lt;p&gt;While fixing Python, we also addressed the missing Rust data. Our investigation revealed a simple oversight: we were generating the report, but we never told SonarQube where to find it.&lt;/p&gt;

&lt;p&gt;Unlike C++, which required a plugin and a format converter, &lt;strong&gt;Rust&lt;/strong&gt; support in the Community Build is robust. The documentation confirms that the native Rust sensor supports &lt;strong&gt;LCOV&lt;/strong&gt; ingestion out of the box.&lt;/p&gt;

&lt;p&gt;We simply need to map the file we generated in our CI pipeline (&lt;code&gt;coverage.rust.info&lt;/code&gt;) to the correct property key.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Action: Update &lt;code&gt;sonar-project.properties&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="c"&gt;# --- COVERAGE REPORTING ---
&lt;/span&gt;
&lt;span class="c"&gt;# 2. Rust (LCOV)
# Native support in Community Edition requires this specific property.
# It tells the Rust sensor to read the LCOV file we generated with cargo-llvm-cov.
&lt;/span&gt;&lt;span class="py"&gt;sonar.rust.lcov.reportPaths&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;coverage.rust.info&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a "First Principles" lesson in configuration: tools are useless if they are disconnected. We spent effort generating the data, but without this single line of glue code, that effort was invisible to the system.&lt;/p&gt;

&lt;h2&gt;
  
  
  9.4 The Surgeon's Scalpel (Coverage Exclusions)
&lt;/h2&gt;

&lt;p&gt;Finally, we apply a more granular tool: &lt;strong&gt;Coverage Exclusions&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Sometimes, you have code that &lt;em&gt;is&lt;/em&gt; production code (it belongs in &lt;code&gt;sonar.sources&lt;/code&gt;), but it is structurally impossible or architecturally unnecessary to unit test.&lt;/p&gt;

&lt;p&gt;In our "Hero Project," our Rust implementation includes two binary entry points: &lt;code&gt;src/bin/httprust_client.rs&lt;/code&gt; and &lt;code&gt;src/bin/reqwest_client.rs&lt;/code&gt;. These files are thin wrappers—simple &lt;code&gt;main()&lt;/code&gt; functions that parse arguments and call the library. Testing &lt;code&gt;main()&lt;/code&gt; functions is notoriously difficult and often yields low value.&lt;/p&gt;

&lt;p&gt;However, because they are in &lt;code&gt;src/&lt;/code&gt;, SonarQube treats them as production code and penalizes us for their 0% coverage.&lt;/p&gt;

&lt;p&gt;We do not want to use &lt;code&gt;sonar.exclusions&lt;/code&gt;, because that would hide them completely. We &lt;em&gt;want&lt;/em&gt; SonarQube to scan them for bugs (e.g., a memory leak in &lt;code&gt;main&lt;/code&gt;). We just don't want them dragging down our coverage score.&lt;/p&gt;

&lt;p&gt;The solution is &lt;strong&gt;&lt;code&gt;sonar.coverage.exclusions&lt;/code&gt;&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="c"&gt;# 3. Coverage Exclusions
# The Surgeon's Scalpel: Analyzed for bugs, but ignored for coverage stats.
# We exclude the Rust binaries (executables)
&lt;/span&gt;&lt;span class="py"&gt;sonar.coverage.exclusions&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;src/rust/src/bin/**&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the difference between a "blunt instrument" (hiding the file) and a "precision tool" (tuning the metric).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Payoff:&lt;/strong&gt;&lt;br&gt;
With these three changes—splitting scopes, mapping Rust reports, and excluding entry points—we committed the configuration and ran the build.&lt;/p&gt;

&lt;p&gt;The result was definitive. Our coverage score, once a broken 0% or a noisy 24%, stabilized at a rock-solid &lt;strong&gt;94.1%&lt;/strong&gt;. We now have a clean, accurate signal.&lt;/p&gt;
&lt;h2&gt;
  
  
  9.5 The Blueprint: Deconstructing &lt;code&gt;sonar-project.properties&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;We have modified our &lt;code&gt;sonar-project.properties&lt;/code&gt; file iteratively throughout this process. Now, let us step back and analyze the complete, final "Blueprint" that drives our inspection.&lt;/p&gt;

&lt;p&gt;This file is the single source of truth for the scanner. It bridges the gap between our source code structure and SonarQube's expectations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Project Identification
# This key is the unique ID of the project in SonarQube's database.
# Since we imported this project from GitLab, we MUST use the key
# generated by SonarQube (e.g., with the UUID suffix).
# You can find this key in the SonarQube UI -&amp;gt; Project Information.
&lt;/span&gt;&lt;span class="py"&gt;sonar.projectKey&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;articles_0004_std_lib_http_client_47ae4183-478d-4d13-bb80-86217c694444&lt;/span&gt;

&lt;span class="c"&gt;# 2. Analysis Scope (The "What")
# sonar.sources defines Production Code (analyzed for bugs + coverage required).
# We include 'src' (application code) and 'include' (C++ headers).
&lt;/span&gt;&lt;span class="py"&gt;sonar.sources&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;src,include&lt;/span&gt;

&lt;span class="c"&gt;# sonar.tests defines Test Code (analyzed for bugs + NO coverage required).
# We explicitly move 'src/python/tests' here to fix our Python coverage ratio.
&lt;/span&gt;&lt;span class="py"&gt;sonar.tests&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;tests, src/python/tests&lt;/span&gt;

&lt;span class="c"&gt;# sonar.exclusions removes files from analysis entirely (invisible to SonarQube).
# We hide build artifacts (.o, .so) and temporary directories.
&lt;/span&gt;&lt;span class="py"&gt;sonar.exclusions&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;build_debug/**, build_release/**, **/*.o, **/*.so, **/*.a, **/*.zip, **/*.crate, **/*.whl&lt;/span&gt;

&lt;span class="c"&gt;# sonar.coverage.exclusions keeps files visible but ignores them for coverage stats.
# We use this for our Rust binary entry points, which are hard to unit test.
&lt;/span&gt;&lt;span class="py"&gt;sonar.coverage.exclusions&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;src/rust/src/bin/**&lt;/span&gt;

&lt;span class="c"&gt;# 3. Language Specifics
# We hint the scanner to use Python 3.12 parsing rules.
&lt;/span&gt;&lt;span class="py"&gt;sonar.python.version&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;3.12&lt;/span&gt;
&lt;span class="c"&gt;# We explicitly tell the 'sonar-cxx' plugin which extensions it owns.
&lt;/span&gt;&lt;span class="py"&gt;sonar.cxx.file.suffixes&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;.cxx,.cpp,.cc,.c,.h,.hpp&lt;/span&gt;

&lt;span class="c"&gt;# 4. Coverage Reporting (The "Evidence")
# Python: Native Cobertura XML support.
&lt;/span&gt;&lt;span class="py"&gt;sonar.python.coverage.reportPaths&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;coverage.python.xml&lt;/span&gt;

&lt;span class="c"&gt;# Rust: Native LCOV support (Community Build feature).
&lt;/span&gt;&lt;span class="py"&gt;sonar.rust.lcov.reportPaths&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;coverage.rust.info&lt;/span&gt;

&lt;span class="c"&gt;# C/C++: Cobertura XML support via the 'sonar-cxx' community plugin.
# Note that we point to the file generated by our lcov_cobertura converter.
&lt;/span&gt;&lt;span class="py"&gt;sonar.cxx.cobertura.reportPaths&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;coverage.cxx.xml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Where to find the Values
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;sonar.projectKey&lt;/code&gt;&lt;/strong&gt;: This is the most critical value. If you get this wrong, the scanner will create a &lt;em&gt;new&lt;/em&gt; project instead of updating the existing one. You find this in the SonarQube UI:

&lt;ol&gt;
&lt;li&gt; Go to your Project Dashboard.&lt;/li&gt;
&lt;li&gt; Click &lt;strong&gt;Project Information&lt;/strong&gt; (usually on the right sidebar).&lt;/li&gt;
&lt;li&gt; Copy the &lt;strong&gt;Project Key&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;sonar.sources&lt;/code&gt; / &lt;code&gt;sonar.tests&lt;/code&gt;&lt;/strong&gt;: These are relative paths from your repository root. You determine these by looking at your project structure (&lt;code&gt;ls -R&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;sonar.*.reportPaths&lt;/code&gt;&lt;/strong&gt;: These must match the output filenames defined in your &lt;code&gt;run-coverage-cicd.sh&lt;/code&gt; script. Consistency between the shell script (Builder) and this properties file (Inspector) is mandatory.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Chapter 10: The Gatekeeper - Enforcing MQR Standards
&lt;/h1&gt;

&lt;h2&gt;
  
  
  10.1 The Shift to MQR (Multi-Quality Rule) Mode
&lt;/h2&gt;

&lt;p&gt;When you open your SonarQube dashboard for the first time after a successful analysis, you might notice that the terminology differs from older tutorials or legacy versions of the software.&lt;/p&gt;

&lt;p&gt;In previous versions of SonarQube, issues were categorized into three rigid types: &lt;strong&gt;Bugs&lt;/strong&gt;, &lt;strong&gt;Vulnerabilities&lt;/strong&gt;, and &lt;strong&gt;Code Smells&lt;/strong&gt;. While functional, this taxonomy often led to ambiguity. Is a memory leak a "Bug" or a "Code Smell"? Is a hardcoded password a "Vulnerability" or a "Security Hotspot"?&lt;/p&gt;

&lt;p&gt;Modern SonarQube (specifically the v10.x and v25.x Community Builds we are using) has shifted to a new default paradigm called &lt;strong&gt;MQR (Multi-Quality Rule) Mode&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This mode reorients the analysis around three fundamental &lt;strong&gt;Software Qualities&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Reliability:&lt;/strong&gt; Will the software crash? Issues here include logic errors, unhandled exceptions, and memory leaks.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Security:&lt;/strong&gt; Can the software be exploited? Issues here include injection flaws, weak cryptography, and hardcoded secrets.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Maintainability:&lt;/strong&gt; Can the software be updated? Issues here include high cognitive complexity, duplicated blocks, and spaghetti code.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The Severity Shift
&lt;/h3&gt;

&lt;p&gt;Crucially, MQR Mode decouples the &lt;em&gt;Type&lt;/em&gt; of issue from its &lt;em&gt;Severity&lt;/em&gt;. An issue is no longer just "Critical" or "Minor." It is rated on a specific scale of impact for each quality dimension:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Blocker:&lt;/strong&gt; A high probability of high impact (e.g., a buffer overflow). Immediate fix required.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High:&lt;/strong&gt; High impact or high probability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Medium:&lt;/strong&gt; Moderate impact.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Low:&lt;/strong&gt; Low impact.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Info:&lt;/strong&gt; Contextual information.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The "Sonar way" Gate
&lt;/h3&gt;

&lt;p&gt;This shift directly impacts how our &lt;strong&gt;Quality Gate&lt;/strong&gt; functions.&lt;/p&gt;

&lt;p&gt;The default "Sonar way" gate that is currently applied to our project does not simply say "No Bugs." It enforces specific MQR metrics on &lt;strong&gt;New Code&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reliability Rating:&lt;/strong&gt; Must be &lt;strong&gt;A&lt;/strong&gt; (No High/Blocker reliability issues).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security Rating:&lt;/strong&gt; Must be &lt;strong&gt;A&lt;/strong&gt; (No High/Blocker security issues).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintainability Rating:&lt;/strong&gt; Must be &lt;strong&gt;A&lt;/strong&gt; (Technical Debt Ratio &amp;lt; 5%).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Coverage:&lt;/strong&gt; Must be &lt;strong&gt;&amp;gt;= 80.0%&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Duplicated Lines:&lt;/strong&gt; Must be &lt;strong&gt;&amp;lt;= 3.0%&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By understanding this taxonomy, we understand what is required to pass the gate. It is not enough to just "write tests" (Coverage); we must also write clean, secure code (MQR Ratings).&lt;/p&gt;

&lt;h2&gt;
  
  
  10.2 The Logic of the Gate (&lt;code&gt;waitForQualityGate&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;Now that we understand the criteria, we must examine the mechanism that enforces them.&lt;/p&gt;

&lt;p&gt;In our &lt;strong&gt;Article 9&lt;/strong&gt; pipeline, we simply uploaded artifacts. If the build produced a binary, we shipped it. In our updated &lt;code&gt;Jenkinsfile&lt;/code&gt;, we introduced a critical new step:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;time:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;unit:&lt;/span&gt; &lt;span class="s1"&gt;'MINUTES'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;waitForQualityGate&lt;/span&gt; &lt;span class="nl"&gt;abortPipeline:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This step is not a simple "sleep" command. It is a sophisticated, asynchronous state machine.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;The Handover:&lt;/strong&gt; When the &lt;code&gt;sonar-scanner&lt;/code&gt; finishes uploading the report, it leaves behind a metadata file (&lt;code&gt;report-task.txt&lt;/code&gt;) in the workspace. This file contains a &lt;strong&gt;Compute Engine Task ID (&lt;code&gt;ceTaskId&lt;/code&gt;)&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The Pause:&lt;/strong&gt; The &lt;code&gt;waitForQualityGate&lt;/code&gt; step reads this ID. It then puts the Jenkins pipeline into a "Paused" state. The heavy executor is released (depending on the agent configuration), and the job enters a lightweight listening mode.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The Processing:&lt;/strong&gt; On the SonarQube server, the Compute Engine processes the report, calculates the metrics, and compares them against the Quality Gate conditions.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The Callback:&lt;/strong&gt; Once the status is determined (OK or ERROR), SonarQube uses the &lt;strong&gt;Webhook&lt;/strong&gt; we configured in Chapter 5 to call back to Jenkins. It sends a JSON payload containing the status for that specific &lt;code&gt;ceTaskId&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The Decision:&lt;/strong&gt; Jenkins receives the webhook. If the payload says &lt;code&gt;status: "OK"&lt;/code&gt;, the pipeline resumes and proceeds to the &lt;strong&gt;Package&lt;/strong&gt; stage. If it says &lt;code&gt;status: "ERROR"&lt;/code&gt;, the &lt;code&gt;abortPipeline: true&lt;/code&gt; flag triggers an immediate build failure, stopping the conveyor belt before a bad artifact can be created.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  10.3 The "Stop the Line" Verification
&lt;/h2&gt;

&lt;p&gt;To prove that this system works, we must force a failure.&lt;/p&gt;

&lt;p&gt;However, triggering a failure naturally can be difficult in a new environment. The default "Sonar way" gate only checks &lt;strong&gt;New Code&lt;/strong&gt;. Since our coverage is currently high (94.1%), and we haven't introduced new bugs, our build is passing.&lt;/p&gt;

&lt;p&gt;To simulate a "Bad Build," we will create a draconian Quality Gate that demands perfection—&lt;strong&gt;100% Coverage&lt;/strong&gt;—and apply it to our project. Since we are at 94.1%, this is guaranteed to fail.&lt;/p&gt;

&lt;h3&gt;
  
  
  Action 1: Create the "Fail-Hard" Gate
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt; Log in to SonarQube (&lt;code&gt;http://sonarqube.cicd.local:9000&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt; Navigate to &lt;strong&gt;Quality Gates&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; Click &lt;strong&gt;Create&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;fail-hard&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Add Condition:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Where:&lt;/strong&gt; &lt;strong&gt;Overall Code&lt;/strong&gt; (We want to fail immediately based on current state).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quality Gate fails when:&lt;/strong&gt; Select &lt;strong&gt;Coverage&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Operator:&lt;/strong&gt; &lt;strong&gt;is less than&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Value:&lt;/strong&gt; &lt;code&gt;100.0&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; Click &lt;strong&gt;Add Condition&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Action 2: Enforce the Gate
&lt;/h3&gt;

&lt;p&gt;We must now assign this strict gate to our specific project.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Navigate to the &lt;strong&gt;&lt;code&gt;articles_...&lt;/code&gt;&lt;/strong&gt; Project Dashboard.&lt;/li&gt;
&lt;li&gt; Click &lt;strong&gt;Project Settings&lt;/strong&gt; &amp;gt; &lt;strong&gt;Quality Gate&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; Select &lt;strong&gt;Always use a specific Quality Gate&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; Choose &lt;strong&gt;&lt;code&gt;fail-hard&lt;/code&gt;&lt;/strong&gt; from the dropdown.&lt;/li&gt;
&lt;li&gt; Click &lt;strong&gt;Save&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Action 3: The Failed Build
&lt;/h3&gt;

&lt;p&gt;Go to Jenkins and trigger the &lt;strong&gt;&lt;code&gt;0004_std_lib_http_client&lt;/code&gt;&lt;/strong&gt; job again.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Observation:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; The &lt;strong&gt;Test &amp;amp; Coverage&lt;/strong&gt; stage will pass (Green).&lt;/li&gt;
&lt;li&gt; The &lt;strong&gt;Code Analysis&lt;/strong&gt; stage will start. The scanner will run.&lt;/li&gt;
&lt;li&gt; The pipeline will pause at &lt;code&gt;Checking status of SonarQube task...&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; A few seconds later, the pipeline will turn &lt;strong&gt;Red&lt;/strong&gt; and abort.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The Evidence:&lt;/strong&gt;&lt;br&gt;
If you check the Console Output, you will see:&lt;br&gt;
&lt;code&gt;SonarQube task '...' Quality gate is 'ERROR'&lt;/code&gt;&lt;br&gt;
&lt;code&gt;Stage "Package" skipped due to earlier failure(s)&lt;/code&gt;&lt;br&gt;
&lt;code&gt;Stage "Publish" skipped due to earlier failure(s)&lt;/code&gt;&lt;br&gt;
&lt;code&gt;ERROR: Pipeline aborted due to quality gate failure: ERROR&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Crucially, if you check &lt;strong&gt;Artifactory&lt;/strong&gt;, you will see that &lt;strong&gt;no new artifacts were uploaded&lt;/strong&gt; for this build number. The system worked. The "Inspector" stopped the line, preventing a non-compliant product from reaching the warehouse.&lt;/p&gt;

&lt;p&gt;You can now revert the Quality Gate setting in SonarQube back to &lt;strong&gt;"Sonar way"&lt;/strong&gt; to restore the passing state.&lt;/p&gt;

&lt;h2&gt;
  
  
  10.4 Conclusion
&lt;/h2&gt;

&lt;p&gt;We have reached a pivotal moment in our "City Planning."&lt;/p&gt;

&lt;p&gt;With the deployment of SonarQube and the enforcement of the Quality Gate, our infrastructure has evolved from a simple "build loop" into a sophisticated &lt;strong&gt;High-Assurance Software Supply Chain&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Let's review the architectural state of our city:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;The Library (GitLab):&lt;/strong&gt; Stores our blueprints securely.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The Factory (Jenkins):&lt;/strong&gt; Compiles our polyglot code using a precise, immutable toolchain (GCC 15/Python 3.12), resolving the binary incompatibility issues that plague manual builds.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The Inspector (SonarQube):&lt;/strong&gt; Analyzes the output using a custom-built image that trusts our internal PKI, ingesting data from three different languages (C++, Rust, Python) through a unified dashboard.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The Warehouse (Artifactory):&lt;/strong&gt; Receives &lt;em&gt;only&lt;/em&gt; those artifacts that have passed the Inspector's rigorous MQR standards.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We have solved the "Quantity over Quality" problem. We are no longer filling our warehouse with time bombs. If a developer commits code that leaks memory or fails tests, the factory line stops immediately. The artifact is rejected. The system protects itself.&lt;/p&gt;

&lt;p&gt;However, despite this sophistication, our city has one remaining flaw: &lt;strong&gt;It is silent.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When the Quality Gate slammed shut in our last test, the only way you knew was because you were staring at the Jenkins console. In a real team, developers push code and move on to the next task. They need to be notified &lt;em&gt;actively&lt;/em&gt; when something breaks. They need the city to speak to them.&lt;/p&gt;

&lt;p&gt;In the next article, we will install the "Public Address System" of our city. We will deploy &lt;strong&gt;Mattermost&lt;/strong&gt;, an open-source ChatOps platform. We will connect it to our Factory and our Inspector, ensuring that when the line stops, the entire engineering team gets the alert instantly.&lt;/p&gt;

</description>
      <category>cicd</category>
      <category>devops</category>
      <category>infrastructureascode</category>
      <category>sonarqube</category>
    </item>
    <item>
      <title>Part 05: Building a Sovereign Software Factory: Artifactory &amp; The "Strict TLS" Trap</title>
      <dc:creator>Warren Jitsing</dc:creator>
      <pubDate>Fri, 19 Dec 2025 05:16:26 +0000</pubDate>
      <link>https://dev.to/warren_jitsing_dd1c1d6fc6/part-05-building-a-sovereign-software-factory-artifactory-the-strict-tls-trap-184a</link>
      <guid>https://dev.to/warren_jitsing_dd1c1d6fc6/part-05-building-a-sovereign-software-factory-artifactory-the-strict-tls-trap-184a</guid>
      <description>&lt;p&gt;GitHub: &lt;a href="https://github.com/InfiniteConsult/0009_cicd_part05_artifactory" rel="noopener noreferrer"&gt;https://github.com/InfiniteConsult/0009_cicd_part05_artifactory&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; In this installment, we solve the "Incinerator Problem" where ephemeral build agents destroy valuable artifacts upon completion. We construct the &lt;strong&gt;"Secure Warehouse"&lt;/strong&gt; by deploying &lt;strong&gt;JFrog Artifactory&lt;/strong&gt;, but first, we must navigate the &lt;strong&gt;"Strict TLS" Trap&lt;/strong&gt;: a conflict between Artifactory's Go-based Router and standard OpenSSL certificates. We implement a custom "Strict Compliance" certificate generation process, enforce &lt;strong&gt;PostgreSQL 15+&lt;/strong&gt; schema security, and upgrade our Jenkins pipeline to package raw binaries into versioned "Products" (SDKs, Crates, Wheels) before shipping them to long-term storage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Sovereign Software Factory Series:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Part 01:&lt;/strong&gt; Building a Sovereign Software Factory: Docker Networking &amp;amp; Persistence&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 02:&lt;/strong&gt; Building a Sovereign Software Factory: The Local Root CA &amp;amp; Trust Chains&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 03:&lt;/strong&gt; Building a Sovereign Software Factory: Self-Hosted GitLab &amp;amp; Secrets Management&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 04:&lt;/strong&gt; Building a Sovereign Software Factory: Jenkins Configuration as Code (JCasC)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 05: Building a Sovereign Software Factory: Artifactory &amp;amp; The "Strict TLS" Trap&lt;/strong&gt; (You are here)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 06:&lt;/strong&gt; Building a Sovereign Software Factory: SonarQube Quality Gates&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 07:&lt;/strong&gt; Building a Sovereign Software Factory: ChatOps with Mattermost&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 08:&lt;/strong&gt; Building a Sovereign Software Factory: Observability with the ELK Stack&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 09:&lt;/strong&gt; Building a Sovereign Software Factory: Monitoring with Prometheus &amp;amp; Grafana&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 10:&lt;/strong&gt; Building a Sovereign Software Factory: The Python API Package (Capstone)&lt;/li&gt;
&lt;/ol&gt;




&lt;h1&gt;
  
  
  Chapter 1: The Challenge - The "Incinerator" Problem
&lt;/h1&gt;

&lt;h2&gt;
  
  
  1.1 The Current State: A Factory Without a Warehouse
&lt;/h2&gt;

&lt;p&gt;In the previous articles, we successfully established the two most visible pillars of our CI/CD city. We built the "Central Library" (GitLab) to store our blueprints, and we constructed the "Factory" (Jenkins) to execute our work.&lt;/p&gt;

&lt;p&gt;Our architecture is sound. We have a secure, "Control Center" (our &lt;code&gt;dev-container&lt;/code&gt;) that orchestrates everything. We have a private road network (&lt;code&gt;cicd-net&lt;/code&gt;) that allows our services to communicate securely over HTTPS using our own internal Certificate Authority. Most importantly, we have a working pipeline. When a developer pushes code to GitLab, a webhook fires, the Jenkins "Foreman" hires a "General Purpose Worker" (our custom Docker agent), and that worker successfully compiles our complex, polyglot "Hero Project."&lt;/p&gt;

&lt;p&gt;If you look at the Jenkins dashboard from our last session, you will see a green status. The tests passed. The C++ library (&lt;code&gt;libhttpc.so&lt;/code&gt;), the Rust binary (&lt;code&gt;httprust&lt;/code&gt;), and the Python wheel (&lt;code&gt;.whl&lt;/code&gt;) were all successfully created.&lt;/p&gt;

&lt;p&gt;However, despite this success, our system has a critical architectural flaw. We have built a factory that incinerates its products immediately after manufacturing them.&lt;/p&gt;

&lt;p&gt;Because our Jenkins agents are ephemeral containers designed to provide a clean slate for every build, they are destroyed the moment the pipeline finishes. When the container is removed, every artifact inside it—the very software we aim to deliver—is deleted along with it. We are verifying the &lt;em&gt;process&lt;/em&gt;, but we are failing to capture the &lt;em&gt;product&lt;/em&gt;. We have a factory, but we have no warehouse.&lt;/p&gt;

&lt;h2&gt;
  
  
  1.2 The "Incinerator" Pain Point (Compute vs. Storage)
&lt;/h2&gt;

&lt;p&gt;To understand why this happens, we must distinguish between the &lt;strong&gt;Compute Plane&lt;/strong&gt; and the &lt;strong&gt;Storage Plane&lt;/strong&gt; of our infrastructure.&lt;/p&gt;

&lt;p&gt;Jenkins is a Compute engine. Its agents are designed to be ephemeral "cattle," not persistent "pets." This is a deliberate architectural choice. We want every build to start with a blank slate—a fresh filesystem, no leftover temporary files, and no side effects from previous runs—to guarantee reproducibility. If a build works on Monday, it must work on Tuesday, regardless of what happened in between.&lt;/p&gt;

&lt;p&gt;To achieve this, the Docker Plugin destroys the agent container the instant the job completes. This is excellent for hygiene but catastrophic for delivery. By destroying the container, we are effectively incinerating the finished goods.&lt;/p&gt;

&lt;p&gt;We are currently stuck in a loop of "verification without retention." We spend compute cycles to compile C++ code, link Rust binaries, and package Python wheels, only to verify that they &lt;em&gt;can&lt;/em&gt; be built before throwing them away. We cannot deploy these binaries to a staging environment because they no longer exist. We cannot debug a production crash by inspecting the exact version that failed because that file is gone.&lt;/p&gt;

&lt;p&gt;We need a dedicated &lt;strong&gt;Storage Plane&lt;/strong&gt;. We need a system designed to persist, organize, and secure these binary assets long after the compute resources that created them have vanished.&lt;/p&gt;

&lt;h2&gt;
  
  
  1.3 The "Storage Plane": Why not just commit to Git?
&lt;/h2&gt;

&lt;p&gt;The most common reaction to this problem is to ask: "Why do we need another server? We already have GitLab. Why not just commit the compiled binaries to the repository?"&lt;/p&gt;

&lt;p&gt;This is a seductive anti-pattern. It seems efficient to keep the source and the build output together, but it fundamentally misunderstands the mechanics of version control. We must distinguish between the "Filing Cabinet" (Git) and the "Warehouse" (Artifactory).&lt;/p&gt;

&lt;p&gt;Git is a "Time Machine" optimized for text. When you change a line of code, Git calculates the difference (the delta) and stores only that tiny change. It is incredibly efficient at tracking the evolution of text files over time.&lt;/p&gt;

&lt;p&gt;Binaries, however, are opaque blobs. If you recompile a 50MB executable, almost every bit in that file changes. Git cannot calculate a meaningful delta, so it must store a fresh, full copy of that 50MB file every single time you commit. If you build ten times a day, your repository grows by 500MB a day. Within a month, your agile "Filing Cabinet" is stuffed with heavy machinery. &lt;code&gt;git clone&lt;/code&gt; operations that used to take seconds will start taking hours, choking the network and slowing down every developer on the team.&lt;/p&gt;

&lt;p&gt;We need a system designed for heavy lifting. We need a "Warehouse" optimized for storing large, immutable, checksum-based artifacts, not line-by-line text diffs.&lt;/p&gt;

&lt;h2&gt;
  
  
  1.4 The Architectural Decision: Why Not Use GitLab's Registry?
&lt;/h2&gt;

&lt;p&gt;Before we break ground on a new building, we must address the elephant in the room. Our GitLab container actually comes with a built-in "Package Registry." Why are we incurring the architectural cost of deploying &lt;strong&gt;JFrog Artifactory&lt;/strong&gt;, a completely separate, resource-intensive application?&lt;/p&gt;

&lt;p&gt;We are choosing a dedicated component for four specific architectural reasons that align with our "City Planning" philosophy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. The "Universal Bucket" (Simplicity &amp;amp; Licensing)&lt;/strong&gt;&lt;br&gt;
Our "Hero Project" is polyglot. It produces C/C++ libraries, Rust binaries, and Python wheels. To store these in GitLab's registry, we would need to configure three separate, complex interactions: a PyPI upload for Python, a Cargo registry configuration for Rust, and a Generic upload for C++.&lt;/p&gt;

&lt;p&gt;However, there is a pragmatic constraint. We are using the free &lt;strong&gt;Artifactory OSS&lt;/strong&gt; version. This version is strictly limited in the package types it supports (primarily Maven, Gradle, and Ivy). It &lt;strong&gt;does not&lt;/strong&gt; natively support PyPI, Cargo, or Conan repositories.&lt;/p&gt;

&lt;p&gt;Therefore, using Artifactory's &lt;strong&gt;Generic Repository&lt;/strong&gt; is not just a simplification; it is a necessity. It allows us to treat all our polyglot artifacts—tarballs, crates, and wheels—as simple files in a folder structure. It is a "Simple Bucket" that lets us focus on the pipeline flow first without needing the paid Enterprise features for specific language protocols.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Decoupling (Resilience)&lt;/strong&gt;&lt;br&gt;
We are building a modular city. Ideally, the buildings should be independent. If we decide to demolish our Library (switch from GitLab to GitHub) in the future, our Warehouse should remain standing. By bundling our artifacts inside our SCM, we create vendor lock-in. Separating them ensures that our asset storage is independent of our source control choice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The "Bunker" Foundation (Virtual Repositories)&lt;/strong&gt;&lt;br&gt;
We are simulating a high-security "Air Gapped" environment. While we aren't configuring remote proxies today, deploying Artifactory lays the foundation for its superpower: &lt;strong&gt;Virtual Repositories&lt;/strong&gt;. This allows a single URL to transparently aggregate local internal artifacts &lt;em&gt;and&lt;/em&gt; proxied remote content (like Maven Central). This is the industry standard for secure supply chains, and we are pouring the foundation for it now.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. The "Chain of Custody" (Build Info)&lt;/strong&gt;&lt;br&gt;
GitLab Releases are designed for humans; they provide download links. Artifactory &lt;strong&gt;Build Info&lt;/strong&gt; is designed for machines; it provides forensic audit trails.&lt;/p&gt;

&lt;p&gt;We need to capture more than just the file. We need to capture the &lt;strong&gt;context&lt;/strong&gt;. The Build Info object links the specific SHA256 checksum of a binary back to the Git Commit hash, the Jenkins Build ID, the environment variables, and the dependencies used to create it. This creates a "Chain of Custody" that turns a random file into a trusted supply chain asset.&lt;/p&gt;
&lt;h1&gt;
  
  
  Chapter 2: The Architecture - Designing the Warehouse
&lt;/h1&gt;
&lt;h2&gt;
  
  
  2.1 The Database Pivot: "City Infrastructure" vs. "Private Utilities"
&lt;/h2&gt;

&lt;p&gt;Before we can deploy the Artifactory container, we must make a fundamental architectural decision regarding its data storage. Unlike Jenkins, which stores its configuration and job history as flat XML files on the filesystem, Artifactory relies heavily on a transactional database to manage metadata, security tokens, and package indexing.&lt;/p&gt;

&lt;p&gt;By default, Artifactory ships with an embedded Apache Derby database. This is designed for "zero-config" trials: you run the container, the database spins up inside the same JVM process, and the application starts. While convenient for a five-minute test, this "Private Utility" model is architecturally unsound for a persistent, professional environment.&lt;/p&gt;

&lt;p&gt;The embedded database introduces significant risks. It runs within the application container, meaning if the container crashes or is killed abruptly (a common occurrence in Docker development), the database often fails to close its lock files or flush its transaction logs, leading to corruption. Furthermore, in the specific context of Artifactory 7.x running in Docker, the embedded database has known stability issues that can cause boot loops or data loss during upgrades.&lt;/p&gt;

&lt;p&gt;We will reject this default. Instead of allowing every new building in our city to drill its own private well, we will build a centralized water treatment plant. We will deploy &lt;strong&gt;PostgreSQL 17&lt;/strong&gt; as a first-class piece of "City Infrastructure."&lt;/p&gt;

&lt;p&gt;This decision pays dividends beyond just Artifactory. By establishing a robust, SSL-secured, shared database service now, we are laying the foundation for the rest of our stack. When we deploy &lt;strong&gt;SonarQube&lt;/strong&gt; (for code quality), &lt;strong&gt;Mattermost&lt;/strong&gt; (for ChatOps), and &lt;strong&gt;Grafana&lt;/strong&gt; (for monitoring) in future articles, they will not need their own private databases. They will simply plug into this existing, high-performance infrastructure. This reduces our resource footprint and centralizes our backup and security strategy to a single, manageable point.&lt;/p&gt;
&lt;h2&gt;
  
  
  2.2 The Security Shift: Navigating PostgreSQL 15+
&lt;/h2&gt;

&lt;p&gt;Choosing to deploy a modern database comes with modern responsibilities. We are deploying &lt;strong&gt;PostgreSQL 17&lt;/strong&gt;, the latest stable version supported by Artifactory and SonarQube. This forces us to confront a significant "breaking change" introduced in version 15 that alters the default security posture of the database.&lt;/p&gt;

&lt;p&gt;For decades, PostgreSQL had a permissive default: any user connected to a database had implicit permission to create tables in the default &lt;code&gt;public&lt;/code&gt; schema. This was convenient for developers but presented a security risk—a compromised low-privilege account could fill the database with garbage data or malicious tables.&lt;/p&gt;

&lt;p&gt;In PostgreSQL 15, this default was revoked. The &lt;code&gt;public&lt;/code&gt; schema is now owned strictly by the database owner, and the &lt;code&gt;PUBLIC&lt;/code&gt; role (representing all users) no longer has &lt;code&gt;CREATE&lt;/code&gt; privileges.&lt;/p&gt;

&lt;p&gt;This shift breaks the "lazy" setup scripts found in many older tutorials. If we simply create an &lt;code&gt;artifactory&lt;/code&gt; user and a database, the application will crash on startup with "Permission Denied" errors when it attempts to initialize its schema.&lt;/p&gt;

&lt;p&gt;To navigate this, we must adopt a &lt;strong&gt;"Zero Trust"&lt;/strong&gt; mindset in our initialization architecture. We cannot rely on implicit permissions. Our setup scripts must now be explicit, executing specific &lt;code&gt;GRANT CREATE, USAGE ON SCHEMA public&lt;/code&gt; commands for each service user. This ensures that our database environment remains secure by default, with every privilege intentionally defined rather than accidentally inherited.&lt;/p&gt;
&lt;h2&gt;
  
  
  2.3 The "Microservice Explosion" (Artifactory 7 vs. 6)
&lt;/h2&gt;

&lt;p&gt;If you have used older versions of Artifactory (v6 and below), you might remember it as a standard Java web application running inside Apache Tomcat. You deployed a &lt;code&gt;.war&lt;/code&gt; file, mapped port 8081, and you were done.&lt;/p&gt;

&lt;p&gt;Artifactory 7 abandoned this monolithic architecture in favor of a scalable, cloud-native design. It is no longer a single application; it is a &lt;strong&gt;cluster of microservices&lt;/strong&gt; running inside a single container.&lt;/p&gt;

&lt;p&gt;When we launch our container, we aren't just starting a web app; we are spinning up an entire internal service mesh:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Artifactory (Service):&lt;/strong&gt; The core artifact management engine (Java).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Access:&lt;/strong&gt; A dedicated security service that handles authentication, tokens, and permissions (Java).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Metadata:&lt;/strong&gt; A service for indexing and calculating metadata for packages (Java).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Frontend:&lt;/strong&gt; The web UI service.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Router:&lt;/strong&gt; The API Gateway and service registry (written in Go, based on Traefik).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This architectural shift creates new complexity for our "City Planning." We can no longer simply talk to the Tomcat backend on port 8081. Instead, all traffic must flow through the &lt;strong&gt;Router&lt;/strong&gt; on &lt;strong&gt;port 8082&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The Router is the traffic cop. It acts as the entry point for the "City," terminating SSL and directing requests to the correct internal microservice. This creates a complex internal environment where these services must communicate with each other securely over &lt;code&gt;localhost&lt;/code&gt;. If this internal network is misconfigured, the Router will fail to connect to the Access service, and the entire container will enter a boot loop.&lt;/p&gt;
&lt;h2&gt;
  
  
  2.4 The TLS Trap: "Strict Compliance" (Go vs. OpenSSL)
&lt;/h2&gt;

&lt;p&gt;The introduction of the Go-based Router brings a subtle but critical compatibility challenge regarding our Public Key Infrastructure (PKI). In Article 2, we built a standard "Passport Printer" script using OpenSSL. We used this to generate certificates for GitLab (Nginx) and Jenkins (Jetty), and both accepted them without complaint.&lt;/p&gt;

&lt;p&gt;However, the Artifactory Router is different. It is built on the Go programming language's standard library (&lt;code&gt;crypto/x509&lt;/code&gt;), which is notorious in the DevOps community for its strict, unforgiving adherence to RFC 5280 standards. While C-based libraries like OpenSSL are often permissive—ignoring minor spec violations—Go will reject a certificate that is technically imperfect.&lt;/p&gt;

&lt;p&gt;This creates a "TLS Trap." If we try to reuse our standard certificate generation script for Artifactory, the Router will fail to start, often with cryptic "handshake failure" or "unhandled critical extension" errors.&lt;/p&gt;

&lt;p&gt;The issue lies in the &lt;strong&gt;Key Usage&lt;/strong&gt; extensions.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Criticality:&lt;/strong&gt; The RFC suggests that if a certificate defines specific Key Usages (like Digital Signature), that extension should be marked &lt;strong&gt;CRITICAL&lt;/strong&gt;. Go enforces this; many others do not.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Dual Role (mTLS):&lt;/strong&gt; Because the Router acts as a &lt;strong&gt;Server&lt;/strong&gt; (accepting traffic from your browser) &lt;em&gt;and&lt;/em&gt; a &lt;strong&gt;Client&lt;/strong&gt; (sending traffic to internal microservices like Access), it requires a certificate that explicitly permits &lt;em&gt;both&lt;/em&gt; roles. It needs &lt;code&gt;ExtendedKeyUsage&lt;/code&gt; values for &lt;code&gt;serverAuth&lt;/code&gt; and &lt;code&gt;clientAuth&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To solve this, we cannot use our existing tools. We must engineer a bespoke "Strict Compliance" certificate generation process specifically for Artifactory, ensuring every flag is set exactly as the Go library expects.&lt;/p&gt;
&lt;h2&gt;
  
  
  2.5 The "Hybrid Persistence" Pattern
&lt;/h2&gt;

&lt;p&gt;Finally, we must address how we store the data. Artifactory creates massive amounts of binary data. Storing this in a standard Docker volume is best practice for performance and safety (preventing accidental deletion). However, Artifactory also relies on complex configuration files (like &lt;code&gt;system.yaml&lt;/code&gt;) that we need to edit frequently from our host machine.&lt;/p&gt;

&lt;p&gt;If we mount a Docker volume to the data directory, the configuration files are buried inside the volume, inaccessible to our host's text editors. If we bind-mount a host directory, we expose the database files to potential permission corruption by the host OS.&lt;/p&gt;

&lt;p&gt;To resolve this, we will use a &lt;strong&gt;Hybrid Persistence&lt;/strong&gt; pattern using layered mounts.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;The Foundation (Volume):&lt;/strong&gt; We will mount a Docker-managed volume (&lt;code&gt;artifactory-data&lt;/code&gt;) to the root application directory &lt;code&gt;/var/opt/jfrog/artifactory&lt;/code&gt;. This handles the heavy, opaque data storage safely.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The Overlays (Bind Mounts):&lt;/strong&gt; We will then bind-mount specific subdirectories from our host (&lt;code&gt;~/cicd_stack/artifactory/var/etc&lt;/code&gt;) &lt;em&gt;over&lt;/em&gt; the corresponding directories inside the container.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This strategy gives us the best of both worlds: the robustness of a managed volume for the "blob" storage, and the convenience of host-side editing for our configuration files.&lt;/p&gt;
&lt;h1&gt;
  
  
  Chapter 3: Action Plan (Part 1) - The Shared Data Layer
&lt;/h1&gt;
&lt;h2&gt;
  
  
  3.1 The Architect (&lt;code&gt;01-setup-database.sh&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;We begin by establishing our shared data infrastructure. This script acts as the "Architect" for our database service. It runs entirely on the host machine and is responsible for preparing the filesystem, generating cryptographic secrets, and defining the security policies that the database container will enforce upon startup.&lt;/p&gt;

&lt;p&gt;This script creates a directory structure at &lt;code&gt;~/cicd_stack/postgres&lt;/code&gt;, populating it with SSL certificates, configuration files, and initialization scripts. It solves the "Bootstrap Paradox" by ensuring that all passwords and permissions exist &lt;em&gt;before&lt;/em&gt; the database process is ever spawned.&lt;/p&gt;

&lt;p&gt;Create this file at &lt;code&gt;~/cicd_stack/postgres/01-setup-database.sh&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;#               01-setup-database.sh&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  This is the "Architect" script for the Shared Database.&lt;/span&gt;
&lt;span class="c"&gt;#  It prepares the host environment for PostgreSQL 17.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  1. Secrets: Generates/Persists passwords for all 4 services.&lt;/span&gt;
&lt;span class="c"&gt;#  2. Certs: Issues SSL certs and fixes permissions (UID 999).&lt;/span&gt;
&lt;span class="c"&gt;#  3. Security: Generates pg_hba.conf enforcing SSL for cicd-net.&lt;/span&gt;
&lt;span class="c"&gt;#  4. Init: Creates the SQL script to initialize databases with&lt;/span&gt;
&lt;span class="c"&gt;#           PostgreSQL 17 compatible permissions.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="c"&gt;# --- 1. Define Paths ---&lt;/span&gt;
&lt;span class="nv"&gt;HOST_CICD_ROOT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/cicd_stack"&lt;/span&gt;
&lt;span class="nv"&gt;POSTGRES_BASE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOST_CICD_ROOT&lt;/span&gt;&lt;span class="s2"&gt;/postgres"&lt;/span&gt;
&lt;span class="nv"&gt;MASTER_ENV_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOST_CICD_ROOT&lt;/span&gt;&lt;span class="s2"&gt;/cicd.env"&lt;/span&gt;

&lt;span class="c"&gt;# Directories to be mounted&lt;/span&gt;
&lt;span class="nv"&gt;DIR_CERTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$POSTGRES_BASE&lt;/span&gt;&lt;span class="s2"&gt;/certs"&lt;/span&gt;
&lt;span class="nv"&gt;DIR_CONFIG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$POSTGRES_BASE&lt;/span&gt;&lt;span class="s2"&gt;/config"&lt;/span&gt;
&lt;span class="nv"&gt;DIR_INIT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$POSTGRES_BASE&lt;/span&gt;&lt;span class="s2"&gt;/init"&lt;/span&gt;

&lt;span class="c"&gt;# Certificate Authority Paths&lt;/span&gt;
&lt;span class="nv"&gt;CA_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOST_CICD_ROOT&lt;/span&gt;&lt;span class="s2"&gt;/ca"&lt;/span&gt;
&lt;span class="nv"&gt;SERVICE_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"postgres.cicd.local"&lt;/span&gt;
&lt;span class="nv"&gt;CA_SERVICE_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CA_DIR&lt;/span&gt;&lt;span class="s2"&gt;/pki/services/&lt;/span&gt;&lt;span class="nv"&gt;$SERVICE_NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;SRC_CRT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CA_SERVICE_DIR&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$SERVICE_NAME&lt;/span&gt;&lt;span class="s2"&gt;.crt.pem"&lt;/span&gt;
&lt;span class="nv"&gt;SRC_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CA_SERVICE_DIR&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$SERVICE_NAME&lt;/span&gt;&lt;span class="s2"&gt;.key.pem"&lt;/span&gt;
&lt;span class="nv"&gt;SRC_ROOT_CA&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CA_DIR&lt;/span&gt;&lt;span class="s2"&gt;/pki/certs/ca.pem"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Starting PostgreSQL 'Architect' Setup..."&lt;/span&gt;

&lt;span class="c"&gt;# --- 2. Secrets Management ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Phase 1: Secrets Management ---"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: Master env file not found at &lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Helper function to check and generate secret&lt;/span&gt;
check_and_generate_secret&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;var_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;var_value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="p"&gt;!var_name&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$var_value&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Generating new &lt;/span&gt;&lt;span class="nv"&gt;$description&lt;/span&gt;&lt;span class="s2"&gt; (&lt;/span&gt;&lt;span class="nv"&gt;$var_name&lt;/span&gt;&lt;span class="s2"&gt;)..."&lt;/span&gt;
        &lt;span class="c"&gt;# 32 bytes of entropy = 64 hex characters&lt;/span&gt;
        &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;new_secret&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;openssl rand &lt;span class="nt"&gt;-hex&lt;/span&gt; 32&lt;span class="si"&gt;)&lt;/span&gt;

        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"# &lt;/span&gt;&lt;span class="nv"&gt;$description&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$var_name&lt;/span&gt;&lt;span class="s2"&gt;=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$new_secret&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

        &lt;span class="c"&gt;# Export for current session&lt;/span&gt;
        &lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;$var_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$new_secret&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;else
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Found existing &lt;/span&gt;&lt;span class="nv"&gt;$var_name&lt;/span&gt;&lt;span class="s2"&gt;."&lt;/span&gt;
    &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

check_and_generate_secret &lt;span class="s2"&gt;"POSTGRES_ROOT_PASSWORD"&lt;/span&gt; &lt;span class="s2"&gt;"PostgreSQL Root Password"&lt;/span&gt;
check_and_generate_secret &lt;span class="s2"&gt;"ARTIFACTORY_DB_PASSWORD"&lt;/span&gt; &lt;span class="s2"&gt;"Artifactory DB Password"&lt;/span&gt;
check_and_generate_secret &lt;span class="s2"&gt;"SONARQUBE_DB_PASSWORD"&lt;/span&gt; &lt;span class="s2"&gt;"SonarQube DB Password"&lt;/span&gt;
check_and_generate_secret &lt;span class="s2"&gt;"MATTERMOST_DB_PASSWORD"&lt;/span&gt; &lt;span class="s2"&gt;"Mattermost DB Password"&lt;/span&gt;
check_and_generate_secret &lt;span class="s2"&gt;"GRAFANA_DB_PASSWORD"&lt;/span&gt; &lt;span class="s2"&gt;"Grafana DB Password"&lt;/span&gt;

&lt;span class="c"&gt;# --- 3. Certificate Preparation ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Phase 2: TLS Certificate Preparation ---"&lt;/span&gt;

&lt;span class="c"&gt;# Ensure local directories exist&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DIR_CERTS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DIR_CONFIG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DIR_INIT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Check if cert already exists in the CA structure&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SRC_CRT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Certificate for &lt;/span&gt;&lt;span class="nv"&gt;$SERVICE_NAME&lt;/span&gt;&lt;span class="s2"&gt; not found in CA. Issuing new certificate..."&lt;/span&gt;

    &lt;span class="c"&gt;# Use a subshell to change directory without affecting the script&lt;/span&gt;
    &lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="nb"&gt;cd&lt;/span&gt; ../0006_cicd_part02_certificate_authority &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1
        &lt;span class="c"&gt;# Check if the script exists&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-x&lt;/span&gt; &lt;span class="s2"&gt;"./02-issue-service-cert.sh"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
            &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: Cert issuance script not found or not executable."&lt;/span&gt;
            &lt;span class="nb"&gt;exit &lt;/span&gt;1
        &lt;span class="k"&gt;fi&lt;/span&gt;
        ./02-issue-service-cert.sh &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SERVICE_NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Certificate for &lt;/span&gt;&lt;span class="nv"&gt;$SERVICE_NAME&lt;/span&gt;&lt;span class="s2"&gt; already exists. Skipping issuance."&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Copy certificates to the Postgres mount directory&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Copying certificates to &lt;/span&gt;&lt;span class="nv"&gt;$DIR_CERTS&lt;/span&gt;&lt;span class="s2"&gt;..."&lt;/span&gt;
&lt;span class="c"&gt;# We rename them to standard postgres names for simplicity in the run command&lt;/span&gt;
&lt;span class="nb"&gt;sudo cp&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SRC_CRT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DIR_CERTS&lt;/span&gt;&lt;span class="s2"&gt;/server.crt"&lt;/span&gt;
&lt;span class="nb"&gt;sudo cp&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SRC_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DIR_CERTS&lt;/span&gt;&lt;span class="s2"&gt;/server.key"&lt;/span&gt;
&lt;span class="nb"&gt;sudo cp&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SRC_ROOT_CA&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DIR_CERTS&lt;/span&gt;&lt;span class="s2"&gt;/root.crt"&lt;/span&gt;

&lt;span class="c"&gt;# CRITICAL: Permission Fix for Container UID 999&lt;/span&gt;
&lt;span class="c"&gt;# PostgreSQL refuses to start if the key file is readable by anyone else.&lt;/span&gt;
&lt;span class="c"&gt;# The container user 'postgres' usually has UID 999.&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Applying strict permissions to SSL keys (requires sudo)..."&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Setting ownership to UID 999 and mode 0600."&lt;/span&gt;

&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; 999:999 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DIR_CERTS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;600 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DIR_CERTS&lt;/span&gt;&lt;span class="s2"&gt;/server.key"&lt;/span&gt;
&lt;span class="c"&gt;# Public certs can be readable&lt;/span&gt;
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;644 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DIR_CERTS&lt;/span&gt;&lt;span class="s2"&gt;/server.crt"&lt;/span&gt;
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;644 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DIR_CERTS&lt;/span&gt;&lt;span class="s2"&gt;/root.crt"&lt;/span&gt;


&lt;span class="c"&gt;# --- 4. Security Policy (pg_hba.conf) ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Phase 3: Generating pg_hba.conf ---"&lt;/span&gt;
&lt;span class="nv"&gt;HBA_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DIR_CONFIG&lt;/span&gt;&lt;span class="s2"&gt;/pg_hba.conf"&lt;/span&gt;

&lt;span class="c"&gt;# We explicitly restrict access to the docker subnet (172.30.0.0/24)&lt;/span&gt;
&lt;span class="c"&gt;# and enforce SSL (hostssl).&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt; "&lt;/span&gt;&lt;span class="nv"&gt;$HBA_FILE&lt;/span&gt;&lt;span class="sh"&gt;"
# TYPE  DATABASE        USER            ADDRESS                 METHOD

# 1. Localhost (Loopback) - Allow for local debugging/healthchecks
local   all             all                                     trust
hostssl all             all             127.0.0.1/32            scram-sha-256

# 2. CICD Network - Reject unencrypted connections
hostnossl all           all             172.30.0.0/24           reject

# 3. CICD Network - Allow SSL connections with password
hostssl all             all             172.30.0.0/24           scram-sha-256

# 4. Reject everything else (Implicit, but good for documentation)
# host    all             all             0.0.0.0/0               reject
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Policy written to &lt;/span&gt;&lt;span class="nv"&gt;$HBA_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;


&lt;span class="c"&gt;# --- 5. Init Script Generation ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Phase 4: Generating Initialization SQL ---"&lt;/span&gt;
&lt;span class="nv"&gt;INIT_SCRIPT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DIR_INIT&lt;/span&gt;&lt;span class="s2"&gt;/01-init.sh"&lt;/span&gt;

&lt;span class="c"&gt;# We use a shell script that calls psql. This allows us to use variables.&lt;/span&gt;
&lt;span class="c"&gt;# Note: We use 'cat &amp;lt;&amp;lt; "EOF"' (quoted EOF) to prevent variable expansion&lt;/span&gt;
&lt;span class="c"&gt;# by the host shell during generation. The variables ($POSTGRES_USER, etc.)&lt;/span&gt;
&lt;span class="c"&gt;# will be expanded by the CONTAINER shell at runtime.&lt;/span&gt;

&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;" &amp;gt; "&lt;/span&gt;&lt;span class="nv"&gt;$INIT_SCRIPT&lt;/span&gt;&lt;span class="sh"&gt;"
#!/bin/bash
set -e

echo "--- Initializing Multi-Tenant Databases ---"

# Define constants for PG17 Compatibility
# We use 'C' collation to satisfy SonarQube's strict case-sensitivity requirement
# while maintaining compatibility with Artifactory, Mattermost, and Grafana.
DB_ENCODING='UTF8'
DB_COLLATE='C'
DB_CTYPE='C'

# Execute SQL block
psql -v ON_ERROR_STOP=1 --username "&lt;/span&gt;&lt;span class="nv"&gt;$POSTGRES_USER&lt;/span&gt;&lt;span class="sh"&gt;" --dbname "&lt;/span&gt;&lt;span class="nv"&gt;$POSTGRES_DB&lt;/span&gt;&lt;span class="sh"&gt;" &amp;lt;&amp;lt;-EOSQL

    -- ===================================================
    -- 1. ARTIFACTORY
    -- ===================================================
    CREATE USER artifactory WITH PASSWORD '&lt;/span&gt;&lt;span class="nv"&gt;$ARTIFACTORY_DB_PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;';
    CREATE DATABASE artifactory WITH OWNER artifactory ENCODING='&lt;/span&gt;&lt;span class="nv"&gt;$DB_ENCODING&lt;/span&gt;&lt;span class="sh"&gt;' LC_COLLATE='&lt;/span&gt;&lt;span class="nv"&gt;$DB_COLLATE&lt;/span&gt;&lt;span class="sh"&gt;' LC_CTYPE='&lt;/span&gt;&lt;span class="nv"&gt;$DB_CTYPE&lt;/span&gt;&lt;span class="sh"&gt;' TEMPLATE=template0;
    GRANT ALL PRIVILEGES ON DATABASE artifactory TO artifactory;
    -- PG17 Fix: Explicitly grant creation on public schema
    GRANT CREATE, USAGE ON SCHEMA public TO artifactory;

    -- ===================================================
    -- 2. SONARQUBE
    -- ===================================================
    CREATE USER sonarqube WITH PASSWORD '&lt;/span&gt;&lt;span class="nv"&gt;$SONARQUBE_DB_PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;';
    CREATE DATABASE sonarqube WITH OWNER sonarqube ENCODING='&lt;/span&gt;&lt;span class="nv"&gt;$DB_ENCODING&lt;/span&gt;&lt;span class="sh"&gt;' LC_COLLATE='&lt;/span&gt;&lt;span class="nv"&gt;$DB_COLLATE&lt;/span&gt;&lt;span class="sh"&gt;' LC_CTYPE='&lt;/span&gt;&lt;span class="nv"&gt;$DB_CTYPE&lt;/span&gt;&lt;span class="sh"&gt;' TEMPLATE=template0;
    GRANT ALL PRIVILEGES ON DATABASE sonarqube TO sonarqube;
    -- PG17 Fix: Explicitly grant creation on public schema
    GRANT CREATE, USAGE ON SCHEMA public TO sonarqube;

    -- ===================================================
    -- 3. MATTERMOST
    -- ===================================================
    CREATE USER mattermost WITH PASSWORD '&lt;/span&gt;&lt;span class="nv"&gt;$MATTERMOST_DB_PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;';
    CREATE DATABASE mattermost WITH OWNER mattermost ENCODING='&lt;/span&gt;&lt;span class="nv"&gt;$DB_ENCODING&lt;/span&gt;&lt;span class="sh"&gt;' LC_COLLATE='&lt;/span&gt;&lt;span class="nv"&gt;$DB_COLLATE&lt;/span&gt;&lt;span class="sh"&gt;' LC_CTYPE='&lt;/span&gt;&lt;span class="nv"&gt;$DB_CTYPE&lt;/span&gt;&lt;span class="sh"&gt;' TEMPLATE=template0;
    GRANT ALL PRIVILEGES ON DATABASE mattermost TO mattermost;
    -- PG17 Fix: Explicitly grant creation on public schema
    GRANT CREATE, USAGE ON SCHEMA public TO mattermost;

    -- ===================================================
    -- 4. GRAFANA
    -- ===================================================
    CREATE USER grafana WITH PASSWORD '&lt;/span&gt;&lt;span class="nv"&gt;$GRAFANA_DB_PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;';
    CREATE DATABASE grafana WITH OWNER grafana ENCODING='&lt;/span&gt;&lt;span class="nv"&gt;$DB_ENCODING&lt;/span&gt;&lt;span class="sh"&gt;' LC_COLLATE='&lt;/span&gt;&lt;span class="nv"&gt;$DB_COLLATE&lt;/span&gt;&lt;span class="sh"&gt;' LC_CTYPE='&lt;/span&gt;&lt;span class="nv"&gt;$DB_CTYPE&lt;/span&gt;&lt;span class="sh"&gt;' TEMPLATE=template0;
    GRANT ALL PRIVILEGES ON DATABASE grafana TO grafana;
    -- PG17 Fix: Explicitly grant creation on public schema
    GRANT CREATE, USAGE ON SCHEMA public TO grafana;

EOSQL

echo "--- Database Initialization Complete ---"
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# Make the init script executable&lt;/span&gt;
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$INIT_SCRIPT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Init script written to &lt;/span&gt;&lt;span class="nv"&gt;$INIT_SCRIPT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;


&lt;span class="c"&gt;# --- 6. Create Scoped Environment File ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Phase 5: Creating Scoped postgres.env ---"&lt;/span&gt;
&lt;span class="nv"&gt;SCOPED_ENV_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$POSTGRES_BASE&lt;/span&gt;&lt;span class="s2"&gt;/postgres.env"&lt;/span&gt;

&lt;span class="c"&gt;# We map the variables from our Master Env (Host) to the specific&lt;/span&gt;
&lt;span class="c"&gt;# variable names expected by the Postgres Image and our Init Script.&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt; "&lt;/span&gt;&lt;span class="nv"&gt;$SCOPED_ENV_FILE&lt;/span&gt;&lt;span class="sh"&gt;"
# Scoped Environment for PostgreSQL Container
# Auto-generated by 01-setup-database.sh

# 1. Standard PostgreSQL Image Variables
# Note: We map our POSTGRES_ROOT_PASSWORD to POSTGRES_PASSWORD
POSTGRES_PASSWORD=&lt;/span&gt;&lt;span class="nv"&gt;$POSTGRES_ROOT_PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;
POSTGRES_USER=postgres
POSTGRES_DB=postgres

# 2. Application Passwords
# These are required by /docker-entrypoint-initdb.d/01-init.sh
ARTIFACTORY_DB_PASSWORD=&lt;/span&gt;&lt;span class="nv"&gt;$ARTIFACTORY_DB_PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;
SONARQUBE_DB_PASSWORD=&lt;/span&gt;&lt;span class="nv"&gt;$SONARQUBE_DB_PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;
MATTERMOST_DB_PASSWORD=&lt;/span&gt;&lt;span class="nv"&gt;$MATTERMOST_DB_PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;
GRAFANA_DB_PASSWORD=&lt;/span&gt;&lt;span class="nv"&gt;$GRAFANA_DB_PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# Secure the file (contains cleartext passwords)&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;600 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SCOPED_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Scoped env file written to &lt;/span&gt;&lt;span class="nv"&gt;$SCOPED_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;


&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Setup Complete ---"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Secrets persisted in cicd.env"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Scoped secrets created in &lt;/span&gt;&lt;span class="nv"&gt;$SCOPED_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Certificates prepared in &lt;/span&gt;&lt;span class="nv"&gt;$DIR_CERTS&lt;/span&gt;&lt;span class="s2"&gt; (UID 999)"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Config generated in &lt;/span&gt;&lt;span class="nv"&gt;$DIR_CONFIG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Ready to run 02-deploy-database.sh"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deconstructing the Architect
&lt;/h3&gt;

&lt;p&gt;This script performs five critical functions that transform a generic PostgreSQL image into a production-grade service.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. The "Pre-Computation" Strategy (Phase 1)&lt;/strong&gt;&lt;br&gt;
The &lt;code&gt;check_and_generate_secret&lt;/code&gt; function generates high-entropy passwords using &lt;code&gt;openssl rand&lt;/code&gt; and saves them to our master &lt;code&gt;cicd.env&lt;/code&gt; file &lt;em&gt;before&lt;/em&gt; the container ever starts. This prevents the "First Run" race condition where an application might try to connect before a password is fully established. Note that we generate passwords for Artifactory, SonarQube, Mattermost, and Grafana all at once. We are provisioning the city's infrastructure in one go.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The "Leaky Container" Fix (UID 999) (Phase 2)&lt;/strong&gt;&lt;br&gt;
This is one of the most common "gotchas" in Docker database deployments. The official PostgreSQL container runs as user &lt;code&gt;postgres&lt;/code&gt; with a fixed &lt;strong&gt;UID 999&lt;/strong&gt;. When we bind-mount our host's certificate directory (&lt;code&gt;certs/&lt;/code&gt;) into the container, the permissions bleed through. If the private key is owned by our host user (e.g., UID 1000), the database process inside the container (UID 999) cannot read it and will crash immediately.&lt;br&gt;
We solve this with "Host-Side Surgery": &lt;code&gt;sudo chown -R 999:999&lt;/code&gt;. We explicitly set the ownership on the host so it matches the guest's expectation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The "Bouncer" (Phase 3)&lt;/strong&gt;&lt;br&gt;
We generate a strict &lt;code&gt;pg_hba.conf&lt;/code&gt; file. This acts as the firewall for the database application layer.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;hostnossl ... reject&lt;/code&gt;&lt;/strong&gt;: This is the "No Shirt, No Service" policy. We explicitly reject any connection attempt from the network that does not use SSL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;scram-sha-256&lt;/code&gt;&lt;/strong&gt;: We enforce the modern SCRAM authentication method, replacing the vulnerable &lt;code&gt;md5&lt;/code&gt; default. This adds cryptographic salt and channel binding, preventing "Pass-the-Hash" attacks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. The "Concrete Foundation" and "Zero Trust" (Phase 4)&lt;/strong&gt;&lt;br&gt;
The &lt;code&gt;init.sh&lt;/code&gt; script is where we handle the deep architectural requirements.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;DB_COLLATE='C'&lt;/code&gt;&lt;/strong&gt;: We hardcode the collation to &lt;code&gt;C&lt;/code&gt;. This forces the database to use raw byte-value sorting rather than complex, culturally-aware sorting logic. This is a hard requirement for &lt;strong&gt;SonarQube&lt;/strong&gt; (our next article), which requires strict case sensitivity. Changing collation later is impossible without wiping the database, so we must pour this concrete correctly now.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;GRANT CREATE, USAGE ON SCHEMA public&lt;/code&gt;&lt;/strong&gt;: This handles the PostgreSQL 15 security shift. By default, new users can no longer create tables in the public schema. We explicitly &lt;code&gt;GRANT&lt;/code&gt; these permissions to our service users, adhering to a "Zero Trust" model where access is explicitly defined, not implicitly assumed.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  3.2 The Launcher (&lt;code&gt;02-deploy-database.sh&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;With our configuration assets generated and permissions fixed, we can now launch the database container. This script acts as the "Construction Crew," taking the blueprints provided by the Architect and assembling the running service.&lt;/p&gt;

&lt;p&gt;It performs a "Clean Slate" protocol—stopping and removing any existing instance—to ensure that configuration changes (like our new &lt;code&gt;pg_hba.conf&lt;/code&gt;) are always applied fresh.&lt;/p&gt;

&lt;p&gt;Create this file at &lt;code&gt;~/cicd_stack/postgres/02-deploy-database.sh&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;#               02-deploy-database.sh&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  This is the "Launcher" script for the Shared Database.&lt;/span&gt;
&lt;span class="c"&gt;#  It runs the PostgreSQL 17 container using the assets&lt;/span&gt;
&lt;span class="c"&gt;#  prepared by 01-setup-database.sh.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="c"&gt;# --- 1. Define Paths ---&lt;/span&gt;
&lt;span class="nv"&gt;HOST_CICD_ROOT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/cicd_stack"&lt;/span&gt;
&lt;span class="nv"&gt;POSTGRES_BASE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOST_CICD_ROOT&lt;/span&gt;&lt;span class="s2"&gt;/postgres"&lt;/span&gt;

&lt;span class="c"&gt;# Prerequisite files&lt;/span&gt;
&lt;span class="nv"&gt;SCOPED_ENV_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$POSTGRES_BASE&lt;/span&gt;&lt;span class="s2"&gt;/postgres.env"&lt;/span&gt;
&lt;span class="nv"&gt;SSL_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$POSTGRES_BASE&lt;/span&gt;&lt;span class="s2"&gt;/certs/server.key"&lt;/span&gt;
&lt;span class="nv"&gt;HBA_CONF&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$POSTGRES_BASE&lt;/span&gt;&lt;span class="s2"&gt;/config/pg_hba.conf"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Starting PostgreSQL Deployment..."&lt;/span&gt;

&lt;span class="c"&gt;# --- 2. Prerequisite Checks ---&lt;/span&gt;
&lt;span class="c"&gt;# We fail fast if the architect script has not been run.&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SCOPED_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: Scoped env file not found at &lt;/span&gt;&lt;span class="nv"&gt;$SCOPED_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Please run 01-setup-database.sh first."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SSL_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: SSL Key not found at &lt;/span&gt;&lt;span class="nv"&gt;$SSL_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Please run 01-setup-database.sh first."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HBA_CONF&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: pg_hba.conf not found at &lt;/span&gt;&lt;span class="nv"&gt;$HBA_CONF&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Please run 01-setup-database.sh first."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# --- 3. Clean Slate Protocol ---&lt;/span&gt;
&lt;span class="c"&gt;# Stop and remove any existing container to ensure a clean start&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;postgres&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Stopping existing 'postgres' container..."&lt;/span&gt;
    docker stop postgres
&lt;span class="k"&gt;fi
if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;-aq&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;postgres&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Removing existing 'postgres' container..."&lt;/span&gt;
    docker &lt;span class="nb"&gt;rm &lt;/span&gt;postgres
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# --- 4. Volume Management ---&lt;/span&gt;
&lt;span class="c"&gt;# Create the persistent data volume if it doesn't exist&lt;/span&gt;
docker volume create postgres-data &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Verified 'postgres-data' volume."&lt;/span&gt;

&lt;span class="c"&gt;# --- 5. Deploy Container ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Launching PostgreSQL 17 container..."&lt;/span&gt;

&lt;span class="c"&gt;# Notes on Mounts:&lt;/span&gt;
&lt;span class="c"&gt;# 1. postgres-data: Persists the DB files.&lt;/span&gt;
&lt;span class="c"&gt;# 2. certs: Provides the SSL keys (Must be owned by UID 999 - fixed in 01).&lt;/span&gt;
&lt;span class="c"&gt;# 3. config: Provides the strict pg_hba.conf.&lt;/span&gt;
&lt;span class="c"&gt;# 4. init: Provides the SQL script to create Artifactory/Sonar/etc users.&lt;/span&gt;

docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; postgres &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--restart&lt;/span&gt; always &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network&lt;/span&gt; cicd-net &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--hostname&lt;/span&gt; postgres.cicd.local &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--publish&lt;/span&gt; 127.0.0.1:5432:5432 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--env-file&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SCOPED_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; postgres-data:/var/lib/postgresql/data &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$POSTGRES_BASE&lt;/span&gt;&lt;span class="s2"&gt;/certs"&lt;/span&gt;:/etc/postgresql/ssl &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$POSTGRES_BASE&lt;/span&gt;&lt;span class="s2"&gt;/config/pg_hba.conf"&lt;/span&gt;:/etc/postgresql/pg_hba.conf &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$POSTGRES_BASE&lt;/span&gt;&lt;span class="s2"&gt;/init"&lt;/span&gt;:/docker-entrypoint-initdb.d &lt;span class="se"&gt;\&lt;/span&gt;
  postgres:17 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="nv"&gt;ssl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;on &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="nv"&gt;ssl_cert_file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/etc/postgresql/ssl/server.crt &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="nv"&gt;ssl_key_file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/etc/postgresql/ssl/server.key &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="nv"&gt;hba_file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/etc/postgresql/pg_hba.conf

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"PostgreSQL container started."&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Monitor initialization logs with: docker logs -f postgres"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Wait for 'database system is ready to accept connections' before proceeding."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deconstructing the Launcher
&lt;/h3&gt;

&lt;p&gt;This script connects the dots between our physical assets and the running container.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. The Identity Assertion (&lt;code&gt;--hostname&lt;/code&gt;)&lt;/strong&gt;&lt;br&gt;
We explicitly set &lt;code&gt;--hostname postgres.cicd.local&lt;/code&gt;. This is not cosmetic. This hostname matches the Common Name (CN) of the SSL certificate we generated in the Architect script. This match is what allows clients to use &lt;code&gt;sslmode=verify-full&lt;/code&gt;. If the container's hostname did not match the certificate, clients would reject the connection as a potential Man-in-the-Middle attack.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The Mount Strategy&lt;/strong&gt;&lt;br&gt;
We map four distinct volumes, each serving a specific architectural purpose:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;postgres-data&lt;/code&gt;: The &lt;strong&gt;Docker Volume&lt;/strong&gt; for the actual database files (opaque, high-performance IO).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;certs&lt;/code&gt;: The &lt;strong&gt;Bind Mount&lt;/strong&gt; containing our keys (permission-fixed to UID 999).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;config&lt;/code&gt;: The &lt;strong&gt;Bind Mount&lt;/strong&gt; injecting our strict &lt;code&gt;pg_hba.conf&lt;/code&gt; "Bouncer."&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;init&lt;/code&gt;: The &lt;strong&gt;Bind Mount&lt;/strong&gt; injecting our SQL script. PostgreSQL automatically executes any &lt;code&gt;.sh&lt;/code&gt; or &lt;code&gt;.sql&lt;/code&gt; file found in &lt;code&gt;/docker-entrypoint-initdb.d&lt;/code&gt; during the very first startup.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. The Runtime Flags (&lt;code&gt;-c&lt;/code&gt;)&lt;/strong&gt;&lt;br&gt;
We override the default PostgreSQL configuration directly in the command line.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-c ssl=on&lt;/code&gt;: Forces the server to enable the SSL subsystem.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-c hba_file=...&lt;/code&gt;: Tells PostgreSQL to ignore its default access rules and use our strict &lt;code&gt;pg_hba.conf&lt;/code&gt; instead.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  3.3 The Audit (&lt;code&gt;03-verify-database.sh&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;Deploying the database is not enough. We must verify that our security controls are actually working &lt;em&gt;before&lt;/em&gt; we try to connect complex applications like Artifactory. If we skip this step, we might spend hours debugging Artifactory connection errors, not knowing if the issue is the network, the password, or the SSL handshake.&lt;/p&gt;

&lt;p&gt;We will perform a formal audit using a "Negative Testing" methodology. We want to prove that the database &lt;strong&gt;rejects&lt;/strong&gt; insecure connections.&lt;/p&gt;

&lt;p&gt;Create this file in your &lt;strong&gt;article source directory&lt;/strong&gt; (on your host) at &lt;code&gt;~/Documents/FromFirstPrinciples/articles/0009_cicd_part05_artifactory/03-verify-database.sh&lt;/code&gt;. This ensures it is mounted into our &lt;code&gt;dev-container&lt;/code&gt;, where we will run the test.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;#               03-verify-database.sh&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  This script audits the PostgreSQL 17 deployment.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  USAGE INSTRUCTIONS:&lt;/span&gt;
&lt;span class="c"&gt;#  1. This script must be run INSIDE the 'dev-container'.&lt;/span&gt;
&lt;span class="c"&gt;#  2. You must stage the secrets file on the HOST first:&lt;/span&gt;
&lt;span class="c"&gt;#     cp ~/cicd_stack/postgres/postgres.env ~/Documents/FromFirstPrinciples/data/&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  What this script checks:&lt;/span&gt;
&lt;span class="c"&gt;#  1. Network connectivity to postgres.cicd.local.&lt;/span&gt;
&lt;span class="c"&gt;#  2. Strict SSL enforcement (PGSSLMODE=verify-full).&lt;/span&gt;
&lt;span class="c"&gt;#  3. Negative Test: Ensures non-SSL connections are REJECTED.&lt;/span&gt;
&lt;span class="c"&gt;#  4. Authentication for all 4 service users.&lt;/span&gt;
&lt;span class="c"&gt;#  5. Authorization (Can they create tables in 'public'?).&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="c"&gt;# Fix for Perl locale warnings in some containers&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;LC_ALL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;C

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Starting Database Verification Audit..."&lt;/span&gt;

&lt;span class="c"&gt;# --- 1. Dependency Check ---&lt;/span&gt;
&lt;span class="c"&gt;# The dev-container might not have the psql client installed.&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; psql &amp;amp;&amp;gt; /dev/null&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"psql not found. Installing PostgreSQL 17 client..."&lt;/span&gt;
    &lt;span class="c"&gt;# We assume sudo is available or we are root in dev-container&lt;/span&gt;
    &lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update &lt;span class="nt"&gt;-qq&lt;/span&gt;
    &lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nt"&gt;-qq&lt;/span&gt; postgresql-common
    &lt;span class="c"&gt;# Install the official PG repo to get version 17&lt;/span&gt;
    &lt;span class="nb"&gt;yes&lt;/span&gt; | &lt;span class="nb"&gt;sudo&lt;/span&gt; /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh
    &lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update &lt;span class="nt"&gt;-qq&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nt"&gt;-qq&lt;/span&gt; postgresql-client-17
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# --- 2. Load Environment Secrets ---&lt;/span&gt;
&lt;span class="c"&gt;# We expect the file to be in ~/data (mapped from Host ~/Documents/FromFirstPrinciples/data)&lt;/span&gt;
&lt;span class="nv"&gt;ENV_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/data/postgres.env"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"CRITICAL ERROR: Secrets file not found at &lt;/span&gt;&lt;span class="nv"&gt;$ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Please run this command on your HOST machine first:"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"  cp ~/cicd_stack/postgres/postgres.env ~/Documents/FromFirstPrinciples/data/"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Loading secrets from &lt;/span&gt;&lt;span class="nv"&gt;$ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;..."&lt;/span&gt;
&lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# --- 3. Negative Test: SSL Rejection ---&lt;/span&gt;
verify_ssl_rejection&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"---------------------------------------------------"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Security Audit: Verifying Non-SSL Rejection"&lt;/span&gt;

    &lt;span class="c"&gt;# Use Artifactory creds for the test (User doesn't matter, connection type does)&lt;/span&gt;
    &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PGPASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ARTIFACTORY_DB_PASSWORD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    &lt;span class="c"&gt;# CRITICAL FIX: Use ENV variable, not --set&lt;/span&gt;
    &lt;span class="c"&gt;# We attempt to force a non-SSL connection.&lt;/span&gt;
    &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PGSSLMODE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"disable"&lt;/span&gt;

    &lt;span class="c"&gt;# We explicitly want this command to FAIL.&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;psql &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--host&lt;/span&gt; &lt;span class="s2"&gt;"postgres.cicd.local"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--username&lt;/span&gt; &lt;span class="s2"&gt;"artifactory"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--dbname&lt;/span&gt; &lt;span class="s2"&gt;"artifactory"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--no-password&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--command&lt;/span&gt; &lt;span class="s2"&gt;"SELECT 1;"&lt;/span&gt; &amp;amp;&amp;gt; /dev/null&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then

        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;" [FAIL] SECURITY BREACH! The database accepted a non-SSL connection."&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"        Check your pg_hba.conf file for 'hostnossl ... reject'."&lt;/span&gt;
        &lt;span class="nb"&gt;exit &lt;/span&gt;1
    &lt;span class="k"&gt;else
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;" [PASS] Connection rejected (Expected). The database correctly blocked non-SSL traffic."&lt;/span&gt;
    &lt;span class="k"&gt;fi&lt;/span&gt;

    &lt;span class="c"&gt;# Unset the variable so it doesn't pollute later tests&lt;/span&gt;
    &lt;span class="nb"&gt;unset &lt;/span&gt;PGSSLMODE
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# --- 4. Positive Test: Functional Verification ---&lt;/span&gt;
verify_service_db&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;pass&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;db&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$3&lt;/span&gt;

    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"---------------------------------------------------"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Auditing Service: &lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$pass&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;" [FAIL] Password variable is empty."&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;1
    &lt;span class="k"&gt;fi

    &lt;/span&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PGPASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$pass&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="c"&gt;# CRITICAL FIXES:&lt;/span&gt;
    &lt;span class="c"&gt;# 1. Force strict verification&lt;/span&gt;
    &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PGSSLMODE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"verify-full"&lt;/span&gt;
    &lt;span class="c"&gt;# 2. Tell libpq to look at the System Certificate Bundle (where your CA is)&lt;/span&gt;
    &lt;span class="c"&gt;#    Instead of looking in ~/.postgresql/root.crt&lt;/span&gt;
    &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PGSSLROOTCERT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/etc/ssl/certs/ca-certificates.crt"&lt;/span&gt;

    psql &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--host&lt;/span&gt; &lt;span class="s2"&gt;"postgres.cicd.local"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--username&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--dbname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$db&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--no-password&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--set&lt;/span&gt; &lt;span class="nv"&gt;ON_ERROR_STOP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="no"&gt;EOSQL&lt;/span&gt;&lt;span class="sh"&gt;

        -- 1. Check SSL Status (Using pg_stat_ssl view)
        SELECT CASE
            WHEN ssl THEN 'SSL: ACTIVE'
            ELSE 'SSL: INACTIVE'
        END AS security_status
        FROM pg_stat_ssl
        WHERE pid = pg_backend_pid();

        -- 2. Check PG17 Permissions (Create Table in Public)
        CREATE TABLE public.verification_test (id int);

        -- 3. Cleanup
        DROP TABLE public.verification_test;
&lt;/span&gt;&lt;span class="no"&gt;
EOSQL

&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt; &lt;span class="nt"&gt;-eq&lt;/span&gt; 0 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;" [PASS] Authentication successful."&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;" [PASS] SSL encryption verified."&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;" [PASS] PG17 Schema permissions verified (CRUD OK)."&lt;/span&gt;
    &lt;span class="k"&gt;else
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;" [FAIL] Verification failed for user: &lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="nb"&gt;exit &lt;/span&gt;1
    &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# --- 5. Execution Loop ---&lt;/span&gt;

&lt;span class="c"&gt;# 1. First, verify the security controls&lt;/span&gt;
verify_ssl_rejection

&lt;span class="c"&gt;# 2. Then verify functionality for all tenants&lt;/span&gt;
verify_service_db &lt;span class="s2"&gt;"artifactory"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ARTIFACTORY_DB_PASSWORD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"artifactory"&lt;/span&gt;
verify_service_db &lt;span class="s2"&gt;"sonarqube"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SONARQUBE_DB_PASSWORD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"sonarqube"&lt;/span&gt;
verify_service_db &lt;span class="s2"&gt;"mattermost"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MATTERMOST_DB_PASSWORD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"mattermost"&lt;/span&gt;
verify_service_db &lt;span class="s2"&gt;"grafana"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GRAFANA_DB_PASSWORD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"grafana"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"---------------------------------------------------"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"AUDIT COMPLETE: All database services are healthy and secure."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deconstructing the Audit
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. The "Negative Testing" Philosophy&lt;/strong&gt;&lt;br&gt;
Most tutorials only show you how to connect successfully. We prioritize verifying that we &lt;em&gt;cannot&lt;/em&gt; connect insecurely.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;PGSSLMODE="disable"&lt;/code&gt;&lt;/strong&gt;: We deliberately attempt to break our own rules by forcing a clear-text connection.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Payoff:&lt;/strong&gt; The script considers it a &lt;strong&gt;&lt;code&gt;[PASS]&lt;/code&gt;&lt;/strong&gt; only if the connection fails. This proves that our &lt;code&gt;pg_hba.conf&lt;/code&gt; "Bouncer" is physically rejecting unencrypted traffic.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. The "Identity" Check (&lt;code&gt;verify-full&lt;/code&gt;)&lt;/strong&gt;&lt;br&gt;
In the positive tests, we set &lt;code&gt;PGSSLMODE="verify-full"&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;verify-ca&lt;/code&gt;&lt;/strong&gt; only checks the signature (Is this a valid passport?).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;verify-full&lt;/code&gt;&lt;/strong&gt; checks the hostname (Is this &lt;em&gt;your&lt;/em&gt; passport?).
By enforcing this, we validate that the certificate we generated (&lt;code&gt;postgres.cicd.local&lt;/code&gt;) correctly matches the hostname resolved by our internal DNS. This prevents Man-in-the-Middle attacks within our own network.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Executing the Audit
&lt;/h3&gt;

&lt;p&gt;This script requires a small "data bridging" step because the &lt;code&gt;dev-container&lt;/code&gt; cannot see the &lt;code&gt;cicd_stack&lt;/code&gt; deployment directory on the host (for security reasons).&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;On your Host:&lt;/strong&gt; Stage the secrets file where the dev container can see it.&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cp&lt;/span&gt; ~/cicd_stack/postgres/postgres.env ~/Documents/FromFirstPrinciples/data/
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Enter the Dev Container:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;./dev-container.sh
&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;OR, if already started&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh &lt;span class="nt"&gt;-i&lt;/span&gt; ~/.ssh/id_rsa &lt;span class="nt"&gt;-p&lt;/span&gt; 10200 &amp;lt;your_username&amp;gt;@127.0.0.1
&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;OR&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; dev-container bash
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Run the Audit:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Inside dev-container&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;articles/0009_cicd_part05_artifactory
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x 03-verify-database.sh
./03-verify-database.sh
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The Result:&lt;/strong&gt;&lt;br&gt;
You will see &lt;code&gt;[PASS] Connection rejected&lt;/code&gt; when the script attempts an insecure connection. You will then see &lt;code&gt;[PASS] SSL encryption verified&lt;/code&gt; for every service user, confirming that your &lt;code&gt;init.sh&lt;/code&gt; script successfully provisioned the multi-tenant architecture with the correct PostgreSQL 17 permissions.&lt;/p&gt;
&lt;h1&gt;
  
  
  Chapter 4: Action Plan (Part 2) - The Secure Warehouse
&lt;/h1&gt;
&lt;h2&gt;
  
  
  4.1 The Architecture: A Cluster in a Container
&lt;/h2&gt;

&lt;p&gt;With our shared database layer operational, we turn our attention to the application itself. Deploying Artifactory 7.x is fundamentally different from deploying a standard web application like Jenkins. While Jenkins is a single process, Artifactory is a distributed system packaged into a single container. It consists of several distinct microservices—&lt;strong&gt;Artifactory&lt;/strong&gt; (the core), &lt;strong&gt;Access&lt;/strong&gt; (security), &lt;strong&gt;Metadata&lt;/strong&gt;, &lt;strong&gt;Router&lt;/strong&gt;, and &lt;strong&gt;Frontend&lt;/strong&gt;—that must coordinate with one another to function.&lt;/p&gt;

&lt;p&gt;This microservice architecture necessitates a sophisticated internal trust model. We are not just managing a username and password; we are managing the cryptographic trust root for a cluster.&lt;/p&gt;

&lt;p&gt;To secure this "Cluster in a Container," we must explicitly manage two critical cryptographic assets:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;The Master Key:&lt;/strong&gt; This is an AES-256 key used for &lt;strong&gt;Data at Rest&lt;/strong&gt; encryption. Artifactory uses this to encrypt sensitive data within the configuration files (like the database password we just generated) and inside the database itself. If you lose this key, the data is cryptographically locked forever.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The Join Key:&lt;/strong&gt; This is a shared secret used for &lt;strong&gt;Data in Motion&lt;/strong&gt; trust. When the internal microservices (like Metadata or Access) start up, they use this key to "handshake" with one another and establish a circle of trust. Once trusted, they exchange short-lived tokens for API access.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In a default installation, Artifactory generates these keys automatically on the first run. However, relying on auto-generation creates a "Black Box" deployment where we do not possess the recovery keys for our own infrastructure. It also introduces potential race conditions during the initial boot where services might time out waiting for key generation.&lt;/p&gt;

&lt;p&gt;We will define these keys manually on the host &lt;em&gt;before&lt;/em&gt; the container starts. By generating high-entropy keys ourselves, we ensure we own the "Root of Trust" for our warehouse from the very first second.&lt;/p&gt;
&lt;h2&gt;
  
  
  4.2 The Architect (&lt;code&gt;04-setup-artifactory.sh&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;To orchestrate this complex internal environment, we need a robust Architect script. This script is responsible for generating the Master and Join keys, creating the specific directory structure required for the bootstrap process, and—most critically—generating the "Strict Compliance" TLS certificates required by the Artifactory Router.&lt;/p&gt;

&lt;p&gt;Create this file at &lt;code&gt;~/cicd_stack/artifactory/04-setup-artifactory.sh&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;#               04-setup-artifactory.sh&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  This is the "Architect" script for Artifactory.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  UPDATED: "Docs Compliance Mode"&lt;/span&gt;
&lt;span class="c"&gt;#  1. Generates Certificate with clientAuth + Critical KeyUsage.&lt;/span&gt;
&lt;span class="c"&gt;#  2. Sets shared.node.ip to match Certificate CN.&lt;/span&gt;
&lt;span class="c"&gt;#  3. Uses Bootstrap mechanism for SSL ingest.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="c"&gt;# --- 1. Define Paths ---&lt;/span&gt;
&lt;span class="nv"&gt;HOST_CICD_ROOT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/cicd_stack"&lt;/span&gt;
&lt;span class="nv"&gt;ARTIFACTORY_BASE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOST_CICD_ROOT&lt;/span&gt;&lt;span class="s2"&gt;/artifactory"&lt;/span&gt;
&lt;span class="nv"&gt;VAR_ETC&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ARTIFACTORY_BASE&lt;/span&gt;&lt;span class="s2"&gt;/var/etc"&lt;/span&gt;
&lt;span class="nv"&gt;VAR_BOOTSTRAP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ARTIFACTORY_BASE&lt;/span&gt;&lt;span class="s2"&gt;/var/bootstrap"&lt;/span&gt;
&lt;span class="nv"&gt;MASTER_ENV_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOST_CICD_ROOT&lt;/span&gt;&lt;span class="s2"&gt;/cicd.env"&lt;/span&gt;

&lt;span class="c"&gt;# CA Paths&lt;/span&gt;
&lt;span class="nv"&gt;CA_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOST_CICD_ROOT&lt;/span&gt;&lt;span class="s2"&gt;/ca"&lt;/span&gt;
&lt;span class="nv"&gt;CA_CERT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CA_DIR&lt;/span&gt;&lt;span class="s2"&gt;/pki/certs/ca.pem"&lt;/span&gt;
&lt;span class="nv"&gt;CA_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CA_DIR&lt;/span&gt;&lt;span class="s2"&gt;/pki/private/ca.key"&lt;/span&gt;
&lt;span class="nv"&gt;CA_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"your_secure_password"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Starting Artifactory 'Architect' Setup..."&lt;/span&gt;

&lt;span class="c"&gt;# --- 2. Secrets Management ---&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: Master env file not found at &lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi
&lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ARTIFACTORY_DB_PASSWORD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: ARTIFACTORY_DB_PASSWORD not found in cicd.env"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Please run 01-setup-database.sh first."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

&lt;/span&gt;check_and_generate_secret&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;var_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;var_value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="p"&gt;!var_name&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$3&lt;/span&gt; &lt;span class="c"&gt;# bytes&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$var_value&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Generating new &lt;/span&gt;&lt;span class="nv"&gt;$description&lt;/span&gt;&lt;span class="s2"&gt; (&lt;/span&gt;&lt;span class="nv"&gt;$var_name&lt;/span&gt;&lt;span class="s2"&gt;)..."&lt;/span&gt;
        &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;new_secret&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;openssl rand &lt;span class="nt"&gt;-hex&lt;/span&gt; &lt;span class="nv"&gt;$length&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"# &lt;/span&gt;&lt;span class="nv"&gt;$description&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$var_name&lt;/span&gt;&lt;span class="s2"&gt;=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$new_secret&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;$var_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$new_secret&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;else
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Found existing &lt;/span&gt;&lt;span class="nv"&gt;$var_name&lt;/span&gt;&lt;span class="s2"&gt;."&lt;/span&gt;
    &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

check_and_generate_secret &lt;span class="s2"&gt;"ARTIFACTORY_MASTER_KEY"&lt;/span&gt; &lt;span class="s2"&gt;"Artifactory Master Key"&lt;/span&gt; 32
check_and_generate_secret &lt;span class="s2"&gt;"ARTIFACTORY_JOIN_KEY"&lt;/span&gt; &lt;span class="s2"&gt;"Artifactory Join Key"&lt;/span&gt; 16
check_and_generate_secret &lt;span class="s2"&gt;"ARTIFACTORY_ADMIN_PASSWORD"&lt;/span&gt; &lt;span class="s2"&gt;"Artifactory Initial Admin Password"&lt;/span&gt; 16

&lt;span class="c"&gt;# --- 3. Directory Preparation ---&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$VAR_ETC&lt;/span&gt;&lt;span class="s2"&gt;/security/keys/trusted"&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$VAR_ETC&lt;/span&gt;&lt;span class="s2"&gt;/access"&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$VAR_ETC&lt;/span&gt;&lt;span class="s2"&gt;/artifactory"&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$VAR_BOOTSTRAP&lt;/span&gt;&lt;span class="s2"&gt;/router/keys"&lt;/span&gt;

&lt;span class="c"&gt;# --- 4. Strict Certificate Generation ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Phase 3: Generating Strict Compliance Certificate ---"&lt;/span&gt;

&lt;span class="nv"&gt;ROUTER_CERT_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$VAR_BOOTSTRAP&lt;/span&gt;&lt;span class="s2"&gt;/router/keys"&lt;/span&gt;
&lt;span class="nv"&gt;ROUTER_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ROUTER_CERT_DIR&lt;/span&gt;&lt;span class="s2"&gt;/custom-server.key"&lt;/span&gt;
&lt;span class="nv"&gt;ROUTER_CSR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ROUTER_CERT_DIR&lt;/span&gt;&lt;span class="s2"&gt;/custom-server.csr"&lt;/span&gt;
&lt;span class="nv"&gt;ROUTER_CRT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ROUTER_CERT_DIR&lt;/span&gt;&lt;span class="s2"&gt;/custom-server.crt"&lt;/span&gt;
&lt;span class="nv"&gt;ROUTER_CNF&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ROUTER_CERT_DIR&lt;/span&gt;&lt;span class="s2"&gt;/router.cnf"&lt;/span&gt;

&lt;span class="c"&gt;# 1. Generate Private Key&lt;/span&gt;
openssl genrsa &lt;span class="nt"&gt;-out&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ROUTER_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 4096
&lt;span class="nb"&gt;chmod &lt;/span&gt;600 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ROUTER_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# 2. Create OpenSSL Config (The Requirements from Docs)&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ROUTER_CNF&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
[req]
default_bits = 4096
prompt = no
default_md = sha256
distinguished_name = dn
req_extensions = v3_req

[dn]
C=ZA
ST=Gauteng
L=Johannesburg
O=Local CICD Stack
CN=artifactory.cicd.local

[v3_req]
# REQ 1: Key usage extension must be marked CRITICAL
# REQ 2: digitalSignature + keyEncipherment must be enabled
keyUsage = critical, digitalSignature, keyEncipherment

# REQ 3: Extended key usage tlsWebServerAuthentication + tlsWebClientAuthentication
extendedKeyUsage = serverAuth, clientAuth

basicConstraints = critical, CA:FALSE
subjectAltName = @alt_names

[alt_names]
# REQ 4: SANs must include the subject (CN)
DNS.1 = artifactory.cicd.local
DNS.2 = localhost
IP.1 = 127.0.0.1
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# 3. Generate CSR&lt;/span&gt;
openssl req &lt;span class="nt"&gt;-new&lt;/span&gt; &lt;span class="nt"&gt;-key&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ROUTER_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-out&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ROUTER_CSR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-config&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ROUTER_CNF&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# 4. Sign with Root CA&lt;/span&gt;
openssl x509 &lt;span class="nt"&gt;-req&lt;/span&gt; &lt;span class="nt"&gt;-in&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ROUTER_CSR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-CA&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CA_CERT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-CAkey&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CA_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-CAcreateserial&lt;/span&gt; &lt;span class="nt"&gt;-out&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ROUTER_CRT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-days&lt;/span&gt; 365 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-sha256&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-extensions&lt;/span&gt; v3_req &lt;span class="nt"&gt;-extfile&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ROUTER_CNF&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-passin&lt;/span&gt; pass:&lt;span class="nv"&gt;$CA_PASSWORD&lt;/span&gt;

&lt;span class="c"&gt;# Cleanup&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ROUTER_CSR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ROUTER_CNF&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;644 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ROUTER_CRT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Compliant Certificate generated."&lt;/span&gt;

&lt;span class="c"&gt;# --- 5. Trust Staging ---&lt;/span&gt;
&lt;span class="c"&gt;# Docs: "Copy the CA of the custom TLS certificate in etc/security/keys/trusted/"&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CA_CERT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$VAR_ETC&lt;/span&gt;&lt;span class="s2"&gt;/security/keys/trusted/ca.pem"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Root CA staged."&lt;/span&gt;

&lt;span class="c"&gt;# --- 6. Secret File Generation ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ARTIFACTORY_MASTER_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$VAR_ETC&lt;/span&gt;&lt;span class="s2"&gt;/security/master.key"&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;600 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$VAR_ETC&lt;/span&gt;&lt;span class="s2"&gt;/security/master.key"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ARTIFACTORY_JOIN_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$VAR_ETC&lt;/span&gt;&lt;span class="s2"&gt;/security/join.key"&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;600 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$VAR_ETC&lt;/span&gt;&lt;span class="s2"&gt;/security/join.key"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"admin@*=&lt;/span&gt;&lt;span class="nv"&gt;$ARTIFACTORY_ADMIN_PASSWORD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$VAR_ETC&lt;/span&gt;&lt;span class="s2"&gt;/access/bootstrap.creds"&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;600 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$VAR_ETC&lt;/span&gt;&lt;span class="s2"&gt;/access/bootstrap.creds"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Secret files created."&lt;/span&gt;

&lt;span class="c"&gt;# --- 7. Configuration Imports ---&lt;/span&gt;

&lt;span class="c"&gt;# A. Access Config (Enable TLS)&lt;/span&gt;
&lt;span class="nv"&gt;ACCESS_IMPORT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$VAR_ETC&lt;/span&gt;&lt;span class="s2"&gt;/access/access.config.import.yml"&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt; "&lt;/span&gt;&lt;span class="nv"&gt;$ACCESS_IMPORT&lt;/span&gt;&lt;span class="sh"&gt;"
security:
  tls: true
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# B. Artifactory Config (Base URL)&lt;/span&gt;
&lt;span class="nv"&gt;ART_IMPORT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$VAR_ETC&lt;/span&gt;&lt;span class="s2"&gt;/artifactory/artifactory.config.import.yml"&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt; "&lt;/span&gt;&lt;span class="nv"&gt;$ART_IMPORT&lt;/span&gt;&lt;span class="sh"&gt;"
version: 1
GeneralConfiguration:
  # We point to the HTTPS port 8443
  baseUrl: "https://artifactory.cicd.local:8443"

OnboardingConfiguration:
  repoTypes:
    - maven
    - gradle
    - docker
    - pypi
    - npm
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# --- 8. System Configuration (system.yaml) ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Phase 5: Generating system.yaml ---"&lt;/span&gt;
&lt;span class="nv"&gt;SYSTEM_YAML&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$VAR_ETC&lt;/span&gt;&lt;span class="s2"&gt;/system.yaml"&lt;/span&gt;

&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt; "&lt;/span&gt;&lt;span class="nv"&gt;$SYSTEM_YAML&lt;/span&gt;&lt;span class="sh"&gt;"
configVersion: 1

shared:
  # REQ 5: The certificate's subject must match the property shared.node.ip
  node:
    ip: artifactory.cicd.local

  # IPv4 Fix (Still required for Docker stability)
  extraJavaOpts: "-Djava.net.preferIPv4Stack=true"

  database:
    type: postgresql
    driver: org.postgresql.Driver
    url: "jdbc:postgresql://postgres.cicd.local:5432/artifactory?sslmode=verify-full&amp;amp;sslrootcert=/var/opt/jfrog/artifactory/etc/security/keys/trusted/ca.pem"
    username: "artifactory"
    password: "&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ARTIFACTORY_DB_PASSWORD&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"

artifactory:
  tomcat:
    httpsConnector:
      enabled: true
      port: 8443
      # We do not specify file paths. We rely on Bootstrap to import them
      # into the internal keystore.
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"system.yaml generated."&lt;/span&gt;

&lt;span class="c"&gt;# --- 9. Final Permissions ---&lt;/span&gt;
&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; 1030:1030 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ARTIFACTORY_BASE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Setup Complete ---"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deconstructing the Architect
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. The "Strict Compliance" Certificate&lt;/strong&gt;&lt;br&gt;
This section is the solution to the "Go vs. OpenSSL" conflict we identified in the architectural phase.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Config:&lt;/strong&gt; We create a temporary &lt;code&gt;router.cnf&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;keyUsage = critical&lt;/code&gt;&lt;/strong&gt;: This line is non-negotiable. It satisfies the strict RFC 5280 requirement of the Go crypto library.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;extendedKeyUsage = serverAuth, clientAuth&lt;/code&gt;&lt;/strong&gt;: This handles the dual role of the Router. It serves the browser (&lt;code&gt;serverAuth&lt;/code&gt;) and connects to internal services (&lt;code&gt;clientAuth&lt;/code&gt;). Without this dual designation, the internal mTLS handshake would fail, and the Router would crash.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Fixing "Split Brain" Networking (&lt;code&gt;extraJavaOpts&lt;/code&gt;)&lt;/strong&gt;&lt;br&gt;
In the &lt;code&gt;system.yaml&lt;/code&gt; generation block, we inject &lt;code&gt;extraJavaOpts: "-Djava.net.preferIPv4Stack=true"&lt;/code&gt;.&lt;br&gt;
This prevents a common Docker networking issue. The JVM (Tomcat) often attempts to bind to IPv6 (&lt;code&gt;::1&lt;/code&gt;) by default, while the Go Router attempts to connect via IPv4 (&lt;code&gt;127.0.0.1&lt;/code&gt;). This mismatch causes "Connection Refused" errors on &lt;code&gt;localhost&lt;/code&gt;. By forcing the JVM to use the IPv4 stack, we align the internal protocols.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The "Bootstrap" Pattern (&lt;code&gt;config.import.yml&lt;/code&gt;)&lt;/strong&gt;&lt;br&gt;
We use a powerful Artifactory feature called "Configuration Import."&lt;br&gt;
Instead of clicking through the "Welcome Wizard" manually, we write our desired state to &lt;code&gt;artifactory.config.import.yml&lt;/code&gt; and &lt;code&gt;access.config.import.yml&lt;/code&gt;. Artifactory detects these files on boot, ingests the configuration (setting the Base URL to &lt;code&gt;https://artifactory.cicd.local:8443&lt;/code&gt;), enables TLS for the Access service, and then deletes the files. This turns the interactive setup process into immutable Infrastructure-as-Code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Permission Management (&lt;code&gt;chown 1030&lt;/code&gt;)&lt;/strong&gt;&lt;br&gt;
Just as we handled UID 999 for Postgres, we handle &lt;strong&gt;UID 1030&lt;/strong&gt; for Artifactory. The container runs as a non-root user. We must ensure that the configuration directories we just created on the host are readable and writable by this specific internal user ID, or the container will crash with a &lt;code&gt;Permission Denied&lt;/code&gt; error.&lt;/p&gt;
&lt;h2&gt;
  
  
  4.3 The Launcher (&lt;code&gt;05-deploy-artifactory.sh&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;With our filesystem prepared and our certificates minted, we are ready to launch the container. This script is the "Construction Crew." It takes the assets prepared by the Architect and brings the application to life.&lt;/p&gt;

&lt;p&gt;This script also addresses two specific runtime environment challenges: &lt;strong&gt;Version Stability&lt;/strong&gt; and the &lt;strong&gt;Proxy Trap&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Create this file at &lt;code&gt;~/cicd_stack/artifactory/05-deploy-artifactory.sh&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;#               05-deploy-artifactory.sh&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  This is the "Construction Crew" script.&lt;/span&gt;
&lt;span class="c"&gt;#  It launches the Artifactory container.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  UPDATED:&lt;/span&gt;
&lt;span class="c"&gt;#  1. Version 7.90.15.&lt;/span&gt;
&lt;span class="c"&gt;#  2. Ports 8443 (HTTPS) / 8082 (Router).&lt;/span&gt;
&lt;span class="c"&gt;#  3. Bootstrap Mount Enabled.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="c"&gt;# --- 1. Define Paths ---&lt;/span&gt;
&lt;span class="nv"&gt;HOST_CICD_ROOT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/cicd_stack"&lt;/span&gt;
&lt;span class="nv"&gt;ARTIFACTORY_BASE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOST_CICD_ROOT&lt;/span&gt;&lt;span class="s2"&gt;/artifactory"&lt;/span&gt;
&lt;span class="nv"&gt;VAR_ETC&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ARTIFACTORY_BASE&lt;/span&gt;&lt;span class="s2"&gt;/var/etc"&lt;/span&gt;
&lt;span class="nv"&gt;VAR_BOOTSTRAP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ARTIFACTORY_BASE&lt;/span&gt;&lt;span class="s2"&gt;/var/bootstrap"&lt;/span&gt;

&lt;span class="nv"&gt;SYSTEM_YAML&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$VAR_ETC&lt;/span&gt;&lt;span class="s2"&gt;/system.yaml"&lt;/span&gt;
&lt;span class="nv"&gt;MASTER_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$VAR_ETC&lt;/span&gt;&lt;span class="s2"&gt;/security/master.key"&lt;/span&gt;
&lt;span class="nv"&gt;JOIN_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$VAR_ETC&lt;/span&gt;&lt;span class="s2"&gt;/security/join.key"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Starting Artifactory Deployment..."&lt;/span&gt;

&lt;span class="c"&gt;# --- 2. Prerequisite Checks ---&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SYSTEM_YAML&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: system.yaml not found. Run 04-setup-artifactory.sh first."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi
if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: master.key not found. Run 04-setup-artifactory.sh first."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi
if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JOIN_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: join.key not found. Run 04-setup-artifactory.sh first."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# --- 3. Clean Slate Protocol ---&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;artifactory&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Stopping existing 'artifactory' container..."&lt;/span&gt;
    docker stop artifactory
&lt;span class="k"&gt;fi
if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;-aq&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;artifactory&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Removing existing 'artifactory' container..."&lt;/span&gt;
    docker &lt;span class="nb"&gt;rm &lt;/span&gt;artifactory
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# --- 4. Launch Container ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Launching Artifactory OSS container (v7.90.15)..."&lt;/span&gt;

docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; artifactory &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--restart&lt;/span&gt; always &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network&lt;/span&gt; cicd-net &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--hostname&lt;/span&gt; artifactory.cicd.local &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--publish&lt;/span&gt; 127.0.0.1:8443:8443 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--publish&lt;/span&gt; 127.0.0.1:8082:8082 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--env&lt;/span&gt; &lt;span class="nv"&gt;no_proxy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"localhost,127.0.0.1,postgres.cicd.local,artifactory.cicd.local"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--env&lt;/span&gt; &lt;span class="nv"&gt;NO_PROXY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"localhost,127.0.0.1,postgres.cicd.local,artifactory.cicd.local"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; artifactory-data:/var/opt/jfrog/artifactory &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$VAR_ETC&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;:/var/opt/jfrog/artifactory/etc &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$VAR_BOOTSTRAP&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;:/var/opt/jfrog/artifactory/bootstrap &lt;span class="se"&gt;\&lt;/span&gt;
  releases-docker.jfrog.io/jfrog/artifactory-oss:7.90.15

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Artifactory container started."&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   This is a heavy Java application."&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   It will take 1-2 minutes to initialize."&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Monitor logs with: docker logs -f artifactory"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Wait for: 'Router (jfrou) ... Listening on port: 8082'"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Then access: https://artifactory.cicd.local:8443"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deconstructing the Launcher
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Version Pinning (&lt;code&gt;7.90.15&lt;/code&gt;)&lt;/strong&gt;&lt;br&gt;
We are explicitly pinning the image to version &lt;code&gt;7.90.15&lt;/code&gt;. In the world of Enterprise Java applications, "latest" is a dangerous tag. We discovered during development that newer versions (v7.90.20+) introduced a race condition where the Frontend (&lt;code&gt;jffe&lt;/code&gt;) service would crash before the Router was fully initialized, sending the container into a boot loop. By pinning to a known-good version, we ensure stability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The "Proxy Trap" (&lt;code&gt;no_proxy&lt;/code&gt;)&lt;/strong&gt;&lt;br&gt;
This is a critical fix for corporate or complex network environments.&lt;br&gt;
If your host machine defines &lt;code&gt;HTTP_PROXY&lt;/code&gt; variables (common in office environments), Docker containers inherit them by default. This creates a routing disaster: when the internal Router tries to talk to the internal Metadata service on &lt;code&gt;localhost:8081&lt;/code&gt;, the Go HTTP client sees the proxy variable and attempts to route that request &lt;em&gt;out&lt;/em&gt; to the corporate proxy server. The proxy server, having no idea what "localhost" inside your container refers to, rejects the connection.&lt;br&gt;
By injecting &lt;code&gt;no_proxy="localhost,127.0.0.1,..."&lt;/code&gt;, we explicitly tell the internal services to bypass the proxy and communicate directly for local addresses.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The Hybrid Mounts&lt;/strong&gt;&lt;br&gt;
We mount our three persistence layers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;artifactory-data&lt;/code&gt;: The Docker volume for the massive binary blobs.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;etc&lt;/code&gt;: The host directory containing our keys and &lt;code&gt;system.yaml&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;bootstrap&lt;/code&gt;: The host directory containing our certificates and config import files.
This structure allows us to "seed" the configuration from the host while keeping the heavy data storage managed by Docker.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  4.4 Verification (&lt;code&gt;06-verify-artifactory.py&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;With the container running, we need to verify that the internal "City" is actually functioning. Because Artifactory is a mesh of microservices, a simple "container running" status is insufficient. The container might be up, but the Router could be failing to talk to the Metadata service, or the Database connection might be hanging.&lt;/p&gt;

&lt;p&gt;We will use a Python script to perform a "pulse check" on the system. This script hits specific health endpoints that aggregate the status of the internal components. It also attempts to verify our administrative access, though we expect that part to skip until we perform our manual UI setup in the next chapter.&lt;/p&gt;

&lt;p&gt;Create this file at &lt;code&gt;~/Documents/FromFirstPrinciples/articles/0009_cicd_part05_artifactory/06-verify-artifactory.py&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env python3
&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ssl&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;urllib.request&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;urllib.error&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;

&lt;span class="c1"&gt;# --- Configuration ---
&lt;/span&gt;&lt;span class="n"&gt;ENV_FILE_PATH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;home&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cicd_stack&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cicd.env&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;BASE_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://artifactory.cicd.local:8082&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# Endpoints
&lt;/span&gt;&lt;span class="n"&gt;HEALTH_ENDPOINT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;BASE_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/router/api/v1/system/health&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;PING_ENDPOINT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;BASE_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/artifactory/api/system/ping&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="c1"&gt;# We use the endpoint you confirmed works with your token
&lt;/span&gt;&lt;span class="n"&gt;TOKEN_LIST_ENDPOINT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;BASE_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/access/api/v1/tokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;load_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env_path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;env_path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[FAIL] Configuration error: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;env_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; not found.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;

    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'"&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_ssl_context&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="c1"&gt;# Trusts the system CA store (where our Root CA lives)
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_default_context&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;print_response_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Helper to print the full error body for debugging&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;       [SERVER RESPONSE]: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;       [SERVER RESPONSE]: (Could not decode body)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_health&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Test 1: Router Health (Unauthenticated) ---&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;HEALTH_ENDPOINT&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_ssl_context&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HEALTH_ENDPOINT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
                &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;router&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{}).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;state&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;UNKNOWN&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[PASS] Status: 200 OK&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;       Router State: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[FAIL] Status: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nf"&gt;print_response_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HTTPError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[FAIL] HTTP &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print_response_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[FAIL] &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_ping&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Test 2: System Ping (Unauthenticated) ---&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;PING_ENDPOINT&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_ssl_context&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PING_ENDPOINT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OK&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[PASS] Status: 200 OK&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;       Response: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[FAIL] Unexpected response: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HTTPError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[FAIL] HTTP &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print_response_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[FAIL] &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_admin_token&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Test 3: Admin Token Verification ---&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;TOKEN_LIST_ENDPOINT&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ARTIFACTORY_ADMIN_TOKEN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[SKIP] ARTIFACTORY_ADMIN_TOKEN not found in cicd.env&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;

    &lt;span class="c1"&gt;# We use the Bearer header as confirmed by the search AI
&lt;/span&gt;    &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_ssl_context&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TOKEN_LIST_ENDPOINT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
                &lt;span class="n"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;tokens&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[PASS] Status: 200 OK&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;       Admin Access Confirmed.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;       Visible Tokens: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;       First Token ID: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;token_id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[FAIL] Status: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nf"&gt;print_response_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HTTPError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[FAIL] HTTP &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print_response_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;       (Token is valid but lacks permission to list tokens)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;       (Token is invalid or expired)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[FAIL] &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;load_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ENV_FILE_PATH&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;check_health&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;check_ping&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;check_admin_token&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Verification Complete ---&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Running the Verification
&lt;/h3&gt;

&lt;p&gt;Unlike the database audit, we can run this script directly from the &lt;strong&gt;Host&lt;/strong&gt;, provided you have Python 3 installed. We want to prove that our host machine—which will act as the developer workstation—can trust the Artifactory SSL certificate.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# On the Host Machine&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/Documents/FromFirstPrinciples/articles/0009_cicd_part05_artifactory
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x 06-verify-artifactory.py
./06-verify-artifactory.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see &lt;code&gt;[PASS]&lt;/code&gt; for the Router Health and System Ping. The Admin Token verification will currently &lt;code&gt;[SKIP]&lt;/code&gt;. This is expected; we have the infrastructure running, but we haven't yet logged in to generate the keys for the castle. We will handle that next.&lt;/p&gt;

&lt;h1&gt;
  
  
  Chapter 5: UI Configuration - Opening the Warehouse
&lt;/h1&gt;

&lt;h2&gt;
  
  
  5.1 First Login &amp;amp; The "Wizard Bypass"
&lt;/h2&gt;

&lt;p&gt;The infrastructure is live. We have a running database, a secure microservice mesh, and a listening web server. Now, we verify the user experience.&lt;/p&gt;

&lt;p&gt;Open your browser on your host machine and navigate to:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;https://artifactory.cicd.local:8443&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because we meticulously established our Public Key Infrastructure in Article 2 and imported the Root CA into our host's trust store, the page should load immediately with a secure lock icon. There are no "Your connection is not private" warnings to click through.&lt;/p&gt;

&lt;p&gt;You will be greeted by the JFrog login screen. To log in, use the credentials we generated in our &lt;code&gt;01-setup-database.sh&lt;/code&gt; script.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Username:&lt;/strong&gt; &lt;code&gt;admin&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Password:&lt;/strong&gt; Open your &lt;code&gt;~/cicd_stack/cicd.env&lt;/code&gt; file and copy the value of &lt;code&gt;ARTIFACTORY_ADMIN_PASSWORD&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The "Wizard Bypass"
&lt;/h3&gt;

&lt;p&gt;Upon logging in, you might expect to see a "Welcome Wizard" asking you to accept terms, set a Base URL, and create default repositories.&lt;/p&gt;

&lt;p&gt;You will not see this. You will be taken directly to the main dashboard.&lt;/p&gt;

&lt;p&gt;This is the payoff for the &lt;code&gt;artifactory.config.import.yml&lt;/code&gt; file we injected during the setup phase. By defining our desired state (Base URL and Repository Types) in code and bootstrapping it into the container, we have effectively "skipped" the manual onboarding process. This is a critical pattern for "Infrastructure as Code"—we treat the application configuration just like the server configuration.&lt;/p&gt;

&lt;p&gt;To verify this, navigate to &lt;strong&gt;Administration&lt;/strong&gt; (on the top menu) -&amp;gt; &lt;strong&gt;General Management&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Settings&lt;/strong&gt;. You will see that the &lt;strong&gt;Custom Base URL&lt;/strong&gt; is already correctly set to &lt;code&gt;https://artifactory.cicd.local:8443&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  5.2 Creating the Admin Token (The Integration Key)
&lt;/h2&gt;

&lt;p&gt;Now that we are logged in, we need to generate the credentials that Jenkins will use. We cannot simply give Jenkins our admin password. Modern CI/CD best practices dictate the use of &lt;strong&gt;Access Tokens&lt;/strong&gt;, which provide better auditability and can be revoked without changing the root password.&lt;/p&gt;

&lt;p&gt;Specifically, we need a &lt;strong&gt;Global Admin Token&lt;/strong&gt;. As we discovered in our research, Artifactory 7.x distinguishes between "Project Admin" tokens (which are sandboxed to specific projects) and "Global" tokens. Since Jenkins acts as the orchestrator for our entire city—publishing global Build Info metadata and interacting with the system APIs—it requires an Admin-scoped token.&lt;/p&gt;

&lt;p&gt;Here is the procedure to generate the correct token:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Navigate to &lt;strong&gt;Administration&lt;/strong&gt; (top menu) -&amp;gt; &lt;strong&gt;User Management&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Access Tokens&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; Click the &lt;strong&gt;"Generate Token"&lt;/strong&gt; button.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Token Type:&lt;/strong&gt; Select &lt;strong&gt;"Scoped Token"&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Token Scope:&lt;/strong&gt; Select &lt;strong&gt;"Admin"&lt;/strong&gt;. This ensures the token inherits the full administrative power required for global operations.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;User Name:&lt;/strong&gt; Enter &lt;code&gt;admin&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Service:&lt;/strong&gt; Click the &lt;strong&gt;"All"&lt;/strong&gt; checkbox. (Note: In the OSS version, "Artifactory" is likely the only service listed, but checking "All" ensures forward compatibility).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Expiration Time:&lt;/strong&gt; Set to &lt;strong&gt;"Never"&lt;/strong&gt; for the purpose of this lab. In a production environment, you would set a rotation policy here.&lt;/li&gt;
&lt;li&gt; Click &lt;strong&gt;"Generate"&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You will be presented with a &lt;code&gt;Reference Token&lt;/code&gt; (a short string) and an &lt;code&gt;Access Token&lt;/code&gt; (a very long JWT string). &lt;strong&gt;Copy the long Access Token immediately.&lt;/strong&gt; You will not be able to see it again once you close this window.&lt;/p&gt;

&lt;h3&gt;
  
  
  Updating the Secrets File
&lt;/h3&gt;

&lt;p&gt;We must now save this token to our master secrets file so our automation scripts can use it.&lt;/p&gt;

&lt;p&gt;On your host machine, open &lt;code&gt;~/cicd_stack/cicd.env&lt;/code&gt; and add the following line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;ARTIFACTORY_ADMIN_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;paste_your_long_token_here&amp;gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This token is the "Key to the Warehouse." It will allow our &lt;code&gt;07-update-jenkins.sh&lt;/code&gt; script to securely inject credentials into the Jenkins container in the next chapter.&lt;/p&gt;

&lt;h2&gt;
  
  
  5.3 Creating the Repository (&lt;code&gt;generic-local&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;With our admin token secured, we need to prepare the "Landing Zone" for our artifacts.&lt;/p&gt;

&lt;p&gt;Recall that in our &lt;code&gt;artifactory.config.import.yml&lt;/code&gt; bootstrap file, we defined several default repository types (&lt;code&gt;maven&lt;/code&gt;, &lt;code&gt;gradle&lt;/code&gt;, &lt;code&gt;docker&lt;/code&gt;, &lt;code&gt;npm&lt;/code&gt;). However, we intentionally omitted the &lt;strong&gt;Generic&lt;/strong&gt; type. This highlights a key architectural distinction: while package-specific repositories (like Maven) come with complex indexing and metadata rules, a Generic repository is essentially a smart file system.&lt;/p&gt;

&lt;p&gt;Because we didn't bootstrap it, we must create it manually. This will be the destination for our C++ SDKs, Rust Crates, and Python Wheels.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Navigate to &lt;strong&gt;Administration&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Repositories&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; Click the &lt;strong&gt;"Create Repository"&lt;/strong&gt; button in the top right.&lt;/li&gt;
&lt;li&gt; Select &lt;strong&gt;"Local"&lt;/strong&gt;. We want a repository that stores files on our own disk (in the PostgreSQL metadata / Docker Volume blob store), not a proxy to a remote URL.&lt;/li&gt;
&lt;li&gt; Select &lt;strong&gt;"Generic"&lt;/strong&gt; as the package type.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Repository Key:&lt;/strong&gt; Enter &lt;code&gt;generic-local&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; Click &lt;strong&gt;"Create Local Repository"&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We now have an empty storage bucket. Unlike the "Filing Cabinet" (Git), this "Warehouse" doesn't care about diffs or merge conflicts. It simply accepts binary blobs and stores them forever.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Note on Layouts
&lt;/h3&gt;

&lt;p&gt;By default, a Generic repository treats paths literally. When we configure our pipeline later to upload to &lt;code&gt;http-client/16/&lt;/code&gt;, Artifactory will simply create a folder named &lt;code&gt;http-client&lt;/code&gt; and a subfolder named &lt;code&gt;16&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It is important to understand that Artifactory does not automatically know that "16" is a version number. In a more advanced setup, we would apply a &lt;strong&gt;Custom Repository Layout&lt;/strong&gt; (using Regex) to teach Artifactory how to parse our folder structure (e.g., &lt;code&gt;[org]/[module]/[baseRev]&lt;/code&gt;). For our current needs, the physical folder structure is sufficient organization.&lt;/p&gt;

&lt;h2&gt;
  
  
  5.4 The "Set Me Up" Experience (Developer Productivity)
&lt;/h2&gt;

&lt;p&gt;Before we leave the UI, we should verify that our repository is accessible to developers. One of the primary benefits of using an Artifact Manager over a simple file server is the &lt;strong&gt;Developer Experience (DX)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In the &lt;strong&gt;Application&lt;/strong&gt; module (top menu), navigate to &lt;strong&gt;Artifactory&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Artifacts&lt;/strong&gt;. Select your new &lt;code&gt;generic-local&lt;/code&gt; repository in the tree view.&lt;/p&gt;

&lt;p&gt;Click the &lt;strong&gt;"Set Me Up"&lt;/strong&gt; button in the top right corner.&lt;/p&gt;

&lt;p&gt;Artifactory will dynamically generate the exact commands a developer needs to interact with this repository. Because we correctly configured our Base URL (&lt;code&gt;https://artifactory.cicd.local:8443&lt;/code&gt;) and our SSL certificates, the command provided will be production-ready:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-uadmin&lt;/span&gt;:&amp;lt;TOKEN&amp;gt; &lt;span class="nt"&gt;-T&lt;/span&gt; &amp;lt;PATH_TO_FILE&amp;gt; &lt;span class="s2"&gt;"https://artifactory.cicd.local:8443/artifactory/generic-local/&amp;lt;TARGET_FILE_PATH&amp;gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This validates our entire networking stack. It confirms that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; The system knows its own external DNS name (&lt;code&gt;artifactory.cicd.local&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt; It knows it is serving HTTPS on port 8443.&lt;/li&gt;
&lt;li&gt; It is ready to accept uploads.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is the "Warehouse" equivalent of a loading dock with clear signage. We don't force developers to guess the URL structure; we provide it.&lt;/p&gt;

&lt;h1&gt;
  
  
  Chapter 6: Action Plan (Part 3) - The Integrator
&lt;/h1&gt;

&lt;p&gt;We have a fully functional Factory (Jenkins) and a fully functional Warehouse (Artifactory). Currently, however, they are completely unaware of each other. To complete our supply chain, we need to introduce them.&lt;/p&gt;

&lt;p&gt;This integration requires two distinct operations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Authentication:&lt;/strong&gt; Jenkins needs the "Keys to the Warehouse" (the Admin Token we just generated).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Configuration:&lt;/strong&gt; Jenkins needs to know the address of the Warehouse (&lt;code&gt;artifactory.cicd.local&lt;/code&gt;) and be configured to use the Artifactory Plugin.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We will automate this process using a new script, &lt;code&gt;07-update-jenkins.sh&lt;/code&gt;. However, before we write the code, we must navigate a specific Docker behavior that trips up many DevOps engineers during day-two operations.&lt;/p&gt;

&lt;h2&gt;
  
  
  6.1 The "Stale Env" Trap
&lt;/h2&gt;

&lt;p&gt;Our first task is to get the &lt;code&gt;ARTIFACTORY_ADMIN_TOKEN&lt;/code&gt; from our host's master secrets file (&lt;code&gt;cicd.env&lt;/code&gt;) into the Jenkins container.&lt;/p&gt;

&lt;p&gt;In Article 4, we established a "Scoped Environment" pattern. We created a specific file, &lt;code&gt;jenkins.env&lt;/code&gt;, which is passed to the container using the &lt;code&gt;--env-file&lt;/code&gt; flag. The logical assumption is that if we append a new variable to &lt;code&gt;jenkins.env&lt;/code&gt; and restart the container, Jenkins will see it.&lt;/p&gt;

&lt;p&gt;This assumption is false.&lt;/p&gt;

&lt;p&gt;Docker containers are immutable snapshots. When you run &lt;code&gt;docker run&lt;/code&gt;, the Docker daemon reads the &lt;code&gt;--env-file&lt;/code&gt;, resolves the variables, and &lt;strong&gt;bakes them into the container's configuration&lt;/strong&gt;. The link to the file on the host is severed immediately after creation.&lt;/p&gt;

&lt;p&gt;If you run &lt;code&gt;docker stop jenkins-controller&lt;/code&gt; and then &lt;code&gt;docker start jenkins-controller&lt;/code&gt;, Docker simply reloads the &lt;em&gt;original&lt;/em&gt; configuration snapshot. It does &lt;strong&gt;not&lt;/strong&gt; re-read the file on the host. The container will come back up, but it will still have the "Stale Environment" from when it was first built, effectively ignoring our new token.&lt;/p&gt;

&lt;p&gt;To solve this, we cannot use a simple restart. We must destroy the container (&lt;code&gt;docker rm&lt;/code&gt;) and recreate it (&lt;code&gt;docker run&lt;/code&gt;). This forces the daemon to read the modified &lt;code&gt;jenkins.env&lt;/code&gt; file and generate a new configuration snapshot. Our integration script will handle this by triggering the &lt;code&gt;03-deploy-controller.sh&lt;/code&gt; script we wrote in the previous article, ensuring a clean environment reload.&lt;/p&gt;

&lt;h2&gt;
  
  
  6.2 The "JCasC Schema" Nightmare
&lt;/h2&gt;

&lt;p&gt;With the credentials ready to be injected, we face our second integration hurdle: configuring the Jenkins plugin itself.&lt;/p&gt;

&lt;p&gt;In a standard "Configuration as Code" setup, you would typically find the plugin's YAML schema by looking at the documentation or exporting the current configuration. If you search online for "Jenkins Artifactory JCasC example," 99% of the results—including official JFrog examples from just a year ago—will tell you to use a block named &lt;code&gt;artifactoryServers&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you try to use that block with the modern plugin (version 4.x+), Jenkins will crash on startup.&lt;/p&gt;

&lt;p&gt;This is due to a massive, undocumented architectural shift. The newer Jenkins Artifactory Plugin is no longer a standalone integration; it has been re-architected as a wrapper around the JFrog CLI. This change silently broke the JCasC schema. The configuration key &lt;code&gt;artifactoryServers&lt;/code&gt; was removed and replaced with &lt;code&gt;jfrogInstances&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To make matters worse, the internal structure changed as well. Simple fields like &lt;code&gt;credentialsId&lt;/code&gt; were moved inside nested objects like &lt;code&gt;deployerCredentialsConfig&lt;/code&gt; and &lt;code&gt;resolverCredentialsConfig&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We discovered this only by reverse-engineering the error logs during our build process. The plugin threw an &lt;code&gt;UnknownAttributesException&lt;/code&gt;, listing &lt;code&gt;jfrogInstances&lt;/code&gt; as a valid attribute. This validates a core DevOps principle: &lt;strong&gt;Never trust the documentation blindly; trust the error logs.&lt;/strong&gt; We must construct our configuration to match this new, strict schema, or the "Foreman" will never be able to talk to the "Warehouse."&lt;/p&gt;

&lt;h2&gt;
  
  
  6.3 Structured Data vs. Text Hacking (The Python Helper)
&lt;/h2&gt;

&lt;p&gt;To inject this complex &lt;code&gt;jfrogInstances&lt;/code&gt; configuration block into our existing &lt;code&gt;jenkins.yaml&lt;/code&gt;, we have a choice of tools.&lt;/p&gt;

&lt;p&gt;The "quick and dirty" approach would be to use &lt;code&gt;sed&lt;/code&gt; or &lt;code&gt;cat&lt;/code&gt; to append text to the end of the file. This is &lt;strong&gt;Text Hacking&lt;/strong&gt;. It is fragile, dangerous, and unprofessional. YAML relies on strict indentation. A single misplaced space in a &lt;code&gt;sed&lt;/code&gt; command can break the entire Jenkins configuration, causing the controller to fail on boot. Furthermore, &lt;code&gt;sed&lt;/code&gt; has no concept of structure; it cannot check if the configuration already exists, leading to duplicate entries if the script runs twice.&lt;/p&gt;

&lt;p&gt;The "First Principles" approach is &lt;strong&gt;Structured Data Manipulation&lt;/strong&gt;. We treat the YAML file as a data object, not a text stream.&lt;/p&gt;

&lt;p&gt;We will write a Python helper script, &lt;code&gt;update_jcasc.py&lt;/code&gt;. This script uses the &lt;code&gt;PyYAML&lt;/code&gt; library to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Parse&lt;/strong&gt; the existing &lt;code&gt;jenkins.yaml&lt;/code&gt; into a Python dictionary.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Check&lt;/strong&gt; if the Artifactory configuration already exists (Idempotency).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Inject&lt;/strong&gt; the new &lt;code&gt;jfrogInstances&lt;/code&gt; block with the correct indentation and structure.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Dump&lt;/strong&gt; the valid YAML back to disk.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Create this file at &lt;code&gt;~/cicd_stack/jenkins/config/update_jcasc.py&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env python3
&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;yaml&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="c1"&gt;# Path to the JCasC file
&lt;/span&gt;&lt;span class="n"&gt;JCAS_FILE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expanduser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;~/cicd_stack/jenkins/config/jenkins.yaml&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update_jcasc&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[INFO] Reading JCasC file: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;JCAS_FILE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JCAS_FILE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;jcasc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;yaml&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;safe_load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;FileNotFoundError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[ERROR] File not found: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;JCAS_FILE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 1. Add Artifactory Credentials
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[INFO] Injecting Artifactory credentials block...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;credentials&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;jcasc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;jcasc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;credentials&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;system&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;domainCredentials&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;credentials&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]}]}}&lt;/span&gt;

    &lt;span class="n"&gt;artifactory_cred&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;usernamePassword&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;artifactory-creds&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;scope&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;GLOBAL&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;description&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Artifactory Admin Token&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;username&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;${JENKINS_ARTIFACTORY_USERNAME}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;password&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;${JENKINS_ARTIFACTORY_PASSWORD}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;# Navigate to credentials list safely
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;system&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;jcasc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;credentials&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="n"&gt;jcasc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;credentials&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;system&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;domainCredentials&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;credentials&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]}]}&lt;/span&gt;

    &lt;span class="n"&gt;domain_creds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jcasc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;credentials&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;system&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;domainCredentials&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;domain_creds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;domain_creds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;credentials&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]})&lt;/span&gt;

    &lt;span class="n"&gt;creds_list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;domain_creds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;credentials&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;creds_list&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;creds_list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="n"&gt;domain_creds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;credentials&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;creds_list&lt;/span&gt;

    &lt;span class="c1"&gt;# Check existence (Idempotency)
&lt;/span&gt;    &lt;span class="n"&gt;exists&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cred&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;creds_list&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;usernamePassword&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cred&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;cred&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;usernamePassword&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;artifactory-creds&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;exists&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;creds_list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;artifactory_cred&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[INFO] Credential &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;artifactory-creds&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; added.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[INFO] Credential &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;artifactory-creds&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; already exists. Skipping.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 2. Add Artifactory Server Configuration (Updated Schema)
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[INFO] Injecting Artifactory Server configuration (v4+ Schema)...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;unclassified&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;jcasc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;jcasc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;unclassified&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="c1"&gt;# The v4+ Schema: 'jfrogInstances' instead of 'artifactoryServers'
&lt;/span&gt;    &lt;span class="n"&gt;jcasc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;unclassified&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;artifactoryBuilder&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;useCredentialsPlugin&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;jfrogInstances&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;instanceId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;artifactory&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;url&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;${JENKINS_ARTIFACTORY_URL}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;artifactoryUrl&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;${JENKINS_ARTIFACTORY_URL}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;deployerCredentialsConfig&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;credentialsId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;artifactory-creds&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;resolverCredentialsConfig&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;credentialsId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;artifactory-creds&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;bypassProxy&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;connectionRetry&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;timeout&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;
        &lt;span class="p"&gt;}]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;# 3. Write back to file
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[INFO] Writing updated JCasC file...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JCAS_FILE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;w&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;yaml&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jcasc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default_flow_style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sort_keys&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[INFO] JCasC update complete.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;update_jcasc&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deconstructing the Helper
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Schema Logic:&lt;/strong&gt; Notice the structure under &lt;code&gt;artifactoryBuilder&lt;/code&gt;. We define &lt;code&gt;jfrogInstances&lt;/code&gt; (plural) as a list containing our single server. We use &lt;code&gt;${JENKINS_ARTIFACTORY_URL}&lt;/code&gt; as a placeholder, which Jenkins will resolve at runtime from the environment variables we are about to inject.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;bypassProxy: True&lt;/code&gt;:&lt;/strong&gt; This is a critical networking setting for our architecture. If your host machine uses a corporate proxy, Jenkins might try to route requests for &lt;code&gt;artifactory.cicd.local&lt;/code&gt; through that external proxy, causing a connection failure. This flag forces Jenkins to treat Artifactory as an internal, direct-access service.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Idempotency:&lt;/strong&gt; The script checks &lt;code&gt;if not exists&lt;/code&gt; before appending the credential. This allows us to run the integration script multiple times without corrupting the file with duplicate entries.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  6.4 The Integrator Script (&lt;code&gt;07-update-jenkins.sh&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;We now have all the components required for integration: the Admin Token in &lt;code&gt;cicd.env&lt;/code&gt;, the Python helper to patch the YAML, and the understanding that we must recreate the container to apply these changes.&lt;/p&gt;

&lt;p&gt;This script is the conductor. It orchestrates the entire update process in a specific order to ensure a clean state transition.&lt;/p&gt;

&lt;p&gt;Create this file at &lt;code&gt;~/cicd_stack/artifactory/07-update-jenkins.sh&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;#               07-update-jenkins.sh&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  This script integrates Jenkins with Artifactory.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  1. Prereqs: Installs python3-yaml on host.&lt;/span&gt;
&lt;span class="c"&gt;#  2. Secrets: Injects Artifactory secrets into jenkins.env.&lt;/span&gt;
&lt;span class="c"&gt;#  3. JCasC:   Updates jenkins.yaml with Artifactory config.&lt;/span&gt;
&lt;span class="c"&gt;#  4. Apply:   Re-deploys Jenkins (Recreate Container).&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="c"&gt;# --- Paths ---&lt;/span&gt;
&lt;span class="nv"&gt;CICD_ROOT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/cicd_stack"&lt;/span&gt;
&lt;span class="c"&gt;# The module where we defined the Jenkins deployment scripts&lt;/span&gt;
&lt;span class="nv"&gt;JENKINS_MODULE_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/Documents/FromFirstPrinciples/articles/0008_cicd_part04_jenkins"&lt;/span&gt;
&lt;span class="nv"&gt;JENKINS_ENV_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JENKINS_MODULE_DIR&lt;/span&gt;&lt;span class="s2"&gt;/jenkins.env"&lt;/span&gt;
&lt;span class="nv"&gt;DEPLOY_SCRIPT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JENKINS_MODULE_DIR&lt;/span&gt;&lt;span class="s2"&gt;/03-deploy-controller.sh"&lt;/span&gt;

&lt;span class="c"&gt;# Path to the Python helper&lt;/span&gt;
&lt;span class="nv"&gt;PY_HELPER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"update_jcasc.py"&lt;/span&gt;
&lt;span class="c"&gt;# Path to master secrets&lt;/span&gt;
&lt;span class="nv"&gt;MASTER_ENV&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CICD_ROOT&lt;/span&gt;&lt;span class="s2"&gt;/cicd.env"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[INFO] Starting Jenkins &amp;lt;-&amp;gt; Artifactory Integration..."&lt;/span&gt;

&lt;span class="c"&gt;# --- 1. Prerequisites ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[INFO] Checking for Python YAML library..."&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"import yaml"&lt;/span&gt; 2&amp;gt;/dev/null&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[WARN] python3-yaml not found. Installing..."&lt;/span&gt;
    &lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update &lt;span class="nt"&gt;-qq&lt;/span&gt;
    &lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nt"&gt;-qq&lt;/span&gt; python3-yaml python3-dotenv
&lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[INFO] python3-yaml is already installed."&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# --- 2. Secret Injection ---&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[ERROR] Master environment file not found: &lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Load ARTIFACTORY_ADMIN_TOKEN&lt;/span&gt;
&lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ARTIFACTORY_ADMIN_TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[ERROR] ARTIFACTORY_ADMIN_TOKEN not found in cicd.env."&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"       Please ensure you have completed Module 05 setup."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JENKINS_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[ERROR] Jenkins env file not found at: &lt;/span&gt;&lt;span class="nv"&gt;$JENKINS_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[INFO] Injecting Artifactory secrets into jenkins.env..."&lt;/span&gt;

&lt;span class="c"&gt;# Append secrets only if they don't exist&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s2"&gt;"JENKINS_ARTIFACTORY_URL"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JENKINS_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt;&amp;gt; "&lt;/span&gt;&lt;span class="nv"&gt;$JENKINS_ENV_FILE&lt;/span&gt;&lt;span class="sh"&gt;"

# --- Artifactory Integration ---
JENKINS_ARTIFACTORY_URL=https://artifactory.cicd.local:8082/artifactory
JENKINS_ARTIFACTORY_USERNAME=admin
JENKINS_ARTIFACTORY_PASSWORD=&lt;/span&gt;&lt;span class="nv"&gt;$ARTIFACTORY_ADMIN_TOKEN&lt;/span&gt;&lt;span class="sh"&gt;
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[INFO] Secrets injected."&lt;/span&gt;

&lt;span class="c"&gt;# --- 3. Update JCasC ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[INFO] Updating JCasC configuration..."&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PY_HELPER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[ERROR] Python helper script not found at &lt;/span&gt;&lt;span class="nv"&gt;$PY_HELPER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"       Please create the update_jcasc.py script first."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

&lt;/span&gt;python3 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PY_HELPER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# --- 4. Re-Deploy Jenkins ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[INFO] Triggering Jenkins Re-deployment (Container Recreate)..."&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-x&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DEPLOY_SCRIPT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[ERROR] Deploy script not found or not executable: &lt;/span&gt;&lt;span class="nv"&gt;$DEPLOY_SCRIPT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Execute the deploy script from its own directory context&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JENKINS_MODULE_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; ./03-deploy-controller.sh&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[SUCCESS] Integration update complete."&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[INFO] Jenkins is restarting. Wait for initialization."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deconstructing the Integrator
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Prerequisites (The "Python Capability"):&lt;/strong&gt;&lt;br&gt;
We cannot assume the host machine has the necessary libraries to run our helper script. The script checks for &lt;code&gt;python3-yaml&lt;/code&gt; and installs it via &lt;code&gt;apt&lt;/code&gt; if missing. This ensures our automation doesn't crash due to missing dependencies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Secret Injection (Bridging the Gap):&lt;/strong&gt;&lt;br&gt;
This block acts as the bridge between the two articles. It reads the &lt;code&gt;ARTIFACTORY_ADMIN_TOKEN&lt;/code&gt; from the central &lt;code&gt;cicd.env&lt;/code&gt; (created in this article) and appends it to the &lt;code&gt;jenkins.env&lt;/code&gt; (created in the previous article).&lt;br&gt;
Notice the variable mapping:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;JENKINS_ARTIFACTORY_PASSWORD&lt;/code&gt; is populated with the value of &lt;code&gt;ARTIFACTORY_ADMIN_TOKEN&lt;/code&gt;.
This cleanly injects the secret into the Jenkins container's scope without exposing it in the Docker command history.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. The "Re-Deploy" (Solving the Stale Env):&lt;/strong&gt;&lt;br&gt;
This is the critical fix for the "Stale Env" trap. Instead of running &lt;code&gt;docker restart&lt;/code&gt;, the script changes directory (&lt;code&gt;cd&lt;/code&gt;) to the Jenkins module and executes &lt;code&gt;./03-deploy-controller.sh&lt;/code&gt;. This effectively stops, removes, and recreates the container using the &lt;em&gt;newly modified&lt;/em&gt; &lt;code&gt;jenkins.env&lt;/code&gt; file, ensuring the new token is actually loaded into the environment.&lt;/p&gt;
&lt;h2&gt;
  
  
  6.5 Verification
&lt;/h2&gt;

&lt;p&gt;Run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x 07-update-jenkins.sh
./07-update-jenkins.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Watch the logs. The Jenkins controller will restart. Once it is back online (approx. 2 minutes), perform the final verification:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Log in to Jenkins at &lt;code&gt;https://jenkins.cicd.local:10400&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; Navigate to &lt;strong&gt;Manage Jenkins&lt;/strong&gt; -&amp;gt; &lt;strong&gt;System&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; Scroll down to the &lt;strong&gt;JFrog&lt;/strong&gt; (or Artifactory) section.&lt;/li&gt;
&lt;li&gt; You will see the "artifactory" server configuration pre-filled.&lt;/li&gt;
&lt;li&gt; Click &lt;strong&gt;Test Connection&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You should see the message: &lt;strong&gt;"Found JFrog Artifactory 7.90.15 at &lt;a href="https://www.google.com/search?q=https://artifactory.cicd.local:8082/artifactory" rel="noopener noreferrer"&gt;https://artifactory.cicd.local:8082/artifactory&lt;/a&gt;"&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This single green message confirms everything:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DNS Resolution:&lt;/strong&gt; Jenkins found &lt;code&gt;artifactory.cicd.local&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSL Trust:&lt;/strong&gt; The JVM trusted the certificate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authentication:&lt;/strong&gt; The injected Admin Token worked.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plugin Logic:&lt;/strong&gt; The &lt;code&gt;jfrogInstances&lt;/code&gt; JCasC schema was parsed correctly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The "Factory" and the "Warehouse" are now connected.&lt;/p&gt;

&lt;h1&gt;
  
  
  Chapter 7: Packaging Theory - From "Binaries" to "Products"
&lt;/h1&gt;

&lt;h2&gt;
  
  
  7.1 The Concept: A Binary is Not a Product
&lt;/h2&gt;

&lt;p&gt;We have connected our Factory to our Warehouse. We have a secure pipe ready to transport goods. But before we turn on the conveyor belt, we must ask a fundamental question: &lt;em&gt;what exactly are we shipping?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In our current V1 pipeline, our build script produces &lt;strong&gt;Raw Binaries&lt;/strong&gt;: &lt;code&gt;libhttpc.so&lt;/code&gt; and &lt;code&gt;libhttpcpp.so&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This leads to the &lt;strong&gt;"Raw Binary" Trap&lt;/strong&gt;. A shared library file is useless on its own. If a developer downloads &lt;code&gt;libhttpc.so&lt;/code&gt; from Artifactory, they cannot use it. They are missing the "Contract"—the C header files (&lt;code&gt;.h&lt;/code&gt;) that define the API. Without the headers, the binary is a black box. Furthermore, they don't know if it was built for Debug or Release, or what dependencies it requires.&lt;/p&gt;

&lt;p&gt;To run a professional software supply chain, we must stop thinking in terms of &lt;em&gt;compiling binaries&lt;/em&gt; and start thinking in terms of &lt;strong&gt;packaging products&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We are building &lt;strong&gt;Software Development Kits (SDKs)&lt;/strong&gt; and &lt;strong&gt;Packages&lt;/strong&gt;. A "Product" is a self-contained, versioned, and consumable archive that a downstream developer can ingest without knowing or caring about how it was built.&lt;/p&gt;

&lt;p&gt;For our polyglot Hero Project, this means we need to produce three distinct, standardized formats:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;C &amp;amp; C++:&lt;/strong&gt; An &lt;strong&gt;SDK Archive&lt;/strong&gt; (&lt;code&gt;.tar.gz&lt;/code&gt;) containing both the "Implementation" (&lt;code&gt;lib/&lt;/code&gt;) and the "Interface" (&lt;code&gt;include/&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Rust:&lt;/strong&gt; A &lt;strong&gt;Standard Crate&lt;/strong&gt; (&lt;code&gt;.crate&lt;/code&gt;) containing source code and metadata, ready for the Rust ecosystem.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Python:&lt;/strong&gt; A &lt;strong&gt;Binary Wheel&lt;/strong&gt; (&lt;code&gt;.whl&lt;/code&gt;) containing pre-compiled bindings and metadata, ready for &lt;code&gt;pip&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We will now update our build systems to generate these packages natively.&lt;/p&gt;

&lt;h2&gt;
  
  
  7.2 The C/C++ Strategy: The "SDK" Archive
&lt;/h2&gt;

&lt;p&gt;For our C and C++ artifacts, we will use &lt;strong&gt;CPack&lt;/strong&gt;. CPack is bundled with CMake and is the industry standard for creating distribution packages.&lt;/p&gt;

&lt;p&gt;Instead of writing a fragile shell script in Jenkins to &lt;code&gt;cp&lt;/code&gt; files into a temporary directory and &lt;code&gt;tar&lt;/code&gt; them up, we define "Install Rules" directly in our &lt;code&gt;CMakeLists.txt&lt;/code&gt;. This pushes the packaging logic down into the build system where it belongs, making it portable and reproducible on any machine (developer workstation or CI agent).&lt;/p&gt;

&lt;p&gt;We need to modify &lt;code&gt;0004_std_lib_http_client/CMakeLists.txt&lt;/code&gt; in two places.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. The GoogleTest Fix (The "Pollution" Trap)&lt;/strong&gt;&lt;br&gt;
During our initial packaging tests, we discovered that CPack was bundling &lt;code&gt;libgtest.a&lt;/code&gt; and &lt;code&gt;libgmock.a&lt;/code&gt; into our SDK. This is because GoogleTest defines its own install rules, and CPack grabs &lt;em&gt;everything&lt;/em&gt;. We must explicitly disable this to keep our SDK clean.&lt;/p&gt;

&lt;p&gt;Locate the &lt;code&gt;FetchContent&lt;/code&gt; block for GoogleTest and inject the &lt;code&gt;INSTALL_GTEST OFF&lt;/code&gt; setting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cmake"&gt;&lt;code&gt;&lt;span class="nf"&gt;FetchContent_Declare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        googletest
        GIT_REPOSITORY https://github.com/google/googletest.git
        GIT_TAG        v1.17.0
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# --- FIX: Disable GTest Installation ---&lt;/span&gt;
&lt;span class="c1"&gt;# Prevents test libraries from polluting our SDK package&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;INSTALL_GTEST OFF CACHE BOOL &lt;span class="s2"&gt;""&lt;/span&gt; FORCE&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;gtest_force_shared_crt ON CACHE BOOL &lt;span class="s2"&gt;""&lt;/span&gt; FORCE&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;FetchContent_MakeAvailable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;googletest&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. The Packaging Logic&lt;/strong&gt;&lt;br&gt;
Append the following block to the very end of your &lt;code&gt;CMakeLists.txt&lt;/code&gt;. This defines the layout of our SDK: headers go to &lt;code&gt;include/&lt;/code&gt;, and binaries go to &lt;code&gt;lib/&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cmake"&gt;&lt;code&gt;&lt;span class="c1"&gt;# --- Packaging Configuration (CPack) ---&lt;/span&gt;

&lt;span class="c1"&gt;# 1. Define Install Rules&lt;/span&gt;
&lt;span class="c1"&gt;# These tell CMake what files belong in the final package.&lt;/span&gt;

&lt;span class="c1"&gt;# Install the compiled Shared Libraries (.so) into a 'lib/' folder&lt;/span&gt;
&lt;span class="nb"&gt;install&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;TARGETS httpc_lib httpcpp_lib
    LIBRARY DESTINATION lib
    ARCHIVE DESTINATION lib
    RUNTIME DESTINATION bin
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Install the Public Headers into an 'include/' folder&lt;/span&gt;
&lt;span class="nb"&gt;install&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;DIRECTORY include/ DESTINATION include&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# 2. Configure the Package Metadata&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;CPACK_PACKAGE_NAME &lt;span class="s2"&gt;"http-client-cpp-sdk"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;CPACK_PACKAGE_VENDOR &lt;span class="s2"&gt;"Warren Jitsing"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;CPACK_PACKAGE_DESCRIPTION_SUMMARY &lt;span class="s2"&gt;"Multi-Language HTTP Client SDK (C/C++)"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;CPACK_PACKAGE_VERSION &lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;CPACK_PACKAGE_CONTACT &lt;span class="s2"&gt;"warren.jitsing@infcon.co.za"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# 3. Configure the Generator&lt;/span&gt;
&lt;span class="c1"&gt;# We want a simple .tar.gz (TGZ) archive&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;CPACK_GENERATOR &lt;span class="s2"&gt;"TGZ"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# This must be the last command in the file&lt;/span&gt;
&lt;span class="nb"&gt;include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;CPack&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, running &lt;code&gt;cpack -G TGZ&lt;/code&gt; will generate a standardized &lt;code&gt;http-client-cpp-sdk-1.0.0.tar.gz&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  7.3 The Rust Strategy: The ".crate" Standard
&lt;/h2&gt;

&lt;p&gt;For our Rust implementation, the standard distribution format is the &lt;strong&gt;Crate&lt;/strong&gt;. Unlike C++, where we often distribute pre-compiled binaries, the Rust ecosystem distributes &lt;em&gt;source code&lt;/em&gt; bundled with a manifest (&lt;code&gt;Cargo.toml&lt;/code&gt;). The consumer downloads the crate and compiles it locally, ensuring compatibility with their specific architecture and kernel.&lt;/p&gt;

&lt;p&gt;To create a standard crate, we use the &lt;code&gt;cargo package&lt;/code&gt; command. This command creates a &lt;code&gt;.crate&lt;/code&gt; file (which is essentially a tarball of the source) in &lt;code&gt;target/package/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;However, &lt;code&gt;cargo package&lt;/code&gt; imposes a "Bureaucracy Check." It enforces strict metadata requirements to ensure the package is publishable to &lt;em&gt;crates.io&lt;/em&gt; (or Artifactory). If your &lt;code&gt;Cargo.toml&lt;/code&gt; is missing fields like &lt;code&gt;description&lt;/code&gt;, &lt;code&gt;license&lt;/code&gt;, or &lt;code&gt;repository&lt;/code&gt;, the command will fail.&lt;/p&gt;

&lt;p&gt;We must update &lt;code&gt;src/rust/Cargo.toml&lt;/code&gt; to satisfy these requirements and ensure our package name matches our library name standard.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[package]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"httprust"&lt;/span&gt;
&lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.1.0"&lt;/span&gt;
&lt;span class="py"&gt;edition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"2021"&lt;/span&gt;
&lt;span class="c"&gt;# --- Metadata Required for Packaging ---&lt;/span&gt;
&lt;span class="py"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"A standard library HTTP client implementation"&lt;/span&gt;
&lt;span class="py"&gt;license&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"MIT"&lt;/span&gt;
&lt;span class="py"&gt;repository&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://gitlab.cicd.local/Articles/0004_std_lib_http_client"&lt;/span&gt;
&lt;span class="py"&gt;authors&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Warren Jitsing &amp;lt;warren.jitsing@infcon.co.za&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nn"&gt;[lib]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"httprust"&lt;/span&gt;
&lt;span class="py"&gt;path&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"src/lib.rs"&lt;/span&gt;

&lt;span class="nn"&gt;[dependencies]&lt;/span&gt;
&lt;span class="py"&gt;libc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.2"&lt;/span&gt;
&lt;span class="py"&gt;reqwest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.12.23"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;features&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"blocking"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nn"&gt;[[bin]]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"httprust_client"&lt;/span&gt;
&lt;span class="py"&gt;path&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"src/bin/httprust_client.rs"&lt;/span&gt;

&lt;span class="nn"&gt;[[bin]]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"reqwest_client"&lt;/span&gt;
&lt;span class="py"&gt;path&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"src/bin/reqwest_client.rs"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  7.4 The Python Strategy: The Binary Wheel
&lt;/h2&gt;

&lt;p&gt;For Python, the standard "Product" is the &lt;strong&gt;Binary Wheel (&lt;code&gt;.whl&lt;/code&gt;)&lt;/strong&gt;. This is a pre-compiled package that includes metadata and, crucially, any compiled C-extensions. It allows consumers to install the package via &lt;code&gt;pip&lt;/code&gt; instantly without needing a compiler toolchain installed on their machine.&lt;/p&gt;

&lt;p&gt;Our &lt;code&gt;setup.sh&lt;/code&gt; script already handles this. It uses the standard &lt;code&gt;build&lt;/code&gt; module to generate the wheel.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# (From setup.sh)&lt;/span&gt;
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; build &lt;span class="nt"&gt;--wheel&lt;/span&gt; &lt;span class="nt"&gt;--outdir&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CMAKE_BINARY_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/wheelhouse src/python
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We do not need to change our build logic here; we simply need to know that our "Product" will be waiting for us in &lt;code&gt;build_release/wheelhouse/*.whl&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  7.5 The "Staging Area" Pattern (&lt;code&gt;dist/&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;We now have three different build systems (CMake, Cargo, Python Build) outputting artifacts to three different locations (&lt;code&gt;build_release/&lt;/code&gt;, &lt;code&gt;src/rust/target/package/&lt;/code&gt;, &lt;code&gt;build_release/wheelhouse/&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;If we try to configure the Jenkins Artifactory plugin to hunt down these files individually, our pipeline code will become messy and fragile.&lt;/p&gt;

&lt;p&gt;Instead, we will adopt the &lt;strong&gt;Staging Area&lt;/strong&gt; pattern. Before we trigger the upload, we will move all finished products into a single, clean directory named &lt;code&gt;dist/&lt;/code&gt;. This decouples the &lt;em&gt;Build&lt;/em&gt; logic from the &lt;em&gt;Publish&lt;/em&gt; logic. The Artifactory plugin will have a single, simple instruction: "Upload everything in &lt;code&gt;dist/&lt;/code&gt;."&lt;/p&gt;

&lt;p&gt;This completes our packaging theory. We are ready to implement the V2 Pipeline.&lt;/p&gt;

&lt;h1&gt;
  
  
  Chapter 8: Practical Application - The Pipeline V2
&lt;/h1&gt;

&lt;h2&gt;
  
  
  8.1 The Evolution: From "Build" to "Deliver"
&lt;/h2&gt;

&lt;p&gt;We are now ready to upgrade our pipeline. Our V1 &lt;code&gt;Jenkinsfile&lt;/code&gt; was a "Continuous Integration" (CI) pipeline; it verified that the code compiled and passed tests. Our V2 &lt;code&gt;Jenkinsfile&lt;/code&gt; will be a "Continuous Delivery" (CD) pipeline; it will produce shipping-ready artifacts.&lt;/p&gt;

&lt;p&gt;To achieve this, we insert a new &lt;strong&gt;Package&lt;/strong&gt; stage between "Test" and the final "Publish" step. This stage is responsible for executing the specific packaging commands we enabled in Chapter 7 (&lt;code&gt;cpack&lt;/code&gt;, &lt;code&gt;cargo package&lt;/code&gt;) and consolidating the outputs into our "Staging Area" (&lt;code&gt;dist/&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Open &lt;code&gt;0004_std_lib_http_client/Jenkinsfile&lt;/code&gt; and insert the following stage after &lt;code&gt;Test &amp;amp; Coverage&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Package'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'--- Packaging Artifacts ---'&lt;/span&gt;

                &lt;span class="c1"&gt;// 1. Create the "Staging Area"&lt;/span&gt;
                &lt;span class="c1"&gt;// This is the single folder Artifactory will look at later.&lt;/span&gt;
                &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'mkdir -p dist'&lt;/span&gt;

                &lt;span class="c1"&gt;// 2. Package C/C++ SDK (CPack)&lt;/span&gt;
                &lt;span class="c1"&gt;// We must run inside 'build_release' because CPack relies on &lt;/span&gt;
                &lt;span class="c1"&gt;// the CMakeCache.txt generated during the build stage.&lt;/span&gt;
                &lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'build_release'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'cpack -G TGZ -C Release'&lt;/span&gt;
                    &lt;span class="c1"&gt;// Move the resulting tarball to our staging area&lt;/span&gt;
                    &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'mv *.tar.gz ../dist/'&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;

                &lt;span class="c1"&gt;// 3. Package Rust Crate&lt;/span&gt;
                &lt;span class="c1"&gt;// We run inside the rust source directory.&lt;/span&gt;
                &lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'src/rust'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="c1"&gt;// This command generates the .crate file in target/package/&lt;/span&gt;
                    &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'cargo package'&lt;/span&gt;
                    &lt;span class="c1"&gt;// Move it to the staging area&lt;/span&gt;
                    &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'cp target/package/*.crate ../../dist/'&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;

                &lt;span class="c1"&gt;// 4. Collect Python Wheel&lt;/span&gt;
                &lt;span class="c1"&gt;// The wheel was already built by setup.sh in the earlier stage.&lt;/span&gt;
                &lt;span class="c1"&gt;// We simply retrieve it from the wheelhouse.&lt;/span&gt;
                &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'cp build_release/wheelhouse/*.whl dist/'&lt;/span&gt;

                &lt;span class="c1"&gt;// 5. Verification&lt;/span&gt;
                &lt;span class="c1"&gt;// List the contents so we can see exactly what we are about to ship in the logs.&lt;/span&gt;
                &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'ls -l dist/'&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deconstructing the Package Stage
&lt;/h3&gt;

&lt;p&gt;This stage is the implementation of our "Universal Bucket" strategy. It normalizes the chaos of our polyglot build system into a single, uniform directory.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;The Context Switch (&lt;code&gt;dir&lt;/code&gt;):&lt;/strong&gt; Notice how we repeatedly use the &lt;code&gt;dir()&lt;/code&gt; directive. This is crucial. &lt;code&gt;cpack&lt;/code&gt; &lt;em&gt;must&lt;/em&gt; run in the build directory to find its configuration. &lt;code&gt;cargo package&lt;/code&gt; &lt;em&gt;must&lt;/em&gt; run in the source directory to find &lt;code&gt;Cargo.toml&lt;/code&gt;. The pipeline navigates the filesystem so the tools don't have to guess.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The Consolidation:&lt;/strong&gt; Regardless of where the tool puts the file (&lt;code&gt;target/package&lt;/code&gt;, &lt;code&gt;wheelhouse&lt;/code&gt;), we forcefully move it to &lt;code&gt;dist/&lt;/code&gt;. This means our subsequent "Publish" stage will never need to know about the internal structure of our build. It just needs to know about &lt;code&gt;dist/&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The Artifacts:&lt;/strong&gt; At the end of this stage, &lt;code&gt;dist/&lt;/code&gt; will contain three files:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;http-client-cpp-sdk-1.0.0-Linux.tar.gz&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;httprust-0.1.0.crate&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;httppy-0.1.0-py3-none-any.whl&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  8.2 The "Publish" Stage and the Syntax Traps
&lt;/h2&gt;

&lt;p&gt;With our artifacts neatly organized in the &lt;code&gt;dist/&lt;/code&gt; directory, we can define the final stage of our pipeline: &lt;strong&gt;Publish&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This stage uses the Artifactory Plugin to upload our files and, critically, publish the build metadata. However, configuring this step involves navigating three specific syntax traps that often trip up first-time users: &lt;strong&gt;String Interpolation&lt;/strong&gt;, &lt;strong&gt;Pattern Matching&lt;/strong&gt;, and &lt;strong&gt;Silent Failures&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Here is the complete &lt;code&gt;Publish&lt;/code&gt; stage. Add this to your &lt;code&gt;Jenkinsfile&lt;/code&gt; immediately after the &lt;code&gt;Package&lt;/code&gt; stage.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Publish'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'--- Publishing to Artifactory ---'&lt;/span&gt;

                &lt;span class="c1"&gt;// 1. Upload the Artifacts&lt;/span&gt;
                &lt;span class="n"&gt;rtUpload&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;
                    &lt;span class="c1"&gt;// Use the ID 'artifactory' defined in our JCasC global config&lt;/span&gt;
                    &lt;span class="nl"&gt;serverId:&lt;/span&gt; &lt;span class="s1"&gt;'artifactory'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;

                    &lt;span class="c1"&gt;// 2. The File Spec&lt;/span&gt;
                    &lt;span class="c1"&gt;// We use Triple Double-Quotes (""") to allow variable interpolation&lt;/span&gt;
                    &lt;span class="nl"&gt;spec:&lt;/span&gt; &lt;span class="s2"&gt;"""{
                          "files": [
                            {
                              "pattern": "dist/*", 
                              "target": "generic-local/http-client/${BUILD_NUMBER}/",
                              "flat": "true"
                            }
                          ]
                    }"""&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;

                    &lt;span class="c1"&gt;// 3. The "Silent Failure" Fix&lt;/span&gt;
                    &lt;span class="c1"&gt;// Force the build to fail if no files match the pattern&lt;/span&gt;
                    &lt;span class="nl"&gt;failNoOp:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;

                    &lt;span class="c1"&gt;// Associate files with this Jenkins Job&lt;/span&gt;
                    &lt;span class="nl"&gt;buildName:&lt;/span&gt; &lt;span class="s2"&gt;"${JOB_NAME}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                    &lt;span class="nl"&gt;buildNumber:&lt;/span&gt; &lt;span class="s2"&gt;"${BUILD_NUMBER}"&lt;/span&gt;
                &lt;span class="o"&gt;)&lt;/span&gt;

                &lt;span class="c1"&gt;// 4. Publish Build Info (The "Bill of Materials")&lt;/span&gt;
                &lt;span class="n"&gt;rtPublishBuildInfo&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;
                    &lt;span class="nl"&gt;serverId:&lt;/span&gt; &lt;span class="s1"&gt;'artifactory'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                    &lt;span class="nl"&gt;buildName:&lt;/span&gt; &lt;span class="s2"&gt;"${JOB_NAME}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                    &lt;span class="nl"&gt;buildNumber:&lt;/span&gt; &lt;span class="s2"&gt;"${BUILD_NUMBER}"&lt;/span&gt;
                &lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deconstructing the Syntax Traps
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. The Interpolation Trap (&lt;code&gt;"""&lt;/code&gt; vs &lt;code&gt;'''&lt;/code&gt;)&lt;/strong&gt;&lt;br&gt;
In Jenkins Groovy, strings behave differently based on quotes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;'''Triple Single Quotes'''&lt;/code&gt;: Treat the string literally. If we used this, Jenkins would send the string &lt;code&gt;${BUILD_NUMBER}&lt;/code&gt; to Artifactory as literal text, resulting in a folder named &lt;code&gt;${BUILD_NUMBER}&lt;/code&gt; instead of &lt;code&gt;16&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;"""Triple Double Quotes"""&lt;/code&gt;: Enable &lt;strong&gt;Variable Interpolation&lt;/strong&gt;. Jenkins replaces &lt;code&gt;${BUILD_NUMBER}&lt;/code&gt; with the actual build number (e.g., &lt;code&gt;16&lt;/code&gt;) &lt;em&gt;before&lt;/em&gt; sending the command. This ensures our artifacts land in the correct versioned folder.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. The Pattern Trap (Regex vs. Wildcards)&lt;/strong&gt;&lt;br&gt;
The Artifactory File Spec supports both Regex and Wildcards. Regex is powerful but fragile. During our testing, complex regex patterns like &lt;code&gt;dist/(.*)&lt;/code&gt; failed to match files correctly without specific flags.&lt;br&gt;
We opted for the robust simplicity of &lt;strong&gt;Wildcards&lt;/strong&gt;: &lt;code&gt;"pattern": "dist/*"&lt;/code&gt;.&lt;br&gt;
Combined with &lt;code&gt;"flat": "true"&lt;/code&gt;, this tells Artifactory: "Take every file inside &lt;code&gt;dist/&lt;/code&gt;, ignore the folder structure, and upload them directly to the target path."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The "Silent Failure" Trap (&lt;code&gt;failNoOp&lt;/code&gt;)&lt;/strong&gt;&lt;br&gt;
This is the most dangerous default behavior in the Artifactory plugin. By default, if your pattern (&lt;code&gt;dist/*&lt;/code&gt;) matches &lt;strong&gt;zero files&lt;/strong&gt; (perhaps because the package stage failed silently), the &lt;code&gt;rtUpload&lt;/code&gt; step returns &lt;strong&gt;SUCCESS&lt;/strong&gt;.&lt;br&gt;
This leads to "Green Builds" that delivered nothing—a phantom release.&lt;br&gt;
We explicitly set &lt;code&gt;failNoOp: true&lt;/code&gt;. This forces the pipeline to turn &lt;strong&gt;Red&lt;/strong&gt; (Failure) if it uploads nothing. This guarantees that a successful build actually indicates a successful delivery.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. The "Chain of Custody" (&lt;code&gt;rtPublishBuildInfo&lt;/code&gt;)&lt;/strong&gt;&lt;br&gt;
The final step, &lt;code&gt;rtPublishBuildInfo&lt;/code&gt;, does not upload files. It uploads &lt;strong&gt;Metadata&lt;/strong&gt;. It scrapes the Jenkins environment (Git Commit Hash, User, Timestamp, Dependencies) and creates a JSON manifest. It sends this manifest to Artifactory, atomically linking it to the files we just uploaded. This is what allows us to trace a binary back to the source code.&lt;/p&gt;
&lt;h1&gt;
  
  
  Chapter 9: The Result - Visualizing the Supply Chain
&lt;/h1&gt;
&lt;h2&gt;
  
  
  9.1 The Artifact Browser: Exploring the Warehouse
&lt;/h2&gt;

&lt;p&gt;The infrastructure is ready, and the pipeline definition is updated. It is time to run the supply chain.&lt;/p&gt;

&lt;p&gt;Go to your Jenkins dashboard and navigate to the &lt;strong&gt;&lt;code&gt;Articles/0004_std_lib_http_client&lt;/code&gt;&lt;/strong&gt; job. Click into the &lt;strong&gt;&lt;code&gt;main&lt;/code&gt;&lt;/strong&gt; branch and hit &lt;strong&gt;"Build Now"&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Watch the console output. You will see the new &lt;strong&gt;Package&lt;/strong&gt; stage execute, compressing our artifacts into the &lt;code&gt;dist/&lt;/code&gt; directory. Then, you will see the &lt;strong&gt;Publish&lt;/strong&gt; stage kick in. Unlike our previous tests, this will not be a silent success; the logs will explicitly show the upload of our three specific files to the Artifactory server.&lt;/p&gt;

&lt;p&gt;Once the build is green, we can verify the physical results.&lt;/p&gt;

&lt;p&gt;Navigate to your Artifactory instance at &lt;code&gt;https://artifactory.cicd.local:8443&lt;/code&gt; and log in. Open the &lt;strong&gt;Application&lt;/strong&gt; module and go to &lt;strong&gt;Artifactory -&amp;gt; Artifacts&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In the tree view, expand the &lt;code&gt;generic-local&lt;/code&gt; repository. You will see a new folder structure that mirrors the logic we defined in our &lt;code&gt;Jenkinsfile&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Instead of a flat list of files, you will see a structured hierarchy: &lt;code&gt;http-client&lt;/code&gt; (the Project), followed by &lt;code&gt;16&lt;/code&gt; (the Build Number). Inside that versioned folder, you will find our three distinct products sitting side-by-side.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;generic-local/
└── http-client/
    └── 16/
        ├── http-client-cpp-sdk-1.0.0-Linux.tar.gz  (The C++ SDK)
        ├── httprust-0.1.0.crate                    (The Rust Source)
        └── httppy-0.1.0-py3-none-any.whl           (The Python Wheel)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This view validates our "Universal Bucket" strategy. We did not need to configure a complex PyPI registry, a Cargo registry, and a Conan server to get started. We successfully captured the output of a polyglot build into a single, unified, and versioned location. These files are now immutable; unlike the Jenkins workspace, they will not disappear when the next build starts.&lt;/p&gt;

&lt;h2&gt;
  
  
  9.2 The "Build Info" Ledger
&lt;/h2&gt;

&lt;p&gt;While seeing the files in the repository confirms &lt;em&gt;storage&lt;/em&gt;, it doesn't confirm &lt;em&gt;provenance&lt;/em&gt;. To see the "Chain of Custody," we need to look at the metadata generated by our pipeline's &lt;code&gt;rtPublishBuildInfo&lt;/code&gt; step.&lt;/p&gt;

&lt;p&gt;Artifactory stores this metadata as a structured JSON document in a special internal repository.&lt;/p&gt;

&lt;p&gt;To view the raw ledger entry:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Navigate to &lt;strong&gt;Application&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Artifacts&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; In the repository tree, select &lt;strong&gt;&lt;code&gt;artifactory-build-info&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; Drill down into the folder structure: &lt;strong&gt;&lt;code&gt;Articles&lt;/code&gt;&lt;/strong&gt; -&amp;gt; &lt;strong&gt;&lt;code&gt;0004_std_lib_http_client&lt;/code&gt;&lt;/strong&gt; -&amp;gt; &lt;strong&gt;&lt;code&gt;main&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; You will see a JSON file named after your build number (e.g., &lt;code&gt;16-&amp;lt;timestamp&amp;gt;.json&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt; Right-click the file and select &lt;strong&gt;View&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You will see a detailed manifest like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Articles/0004_std_lib_http_client/main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"number"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"16"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"agent"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Jenkins"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2.528.2"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"started"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2025-11-22T14:05:59.821+0000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"durationMillis"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;174180&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://jenkins.cicd.local:10400/job/Articles/job/0004_std_lib_http_client/job/main/16/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"vcs"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"revision"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"f2fc8640ad0690b94cd7b0536f0c97fcf3afb8fd"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;":bug: bug(Jenkinsfile): fix upload spec regexp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://gitlab.cicd.local:10300/articles/0004_std_lib_http_client.git"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"modules"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Articles/0004_std_lib_http_client/main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"artifacts"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"sha256"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1bd7a4407b6e2ac7c3b22eea93f6493992f172fe6f50382dd635401694945e85"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http-client-cpp-sdk-1.0.0-Linux.tar.gz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http-client/16/http-client-cpp-sdk-1.0.0-Linux.tar.gz"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"crate"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"sha256"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"7934cf01ec3331565a97de79d93c07ec1a839c749d546acf9da863c592329526"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rust-0.1.0.crate"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http-client/16/rust-0.1.0.crate"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"whl"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"sha256"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"808ffa9c2c532bcf52cf5e3c006ea825cf6e252797657068f5fbc2e506202e9d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"httppy-0.1.0-py3-none-any.whl"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http-client/16/httppy-0.1.0-py3-none-any.whl"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  9.3 Traceability: The "Chain of Custody"
&lt;/h2&gt;

&lt;p&gt;This document is the forensic evidence that links our "Factory" to our "Library."&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Source (&lt;code&gt;vcs.revision&lt;/code&gt;):&lt;/strong&gt;&lt;br&gt;
We see the exact Git Commit SHA: &lt;code&gt;f2fc8640...&lt;/code&gt;. This is the specific version of the blueprint used to manufacture this release. If a bug is found in this artifact years from now, we know exactly which line of code caused it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Process (&lt;code&gt;url&lt;/code&gt;):&lt;/strong&gt;&lt;br&gt;
We have a direct link back to the Jenkins build log: &lt;code&gt;https://jenkins.cicd.local.../16/&lt;/code&gt;. This provides the context of &lt;em&gt;how&lt;/em&gt; it was built (logs, environment variables, test results).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Product (&lt;code&gt;artifacts.sha256&lt;/code&gt;):&lt;/strong&gt;&lt;br&gt;
Most importantly, we have the cryptographic checksums of the output. &lt;code&gt;1bd7a...&lt;/code&gt; is the unique fingerprint of our C++ SDK. If a user downloads a file claiming to be version 16, we can verify its integrity against this ledger.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This completes the "Chain of Custody." We have successfully linked Binary -&amp;gt; Build -&amp;gt; Source.&lt;/p&gt;

&lt;h1&gt;
  
  
  Chapter 10: Conclusion
&lt;/h1&gt;

&lt;h2&gt;
  
  
  10.1 What We've Built: The "Software Supply Chain"
&lt;/h2&gt;

&lt;p&gt;Let's take a moment to appreciate the architectural shift we have accomplished in this article. We began with a "Factory" (Jenkins) that was incredibly efficient at manufacturing but terrible at logistics. Every time a build finished, the factory floor was scrubbed clean, and the finished products were incinerated along with the waste.&lt;/p&gt;

&lt;p&gt;By deploying &lt;strong&gt;Artifactory&lt;/strong&gt;, we have moved from a simple "Build Loop" to a true &lt;strong&gt;Software Supply Chain&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We have established a permanent, secure flow of assets through our city:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Blueprints (GitLab):&lt;/strong&gt; The source of truth for our code.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Manufacturing (Jenkins):&lt;/strong&gt; The ephemeral compute engine that compiles the code in a clean room.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Packaging (CPack/Cargo):&lt;/strong&gt; The standardization step that wraps raw binaries into consumable products (SDKs, Crates).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Warehousing (Artifactory):&lt;/strong&gt; The persistent storage layer that retains the products and the forensic data (Build Info) linking them back to the blueprints.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Our "City" is growing up. We now have a Library, a Factory, a Warehouse, and a Shared Power Plant (PostgreSQL) underpinning it all.&lt;/p&gt;

&lt;h2&gt;
  
  
  10.2 The "Blind Spot": Quantity over Quality
&lt;/h2&gt;

&lt;p&gt;However, looking at our populated &lt;code&gt;generic-local&lt;/code&gt; repository reveals a new, invisible danger. We have solved the problem of &lt;em&gt;retention&lt;/em&gt;, but we have not solved the problem of &lt;em&gt;inspection&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Our pipeline is uncritical. It assumes that if code compiles, it is worth keeping. If a developer commits code that contains a massive memory leak, a hardcoded password, or a critical security vulnerability, our pipeline will happily build it, package it, and ship it to the warehouse. We will then have a perfectly versioned, immutable, and signed package of broken software.&lt;/p&gt;

&lt;p&gt;We have built a high-speed conveyor belt that moves boxes into the warehouse. But we have no idea if the boxes contain diamonds or ticking time bombs.&lt;/p&gt;

&lt;h2&gt;
  
  
  10.3 Next Steps: The "Quality Gate"
&lt;/h2&gt;

&lt;p&gt;To solve this, we need an Inspector. We need a tool that can look inside the box &lt;em&gt;before&lt;/em&gt; we seal it. We need a system that analyzes the internal structure of the code—measuring complexity, detecting bugs, and identifying security flaws—without just relying on the compiler.&lt;/p&gt;

&lt;p&gt;In the next article, we will deploy &lt;strong&gt;SonarQube&lt;/strong&gt;. We will connect it to our Shared Database, integrate it into our Jenkins pipeline, and establish a &lt;strong&gt;Quality Gate&lt;/strong&gt;. This gate will sit between the "Build" and "Package" stages, giving it the power to stop the conveyor belt and fail the build if the code does not meet our standards. We will move from "Continuous Delivery" to "Continuous Quality."&lt;/p&gt;

</description>
      <category>cicd</category>
      <category>devops</category>
      <category>artifactory</category>
      <category>docker</category>
    </item>
    <item>
      <title>Part 04: Building a Sovereign Software Factory: Jenkins Configuration as Code (JCasC)</title>
      <dc:creator>Warren Jitsing</dc:creator>
      <pubDate>Thu, 18 Dec 2025 07:17:27 +0000</pubDate>
      <link>https://dev.to/warren_jitsing_dd1c1d6fc6/part-04-building-a-sovereign-software-factory-jenkins-configuration-as-code-jcasc-30o5</link>
      <guid>https://dev.to/warren_jitsing_dd1c1d6fc6/part-04-building-a-sovereign-software-factory-jenkins-configuration-as-code-jcasc-30o5</guid>
      <description>&lt;p&gt;GitHub: &lt;a href="https://github.com/InfiniteConsult/0008_cicd_part04_jenkins" rel="noopener noreferrer"&gt;https://github.com/InfiniteConsult/0008_cicd_part04_jenkins&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; In this installment, we construct the &lt;strong&gt;"Factory"&lt;/strong&gt; of our city. We deploy a self-hosted &lt;strong&gt;Jenkins&lt;/strong&gt; controller, but we reject the fragility of manual UI configuration. Instead, we implement a strict &lt;strong&gt;Configuration as Code (JCasC)&lt;/strong&gt; architecture, using a "Blueprint First" strategy to define our security matrix, credentials, and toolchains in version-controlled YAML. We solve the "Docker-in-Docker" permission trap by architecting a scalable &lt;strong&gt;Controller/Agent&lt;/strong&gt; model, where ephemeral "General Purpose Workers" are hired on-demand to build our polyglot code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Sovereign Software Factory Series:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Part 01:&lt;/strong&gt; Building a Sovereign Software Factory: Docker Networking &amp;amp; Persistence&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 02:&lt;/strong&gt; Building a Sovereign Software Factory: The Local Root CA &amp;amp; Trust Chains&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 03:&lt;/strong&gt; Building a Sovereign Software Factory: Self-Hosted GitLab &amp;amp; Secrets Management&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 04: Building a Sovereign Software Factory: Jenkins Configuration as Code (JCasC)&lt;/strong&gt; (You are here)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 05:&lt;/strong&gt; Building a Sovereign Software Factory: Artifactory &amp;amp; The "Strict TLS" Trap&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 06:&lt;/strong&gt; Building a Sovereign Software Factory: SonarQube Quality Gates&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 07:&lt;/strong&gt; Building a Sovereign Software Factory: ChatOps with Mattermost&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 08:&lt;/strong&gt; Building a Sovereign Software Factory: Observability with the ELK Stack&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 09:&lt;/strong&gt; Building a Sovereign Software Factory: Monitoring with Prometheus &amp;amp; Grafana&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 10:&lt;/strong&gt; Building a Sovereign Software Factory: The Python API Package (Capstone)&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;&lt;strong&gt;UPDATE 2025-12-09: Critical Plugin Change (GitLab Branch Source)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;gitlab-branch-source&lt;/code&gt; plugin has recently undergone a major update (versions &amp;gt; 736), migrating to a newer GitLab API client. This introduces a breaking change regarding authentication.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Issue:&lt;/strong&gt;&lt;br&gt;
Previously, we used a standard &lt;code&gt;usernamePassword&lt;/code&gt; credential (where the password was the token) for git checkout. On newer plugin versions, this method fails with &lt;code&gt;HTTP 403 Forbidden&lt;/code&gt; or &lt;code&gt;USER NOT FOUND&lt;/code&gt; errors because the plugin now strictly enforces credential types.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Solution:&lt;/strong&gt;&lt;br&gt;
Newer versions require specific &lt;strong&gt;GitLab Group Access Token&lt;/strong&gt; or &lt;strong&gt;GitLab Personal Access Token&lt;/strong&gt; credential types to perform checkouts. To ensure this tutorial works for all versions, we will configure &lt;strong&gt;both&lt;/strong&gt; the legacy credential and the new &lt;strong&gt;Group Access Token (GAT)&lt;/strong&gt; in our system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Action Required:&lt;/strong&gt;&lt;br&gt;
We will introduce a new token variable. Please create a &lt;strong&gt;Group Access Token&lt;/strong&gt; in GitLab (Scope: &lt;code&gt;api&lt;/code&gt;, &lt;code&gt;read_repository&lt;/code&gt;, Role: &lt;code&gt;Maintainer&lt;/code&gt;) for your top-level &lt;code&gt;Articles&lt;/code&gt; group and add it to your &lt;code&gt;cicd.env&lt;/code&gt; file alongside your existing tokens:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# cicd.env&lt;/span&gt;
&lt;span class="c"&gt;# ... existing tokens ...&lt;/span&gt;
&lt;span class="nv"&gt;ARTICLES_GAT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"glpat-m2BG...[redacted]..."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;In the following sections, we will update our scripts to pass this new token to Jenkins. When configuring your job, you can then simply select the credential type that works for your specific installed version of the plugin.&lt;/p&gt;

&lt;h1&gt;
  
  
  Chapter 1: The Challenge: Our Code is Just Sitting on a Shelf
&lt;/h1&gt;

&lt;p&gt;In our last article, we built our "Central Library," a secure, self-hosted GitLab instance. This was a massive step forward. Our code—including our polyglot "hero project"—is now version-controlled, secure, and stored in a central, trusted location. But this success has also revealed our next, and most obvious, challenge: our code is just &lt;em&gt;sitting there&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;It's inert. A developer can &lt;code&gt;git push&lt;/code&gt; a new feature, but nothing &lt;em&gt;happens&lt;/em&gt;. The connection between our code repository and an actual build process is completely missing. We have a fantastic source of truth, but no &lt;em&gt;action&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deconstructing the "It Works On My Machine" Flaw
&lt;/h2&gt;

&lt;p&gt;Without an automated system, we are forced to fall back on a manual build process. A developer would clone the repository, run &lt;code&gt;./setup.sh&lt;/code&gt; on their local workstation, and then run &lt;code&gt;./run-coverage.sh&lt;/code&gt;. This "manual build" process is fraught with problems that prevent any real, scalable development.&lt;/p&gt;

&lt;p&gt;The most famous of these is the "it works on my machine" flaw. This isn't just a meme; it's a critical business risk that stems from two core issues:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Environment Inconsistency:&lt;/strong&gt; My workstation is not identical to yours. I might be running a slightly different version of &lt;code&gt;cmake&lt;/code&gt;, a newer patch of the Rust toolchain, or a different set of Python libraries. A build that succeeds for me might fail for you simply because our environments have "drifted" apart. This makes it impossible to guarantee that a given commit is truly stable.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Lack of Auditability:&lt;/strong&gt; The manual build process is a black hole. There is no central, immutable record of &lt;em&gt;who&lt;/em&gt; built &lt;em&gt;what&lt;/em&gt;, &lt;em&gt;when&lt;/em&gt;. Which commit was used for the "release" binary that was just emailed to the client? Was it built with the latest changes? Was it built in "Debug" or "Release" mode? Did all the tests &lt;em&gt;actually&lt;/em&gt; pass, or did the developer just &lt;em&gt;say&lt;/em&gt; they did? We have no way to know.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This manual, unscalable process is unacceptable. We need a centralized, automated orchestrator that can act as the "brain" of our entire operation.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: An Automated "Factory Foreman"
&lt;/h2&gt;

&lt;p&gt;This is where we introduce &lt;strong&gt;Jenkins&lt;/strong&gt;. Jenkins will be the "Factory Foreman" for our CI/CD city.&lt;/p&gt;

&lt;p&gt;Its job is to watch our "Central Library" (GitLab) for new signals. When a developer opens a Merge Request, GitLab will send a webhook to Jenkins. Jenkins will then, in a fully automated capacity:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Receive the signal&lt;/strong&gt; from GitLab.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Provision a sterile, consistent build environment&lt;/strong&gt; (our &lt;code&gt;general-purpose-agent&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Run the standardized "assembly line"&lt;/strong&gt; that we will define in a &lt;code&gt;Jenkinsfile&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Report the &lt;code&gt;SUCCESS&lt;/code&gt; or &lt;code&gt;FAILURE&lt;/code&gt;&lt;/strong&gt; of that assembly line directly back to the GitLab Merge Request.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This single integration solves all our manual-build problems. It guarantees that every single commit is built and tested in the &lt;em&gt;exact same way&lt;/em&gt;, every single time. It provides a full, auditable log for every build, and it scales far beyond what any single developer could manage. It's the engine that will power our entire CI/CD stack.&lt;/p&gt;

&lt;h1&gt;
  
  
  Chapter 2: The "What" - Our "Controller/Agent" Architecture
&lt;/h1&gt;

&lt;p&gt;Before we can deploy Jenkins, we have to decide on its architecture. A default Jenkins installation is a single, monolithic application that does everything: it serves the UI, manages plugins, &lt;em&gt;and&lt;/em&gt; runs builds all on the same machine. This is simple, but it's a fragile and insecure design that doesn't scale.&lt;/p&gt;

&lt;p&gt;This monolithic model leads to several immediate problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Toolchain Conflicts:&lt;/strong&gt; What if "Project A" needs Java 11 but "Project B" needs Java 17? You end up polluting the controller's filesystem with conflicting dependencies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security:&lt;/strong&gt; A build script that runs on the controller has access to the controller's entire environment, including its credentials and the Docker socket. A rogue script could be disastrous.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance:&lt;/strong&gt; A heavy build (like compiling C++) can consume all the controller's CPU and RAM, making the UI slow or unresponsive for all other users.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We will build a modern, container-based architecture that solves all these problems. Our solution is built on two core concepts: "Pipeline-as-Code" and "Configuration-as-Code."&lt;/p&gt;

&lt;h2&gt;
  
  
  2.1. The "First Principle": Pipeline-as-Code (&lt;code&gt;Jenkinsfile&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;The first part of our solution is deciding where the "assembly line" instructions live. The build logic (checkout, compile, test, etc.) must be version-controlled &lt;em&gt;with&lt;/em&gt; the code it's meant to build.&lt;/p&gt;

&lt;p&gt;We will accomplish this using a &lt;strong&gt;&lt;code&gt;Jenkinsfile&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This is a plain text file, named &lt;code&gt;Jenkinsfile&lt;/code&gt;, that lives in the root of our &lt;code&gt;http-client&lt;/code&gt; repository. It defines every stage of our pipeline directly in a Groovy-based syntax. This is a fundamental shift in responsibility:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Jenkins (the Controller)&lt;/strong&gt; no longer &lt;em&gt;defines&lt;/em&gt; the build. It only &lt;em&gt;executes&lt;/em&gt; the build.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Project (the Code)&lt;/strong&gt; is now responsible for telling Jenkins &lt;em&gt;how&lt;/em&gt; it should be built.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach means our build process is now versioned, auditable, and can be reviewed in a Merge Request just like any other piece of code.&lt;/p&gt;

&lt;h2&gt;
  
  
  2.2. The "Second Principle": Configuration-as-Code (JCasC)
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;Jenkinsfile&lt;/code&gt; defines the build process for &lt;em&gt;a project&lt;/em&gt;, but what about the configuration of the &lt;em&gt;Jenkins controller itself&lt;/em&gt;? We still need to install plugins, set up security, define our API tokens, and configure our build agents.&lt;/p&gt;

&lt;p&gt;If we did this manually through the UI, we would create a &lt;strong&gt;"Snowflake Server"&lt;/strong&gt;—a fragile, unique instance that is impossible to replicate and has no audit trail.&lt;/p&gt;

&lt;p&gt;Our solution is &lt;strong&gt;Configuration-as-Code (JCasC)&lt;/strong&gt;. Just as the &lt;code&gt;Jenkinsfile&lt;/code&gt; defines the pipeline, a &lt;code&gt;jenkins.yaml&lt;/code&gt; file will define the &lt;em&gt;entire configuration of the controller&lt;/em&gt;. This YAML file will be our single source of truth for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creating our &lt;code&gt;admin&lt;/code&gt; user and setting its password.&lt;/li&gt;
&lt;li&gt;Defining our security matrix (who can do what).&lt;/li&gt;
&lt;li&gt;Installing all our plugins (GitLab, Docker, etc.).&lt;/li&gt;
&lt;li&gt;Connecting to our GitLab server.&lt;/li&gt;
&lt;li&gt;Defining our API credentials.&lt;/li&gt;
&lt;li&gt;Configuring our "cloud" of build agents.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By managing our controller's setup in this file, we make our Jenkins instance 100% reproducible, version-controlled, and auditable.&lt;/p&gt;

&lt;h2&gt;
  
  
  2.3. Our Architecture: The "Foreman and the Ephemeral Worker"
&lt;/h2&gt;

&lt;p&gt;With these two "as-Code" principles, we can now define our final architecture. We will build two separate, specialized Docker images:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;The Controller (Our "Foreman"):&lt;/strong&gt; This is our &lt;code&gt;jenkins-controller&lt;/code&gt; image. Its &lt;em&gt;only&lt;/em&gt; job is to serve the UI, manage the API, and orchestrate jobs. We will configure it with &lt;strong&gt;zero executors&lt;/strong&gt;, meaning it will &lt;em&gt;never&lt;/em&gt; run a build on its own. It is the "brain" of the operation.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The Agent (Our "Ephemeral Worker"):&lt;/strong&gt; This is our &lt;code&gt;general-purpose-agent&lt;/code&gt; image. This container is our disposable, "polyglot" toolset, pre-loaded with our entire C++, Rust, and Python toolchain.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This "Foreman/Worker" model is the core of our solution. When a build is triggered, the Controller (Foreman) will "hire" an Agent (Worker) for that one specific job.&lt;/p&gt;

&lt;h2&gt;
  
  
  2.4. The "Mechanism": The Docker Plugin
&lt;/h2&gt;

&lt;p&gt;This "hiring" process is not magic; it's a specific plugin we will configure in our &lt;code&gt;jenkins.yaml&lt;/code&gt;: the &lt;strong&gt;Docker Plugin&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This plugin is the "hiring department" for our factory. Here is how it will work:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; A developer opens a Merge Request in GitLab.&lt;/li&gt;
&lt;li&gt; GitLab sends a webhook to our "Foreman" (the Jenkins Controller).&lt;/li&gt;
&lt;li&gt; The Foreman sees the build request and consults its &lt;code&gt;jenkins.yaml&lt;/code&gt; configuration.&lt;/li&gt;
&lt;li&gt; It instructs the &lt;strong&gt;Docker Plugin&lt;/strong&gt; to provision a new agent.&lt;/li&gt;
&lt;li&gt; The Docker Plugin, using its Docker-out-of-Docker (DooD) capability, connects to the host's &lt;code&gt;docker.sock&lt;/code&gt; and issues a command:
&lt;code&gt;docker run --network cicd-net general-purpose-agent:latest ...&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt; This new "Worker" container starts, connects to the "Foreman" via our &lt;code&gt;cicd-net&lt;/code&gt;, and runs the pipeline defined in the &lt;code&gt;Jenkinsfile&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; After the build is done, the Worker reports &lt;code&gt;SUCCESS&lt;/code&gt; and is &lt;strong&gt;immediately and automatically destroyed&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This architecture is the perfect solution. It's scalable, secure (builds are isolated from the controller), and provides a clean, consistent environment for every single build.&lt;/p&gt;

&lt;h1&gt;
  
  
  Chapter 3: Architecture Deep Dive: Solving the Java &amp;amp; Docker Puzzles
&lt;/h1&gt;

&lt;p&gt;Deploying Jenkins into our secure, container-based ecosystem presents a unique set of technical hurdles. It's not as simple as our GitLab deployment. This is because Jenkins is a Java application and has its own specific, and sometimes rigid, ways of handling security and networking.&lt;/p&gt;

&lt;p&gt;We're about to solve four distinct challenges:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Securing the Jenkins UI (which can't read our &lt;code&gt;.pem&lt;/code&gt; files).&lt;/li&gt;
&lt;li&gt; Granting the Jenkins Controller secure access to the Docker socket (DooD).&lt;/li&gt;
&lt;li&gt; Making the Jenkins Controller's JVM trust our internal GitLab server.&lt;/li&gt;
&lt;li&gt; Making the Jenkins Agent's environment trust our GitLab server.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's tackle them one by one.&lt;/p&gt;




&lt;h2&gt;
  
  
  3.1. Puzzle #1: The Controller's HTTPS Keystore (&lt;code&gt;.p12&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;Our first challenge is that we can't secure the Jenkins UI the same way we secured GitLab.&lt;/p&gt;

&lt;p&gt;With GitLab, we simply mounted our &lt;code&gt;gitlab.cicd.local.crt.pem&lt;/code&gt; and &lt;code&gt;gitlab.cicd.local.key.pem&lt;/code&gt; files and told its Nginx web server to use them. This worked because Nginx is a C-based application that natively reads &lt;code&gt;.pem&lt;/code&gt; files.&lt;/p&gt;

&lt;p&gt;Jenkins, however, is a Java application. Its web server (Jetty) does not natively read separate &lt;code&gt;.pem&lt;/code&gt; certificate and key files. Instead, the Java ecosystem prefers a single, password-protected database file called a &lt;strong&gt;Java Keystore (&lt;code&gt;.jks&lt;/code&gt; or &lt;code&gt;.p12&lt;/code&gt;)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To secure our Jenkins UI at &lt;code&gt;https://jenkins.cicd.local:10400&lt;/code&gt;, we must convert our "passport" (&lt;code&gt;.pem&lt;/code&gt; certificate) and its "key" (&lt;code&gt;.key&lt;/code&gt; file) from Article 2 into this single &lt;code&gt;.p12&lt;/code&gt; file that Java understands.&lt;/p&gt;

&lt;p&gt;Our solution will be to use the &lt;code&gt;openssl pkcs12 -export&lt;/code&gt; command. This command will bundle our certificate and private key together into a new, password-protected file named &lt;code&gt;jenkins.p12&lt;/code&gt;. We will then tell Jenkins to use this file to serve HTTPS by passing its path and password in the &lt;code&gt;JENKINS_OPTS&lt;/code&gt; environment variable when we run the container.&lt;/p&gt;

&lt;h2&gt;
  
  
  3.2. Puzzle #2: The "Build-Time" DooD Fix (Controller)
&lt;/h2&gt;

&lt;p&gt;Our "Foreman" (the Jenkins Controller) needs the ability to "hire" workers. In our architecture, this means it must be able to run Docker commands to spawn our &lt;code&gt;general-purpose-agent&lt;/code&gt; containers. This requires a Docker-out-of-Docker (DooD) setup.&lt;/p&gt;

&lt;p&gt;The obvious first step is to mount the host's Docker socket: &lt;code&gt;-v /var/run/docker.sock:/var/run/docker.sock&lt;/code&gt;. However, this alone will fail.&lt;/p&gt;

&lt;p&gt;This creates an immediate and critical permissions challenge, the same one we solved back in Article 1. The &lt;code&gt;docker.sock&lt;/code&gt; file on the host machine is a protected resource. It's owned by &lt;code&gt;root&lt;/code&gt; and accessible only by members of the &lt;code&gt;docker&lt;/code&gt; group. This group has a specific Group ID (GID) on the host, for example, &lt;code&gt;998&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Inside our container, the &lt;code&gt;jenkins&lt;/code&gt; user runs with its own GID (e.g., &lt;code&gt;1000&lt;/code&gt;). When the &lt;code&gt;jenkins&lt;/code&gt; user tries to access the mounted socket, the host's kernel sees a request from GID &lt;code&gt;1000&lt;/code&gt;, compares it to the socket's required GID &lt;code&gt;998&lt;/code&gt;, and denies access.&lt;/p&gt;

&lt;p&gt;We will solve this not at runtime, but at &lt;em&gt;build time&lt;/em&gt;. This is a cleaner, more permanent solution.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Our &lt;code&gt;02-build-images.sh&lt;/code&gt; script will first inspect the host and find the numerical GID of its &lt;code&gt;docker&lt;/code&gt; group.&lt;/li&gt;
&lt;li&gt; It will pass this number into the &lt;code&gt;docker build&lt;/code&gt; command using the &lt;code&gt;--build-arg HOST_DOCKER_GID&lt;/code&gt; flag.&lt;/li&gt;
&lt;li&gt; Inside our &lt;code&gt;Dockerfile.controller&lt;/code&gt;, we will receive this argument. We will then execute a &lt;code&gt;RUN&lt;/code&gt; command that:

&lt;ul&gt;
&lt;li&gt;Installs the &lt;code&gt;docker-ce-cli&lt;/code&gt; package.&lt;/li&gt;
&lt;li&gt;Creates a new &lt;code&gt;docker&lt;/code&gt; group &lt;em&gt;inside the container&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Crucially, it uses the &lt;code&gt;HOST_DOCKER_GID&lt;/code&gt; to &lt;strong&gt;set the new group's GID to the exact same number as the host&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Finally, it adds the &lt;code&gt;jenkins&lt;/code&gt; user to this newly created, correctly-ID'd group.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By baking this permission into the image itself, we create a controller that is &lt;em&gt;born&lt;/em&gt; with the correct permissions. This makes our deployment script much cleaner, as we no longer need to use runtime flags like &lt;code&gt;--group-add&lt;/code&gt; to modify the user's identity.&lt;/p&gt;

&lt;h2&gt;
  
  
  3.3. Puzzle #3: The "Baked-in" JVM Trust (Controller)
&lt;/h2&gt;

&lt;p&gt;This is one of the most significant challenges in the entire stack. Even with our DooD permissions fixed, our controller will still fail.&lt;/p&gt;

&lt;p&gt;The moment our &lt;code&gt;gitlab-branch-source&lt;/code&gt; plugin (which we'll configure in JCasC) tries to scan our repository at &lt;code&gt;https://gitlab.cicd.local&lt;/code&gt;, the build will fail with a &lt;code&gt;javax.net.ssl.SSLHandshakeException&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The reason for this is that the &lt;strong&gt;Java Virtual Machine (JVM) maintains its own, isolated trust store&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When we built our controller image, we fixed the &lt;em&gt;operating system's&lt;/em&gt; trust store by running &lt;code&gt;update-ca-certificates&lt;/code&gt;. This is great for OS-level tools like &lt;code&gt;git&lt;/code&gt; or &lt;code&gt;curl&lt;/code&gt;. However, the Jenkins controller is a Java application. The JVM &lt;em&gt;completely ignores&lt;/em&gt; the OS trust store and only trusts the certificates found inside its own &lt;code&gt;cacerts&lt;/code&gt; file (a Java-specific keystore).&lt;/p&gt;

&lt;p&gt;Our internal CA is not in that file, so from the JVM's perspective, our &lt;code&gt;gitlab.cicd.local&lt;/code&gt; certificate is untrusted, and it will refuse all SSL connections.&lt;/p&gt;

&lt;p&gt;Our solution is to "bake" our CA's trust directly into the controller's JVM. During our &lt;code&gt;Dockerfile.controller&lt;/code&gt; build, right after we &lt;code&gt;COPY&lt;/code&gt; our &lt;code&gt;ca.pem&lt;/code&gt; file, we will add a &lt;code&gt;RUN&lt;/code&gt; command. This command will use the Java &lt;code&gt;keytool&lt;/code&gt; utility—the standard tool for managing Java keystores—to import our &lt;code&gt;ca.pem&lt;/code&gt; directly into the JVM's master &lt;code&gt;cacerts&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;This permanently solves the problem. Our custom &lt;code&gt;jenkins-controller&lt;/code&gt; image will now be born with a JVM that implicitly trusts every certificate (like GitLab's) that our internal CA has signed. This will allow all Java-based plugins to make secure HTTPS connections to our other internal services without any errors.&lt;/p&gt;

&lt;h2&gt;
  
  
  3.4. Puzzle #4: The Agent's "Dual Trust"
&lt;/h2&gt;

&lt;p&gt;Finally, we must solve the trust problem for our ephemeral agents. It's a common oversight to assume that because the controller trusts GitLab, the agents will too. But the agent is a completely separate container, with its own filesystem, its own OS, and its own trust stores.&lt;/p&gt;

&lt;p&gt;When our pipeline kicks off, the &lt;em&gt;agent&lt;/em&gt; is the container that actually runs the &lt;code&gt;git clone&lt;/code&gt; command against &lt;code&gt;https://gitlab.cicd.local&lt;/code&gt;. If the agent doesn't trust our CA, the clone will immediately fail with an SSL verification error.&lt;/p&gt;

&lt;p&gt;This reveals a new, more complex challenge: the agent has a "dual trust" requirement. It needs to trust our internal CA in &lt;strong&gt;two different places&lt;/strong&gt; to satisfy our "polyglot" toolchain:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;OS-Level Trust (for &lt;code&gt;git&lt;/code&gt;):&lt;/strong&gt; The &lt;code&gt;git&lt;/code&gt; command is a standard Linux application. It relies on the operating system's trust store (the certificates in &lt;code&gt;/usr/local/share/ca-certificates/&lt;/code&gt;) to validate the SSL connection.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;JVM-Level Trust (for &lt;code&gt;sonar-scanner&lt;/code&gt;):&lt;/strong&gt; Our pipeline will &lt;em&gt;also&lt;/em&gt; run Java-based tools. The most important one for our stack will be the &lt;code&gt;sonar-scanner&lt;/code&gt; CLI (for our future SonarQube article). Just like the Jenkins controller, this is a Java tool that &lt;em&gt;completely ignores&lt;/em&gt; the OS trust store and relies &lt;em&gt;only&lt;/em&gt; on its own &lt;code&gt;cacerts&lt;/code&gt; file.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A failure in either store will break our pipeline. The &lt;code&gt;git clone&lt;/code&gt; would fail, or the quality scan would fail.&lt;/p&gt;

&lt;p&gt;Therefore, our &lt;code&gt;Dockerfile.agent&lt;/code&gt; must solve &lt;em&gt;both&lt;/em&gt; problems simultaneously. We will add a build step that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;code&gt;COPY&lt;/code&gt;-ies our &lt;code&gt;ca.pem&lt;/code&gt; file into the build context.&lt;/li&gt;
&lt;li&gt; Runs &lt;code&gt;update-ca-certificates&lt;/code&gt; to add the CA to the OS trust store. This fixes &lt;code&gt;git&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; Runs the Java &lt;code&gt;keytool -importcert&lt;/code&gt; command to add the &lt;em&gt;same&lt;/em&gt; CA to the JVM's &lt;code&gt;cacerts&lt;/code&gt; file. This fixes &lt;code&gt;sonar-scanner&lt;/code&gt; and any other Java-based tools.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By baking in this "dual trust," our &lt;code&gt;general-purpose-agent&lt;/code&gt; will be born ready to communicate securely with all other services in our stack, regardless of the tool being used.&lt;/p&gt;

&lt;h1&gt;
  
  
  Chapter 4: Action Plan (Part 1) – The "Blueprints" (Dockerfiles &amp;amp; Plugins)
&lt;/h1&gt;

&lt;p&gt;With our architecture defined and our key integration challenges solved, we can now start building our two Docker images. We'll start by defining the "blueprints" for our controller and agent.&lt;/p&gt;

&lt;p&gt;The first step is to create the "shopping list" of plugins our controller will need. This file, &lt;code&gt;plugins.txt&lt;/code&gt;, will be fed to the Jenkins plugin installer during our image build, ensuring our controller is born with all the "office furniture" it needs to do its job.&lt;/p&gt;




&lt;h2&gt;
  
  
  4.1. The "Plugin List" (&lt;code&gt;plugins.txt&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;This file is a simple list of plugin IDs and their versions (we'll use &lt;code&gt;latest&lt;/code&gt; for simplicity). Each one adds a critical piece of functionality that enables our specific architecture.&lt;/p&gt;

&lt;p&gt;Here is the complete &lt;code&gt;plugins.txt&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#
# -----------------------------------------------------------
#                      plugins.txt
#
# This file lists all plugins to be installed by the
# 'jenkins-plugin-cli' during our image build.
#
# Each plugin is a piece of "office furniture" for our
# "Foreman," giving it new capabilities.
# -----------------------------------------------------------

# --- Dependencies and standard plugins ---
pipeline-model-definition:latest
cloudbees-folder:latest
antisamy-markup-formatter:latest
build-timeout:latest
credentials-binding:latest
timestamper:latest
ws-cleanup:latest
ant:latest
gradle:latest
workflow-aggregator:latest
github-branch-source:latest
pipeline-github-lib:latest
pipeline-graph-analysis:latest
git:latest
ssh-slaves:latest
ldap:latest
email-ext:latest
mailer:latest
dark-theme:latest

# --- Core Setup &amp;amp; Configuration ---

# The "Blueprint Reader": Allows us to configure Jenkins using
# our 'jenkins.yaml' file. The core of JCasC.
configuration-as-code:latest

# The "Security Guard": Allows us to create the granular
# "who-can-do-what" permissions matrix.
matrix-auth:latest

# --- CI/CD Integration Plugins ---

# The "Red Phone": The bridge to GitLab. This plugin
# allows Jenkins to receive webhooks from GitLab and
# provides the "Multibranch Pipeline" job type.
gitlab-branch-source:latest

# The "Hiring Department": This is the Docker Cloud plugin.
# It's what allows our controller to "spawn" our
# 'general-purpose-agent' containers.
docker-plugin:latest

# The "Secure Warehouse" Connector: Provides the native
# 'rtUpload' steps to publish our artifacts to Artifactory.
artifactory:latest

# The "Quality Inspector" Connector: Provides the
# 'withSonarQubeEnv' and 'waitForQualityGate' steps.
sonar:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deconstructing &lt;code&gt;plugins.txt&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Here are the key plugins we're installing and what they do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;configuration-as-code&lt;/code&gt;:&lt;/strong&gt; This is the core of our "Configuration-as-Code" (JCasC) strategy. It's the plugin that will read our &lt;code&gt;jenkins.yaml&lt;/code&gt; file on boot and configure the entire Jenkins instance, from security realms to API tokens.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;matrix-auth&lt;/code&gt;:&lt;/strong&gt; This is the "security guard" plugin. It provides the granular, grid-based permission system (&lt;code&gt;globalMatrix&lt;/code&gt;) that our &lt;code&gt;jenkins.yaml&lt;/code&gt; file will configure. This is what allows us to lock down anonymous users while granting full rights to our &lt;code&gt;admin&lt;/code&gt; user.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;gitlab-branch-source&lt;/code&gt;:&lt;/strong&gt; This is the modern, official "bridge" to GitLab. It provides two essential features:

&lt;ol&gt;
&lt;li&gt; The &lt;strong&gt;"Multibranch Pipeline"&lt;/strong&gt; job type, which can scan a GitLab project and build all its branches.&lt;/li&gt;
&lt;li&gt; The webhook endpoint (&lt;code&gt;/gitlab-webhook/trigger&lt;/code&gt;) that allows GitLab to notify Jenkins of new commits and merge requests instantly.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;docker-plugin&lt;/code&gt;:&lt;/strong&gt; This is our "hiring department." It's the plugin that allows the controller to connect to the Docker socket, interpret our "cloud" configuration from &lt;code&gt;jenkins.yaml&lt;/code&gt;, and physically spawn our &lt;code&gt;general-purpose-agent&lt;/code&gt; containers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;pipeline-model-definition&lt;/code&gt;:&lt;/strong&gt; This is a crucial dependency we discovered during our research. The &lt;code&gt;docker-plugin&lt;/code&gt; will not load correctly without it, as it provides the core APIs for defining the declarative pipelines that our agent-based model relies on.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4.2. The "Foreman's Office" (&lt;code&gt;Dockerfile.controller&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;With our plugin list defined, we can now write the blueprint for our "Foreman" or &lt;code&gt;jenkins-controller&lt;/code&gt;. This &lt;code&gt;Dockerfile&lt;/code&gt; is a surgical script designed to solve our specific architectural challenges &lt;em&gt;at build time&lt;/em&gt;, resulting in a clean, secure, and ready-to-run image.&lt;/p&gt;

&lt;p&gt;Here is the complete &lt;code&gt;Dockerfile.controller&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;#               Dockerfile.controller&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  This blueprint builds our "Foreman" (Jenkins Controller).&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  1. DooD: Installs Docker CLI &amp;amp; fixes GID permissions.&lt;/span&gt;
&lt;span class="c"&gt;#  2. CA Trust: Bakes in our Local CA to the JVM trust store.&lt;/span&gt;
&lt;span class="c"&gt;#  3. Plugins: Installs all plugins from 'plugins.txt'.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;

&lt;span class="c"&gt;# 1. Start from the official Jenkins Long-Term Support image&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; jenkins/jenkins:lts-jdk21&lt;/span&gt;
&lt;span class="k"&gt;LABEL&lt;/span&gt;&lt;span class="s"&gt; authors="warren_jitsing"&lt;/span&gt;

&lt;span class="c"&gt;# 2. Accept the host's 'docker' GID as a build-time argument&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; HOST_DOCKER_GID&lt;/span&gt;

&lt;span class="c"&gt;# 3. Switch to 'root' to install software and fix permissions&lt;/span&gt;
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; root&lt;/span&gt;

&lt;span class="c"&gt;# 4. Copy our Local CA cert from the build context&lt;/span&gt;
&lt;span class="c"&gt;# (Our '01-setup-jenkins.sh' will place this file here)&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; ca.pem /tmp/ca.pem&lt;/span&gt;

&lt;span class="c"&gt;# 5. Fix CA Trust (for both OS/git and JVM)&lt;/span&gt;
&lt;span class="c"&gt;# This is "baked in" for simplicity and robustness&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;cp&lt;/span&gt; /tmp/ca.pem /usr/local/share/ca-certificates/cicd-stack-ca.crt &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; update-ca-certificates &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; keytool &lt;span class="nt"&gt;-importcert&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;        &lt;span class="nt"&gt;-keystore&lt;/span&gt; /opt/java/openjdk/lib/security/cacerts &lt;span class="se"&gt;\
&lt;/span&gt;        &lt;span class="nt"&gt;-storepass&lt;/span&gt; changeit &lt;span class="se"&gt;\
&lt;/span&gt;        &lt;span class="nt"&gt;-file&lt;/span&gt; /tmp/ca.pem &lt;span class="se"&gt;\
&lt;/span&gt;        &lt;span class="nt"&gt;-alias&lt;/span&gt; &lt;span class="s2"&gt;"CICD-Root-CA"&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;        &lt;span class="nt"&gt;-noprompt&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; /tmp/ca.pem

&lt;span class="c"&gt;# 6. Install Docker CLI &amp;amp; Fix GID Mismatch&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nt"&gt;--no-install-recommends&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;        curl &lt;span class="se"&gt;\
&lt;/span&gt;        gnupg &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; 0755 &lt;span class="nt"&gt;-d&lt;/span&gt; /etc/apt/keyrings &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://download.docker.com/linux/debian/gpg &lt;span class="nt"&gt;-o&lt;/span&gt; /etc/apt/keyrings/docker.asc &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;chmod &lt;/span&gt;a+r /etc/apt/keyrings/docker.asc &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;      &lt;span class="s2"&gt;"deb [arch=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;dpkg &lt;span class="nt"&gt;--print-architecture&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian &lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;&lt;span class="s2"&gt;      &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; /etc/os-release &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$VERSION_CODENAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; stable"&lt;/span&gt; | &lt;span class="se"&gt;\
&lt;/span&gt;      &lt;span class="nb"&gt;tee&lt;/span&gt; /etc/apt/sources.list.d/docker.list &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nt"&gt;--no-install-recommends&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;        docker-ce-cli &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; groupadd &lt;span class="nt"&gt;--gid&lt;/span&gt; &lt;span class="nv"&gt;$HOST_DOCKER_GID&lt;/span&gt; docker &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; usermod &lt;span class="nt"&gt;-aG&lt;/span&gt; docker jenkins &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get clean &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class="k"&gt;*&lt;/span&gt;

&lt;span class="c"&gt;# 7. Switch back to the unprivileged 'jenkins' user&lt;/span&gt;
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; jenkins&lt;/span&gt;

&lt;span class="c"&gt;# 8. Copy our plugin list into the official ref directory&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; plugins.txt /usr/share/jenkins/ref/plugins.txt&lt;/span&gt;

&lt;span class="c"&gt;# 9. Run the official plugin installer script&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;jenkins-plugin-cli &lt;span class="nt"&gt;-f&lt;/span&gt; /usr/share/jenkins/ref/plugins.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Deconstructing &lt;code&gt;Dockerfile.controller&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Let's walk through this blueprint layer by layer to understand how it solves our specific challenges.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;FROM jenkins/jenkins:lts-jdk21&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
We start with the official Long-Term Support (LTS) image for the Jenkins controller. This provides a trusted, stable foundation with a recent Java version.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;ARG HOST_DOCKER_GID&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
This instruction defines a build-time argument. It's how we'll pass the host's Docker group ID into the image when we run our &lt;code&gt;02-build-images.sh&lt;/code&gt; script.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;USER root&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
We switch to the &lt;code&gt;root&lt;/code&gt; user because we need to perform two critical, system-level installations that the standard &lt;code&gt;jenkins&lt;/code&gt; user doesn't have permission to do.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Step 5: Fix CA Trust&lt;/strong&gt;&lt;br&gt;
This &lt;code&gt;RUN&lt;/code&gt; command solves our &lt;strong&gt;"JVM Trust" puzzle&lt;/strong&gt;. It &lt;code&gt;COPY&lt;/code&gt;-ies the &lt;code&gt;ca.pem&lt;/code&gt; we staged in our setup script and:&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1.  Runs `update-ca-certificates` to add our CA to the **Operating System's** trust store.
2.  Runs the Java `keytool` command to import the *same certificate* into the **JVM's** master `cacerts` file.
    By "baking" this trust into the image, we permanently solve any `SSLHandshakeException` errors. Our controller is now born with a JVM that implicitly trusts our internal GitLab server.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Step 6: Install Docker CLI &amp;amp; Fix GID Mismatch&lt;/strong&gt;
This &lt;code&gt;RUN&lt;/code&gt; command solves our &lt;strong&gt;"DooD Permission" puzzle&lt;/strong&gt;. It's a single, multi-step command that:&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1.  Adds the official Docker APT repository.
2.  Installs the `docker-ce-cli` package, giving our controller the `docker` command.
3.  **Fixes the GID.** This is the key. It executes `groupadd --gid $HOST_DOCKER_GID docker` to create a new `docker` group *inside the container* that has the *exact same numerical GID* as the one on our host.
4.  Finally, it adds the `jenkins` user to this new, correctly-ID'd group.
    This is a permanent, clean solution. By solving the permission issue at the image level, we don't have to use any special `--group-add` flags in our `docker run` command.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;USER jenkins&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
With all our system-level modifications complete, we switch back to the low-privilege &lt;code&gt;jenkins&lt;/code&gt; user for the rest of the build.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Steps 8 &amp;amp; 9: Install Plugins&lt;/strong&gt;&lt;br&gt;
This is the final step. We &lt;code&gt;COPY&lt;/code&gt; our &lt;code&gt;plugins.txt&lt;/code&gt; "shopping list" into the official location Jenkins uses for pre-installing plugins. We then &lt;code&gt;RUN&lt;/code&gt; the &lt;code&gt;jenkins-plugin-cli&lt;/code&gt; script, which reads this list and installs every plugin. This ensures our controller boots up for the first time with all the tools (JCasC, Docker, GitLab) it needs.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4.3. The "Factory Worker" (&lt;code&gt;Dockerfile.agent&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;This is the blueprint for our &lt;code&gt;general-purpose-agent&lt;/code&gt;, our "polyglot" factory worker. This image is the real workhorse of our pipeline. Its entire purpose is to be a self-contained, pre-loaded toolset, ready to build our complex "hero project" with zero setup.&lt;/p&gt;

&lt;p&gt;We start &lt;code&gt;FROM jenkins/agent:latest-jdk21&lt;/code&gt;. This is a crucial starting point. Unlike the &lt;code&gt;jenkins/jenkins&lt;/code&gt; controller image, this one is a bare-bones Debian base that includes the Java JDK and the &lt;code&gt;agent.jar&lt;/code&gt; file, which is responsible for communicating with the controller.&lt;/p&gt;

&lt;p&gt;Here is the complete &lt;code&gt;Dockerfile.agent&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;#                    Dockerfile.agent&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  This blueprint builds our "General Purpose Worker".&lt;/span&gt;
&lt;span class="c"&gt;#  It starts from the base Jenkins agent image and "harvests"&lt;/span&gt;
&lt;span class="c"&gt;#  all the complex toolchains from our dev-container to&lt;/span&gt;
&lt;span class="c"&gt;#  create a "polyglot" builder.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  It is a "cattle" image: stateless, disposable, but&lt;/span&gt;
&lt;span class="c"&gt;#  loaded with all the tools our "hero project" needs.&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;

&lt;span class="c"&gt;# 1. Start from the official Jenkins agent image (Debian 12 + JDK 21)&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; jenkins/agent:latest-jdk21&lt;/span&gt;
&lt;span class="k"&gt;LABEL&lt;/span&gt;&lt;span class="s"&gt; authors="warren_jitsing"&lt;/span&gt;

&lt;span class="c"&gt;# 2. Get ARGs from our dev-container for custom builds&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; py312="3.12.12"&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; py313="3.13.9"&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; py314="3.14.0"&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; gcc15="15.2.0"&lt;/span&gt;

&lt;span class="c"&gt;# 3. Switch to 'root' to install everything&lt;/span&gt;
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; root&lt;/span&gt;

&lt;span class="c"&gt;# 4. Copy our Local CA cert from the build context&lt;/span&gt;
&lt;span class="c"&gt;# (Our '01-setup-jenkins.sh' will place this file here)&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; ca.pem /tmp/ca.pem&lt;/span&gt;

&lt;span class="c"&gt;# 5. Install all OS dependencies (harvested from dev-container + hero project)&lt;/span&gt;
&lt;span class="c"&gt;# This includes base tools, "hero project" deps, and Python build deps&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nt"&gt;--no-install-recommends&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    build-essential ca-certificates cmake curl flex &lt;span class="se"&gt;\
&lt;/span&gt;    git git-lfs gnupg2 &lt;span class="nb"&gt;sudo &lt;/span&gt;wget unzip &lt;span class="se"&gt;\
&lt;/span&gt;    llvm lcov hyperfine libcurl4-openssl-dev libboost-all-dev &lt;span class="se"&gt;\
&lt;/span&gt;    libbz2-dev libffi-dev libgdbm-compat-dev libgdbm-dev liblzma-dev &lt;span class="se"&gt;\
&lt;/span&gt;    libncurses5-dev libreadline-dev libsqlite3-dev libssl-dev &lt;span class="se"&gt;\
&lt;/span&gt;    python3-dev python3-pip python3-tk uuid-dev zlib1g-dev &lt;span class="se"&gt;\
&lt;/span&gt;    m4 patch pkg-config procps &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get clean &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class="k"&gt;*&lt;/span&gt;

&lt;span class="c"&gt;# 6. Build and install custom GCC&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /tmp/deps/gcc &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; /tmp/deps/gcc &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; &lt;span class="s2"&gt;"https://github.com/gcc-mirror/gcc/archive/refs/tags/releases/gcc-&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;gcc15&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.tar.gz"&lt;/span&gt; | &lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xz&lt;/span&gt; &lt;span class="nt"&gt;--strip-components&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; ./contrib/download_prerequisites &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;mkdir &lt;/span&gt;build &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;build &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; ../configure &lt;span class="nt"&gt;--disable-multilib&lt;/span&gt; &lt;span class="nt"&gt;--enable-languages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;c,c++ &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; make &lt;span class="nt"&gt;-j&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;nproc&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; make &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; / &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /tmp/deps

&lt;span class="c"&gt;# 7. Add our new custom GCC to the system-wide PATH&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'export PATH="/usr/local/bin:${PATH}"'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /etc/profile.d/gcc.sh &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'export LD_LIBRARY_PATH="/usr/local/lib64:${LD_LIBRARY_PATH}"'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /etc/profile.d/gcc.sh

&lt;span class="c"&gt;# 8. Build and install custom Python versions&lt;/span&gt;
&lt;span class="k"&gt;RUN for &lt;/span&gt;version &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$py312&lt;/span&gt; &lt;span class="nv"&gt;$py313&lt;/span&gt; &lt;span class="nv"&gt;$py314&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Building Python version &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;version&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; ---"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;        &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /tmp/deps/python&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;        &lt;span class="nb"&gt;cd&lt;/span&gt; /tmp/deps/python&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;        curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; &lt;span class="s2"&gt;"https://github.com/python/cpython/archive/refs/tags/v&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;version&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.tar.gz"&lt;/span&gt; | &lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xz&lt;/span&gt; &lt;span class="nt"&gt;--strip-components&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;        &lt;span class="nv"&gt;CONFIGURE_FLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"--enable-optimizations --enable-loadable-sqlite-extensions --with-lto=full"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$version&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$py313&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$version&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$py314&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;            &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Adding --disable-gil flag for nogil build ---"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;            &lt;span class="nv"&gt;CONFIGURE_FLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CONFIGURE_FLAGS&lt;/span&gt;&lt;span class="s2"&gt; --disable-gil"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;        &lt;span class="k"&gt;fi&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;        ./configure &lt;span class="nv"&gt;$CONFIGURE_FLAGS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;        make &lt;span class="nt"&gt;-j&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;nproc&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;        make altinstall&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;        &lt;span class="nb"&gt;cd&lt;/span&gt; / &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="k"&gt;done&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /tmp/deps

&lt;span class="c"&gt;# 9. Install SonarScanner CLI (as root)&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;wget &lt;span class="nt"&gt;-O&lt;/span&gt; /tmp/sonar.zip &lt;span class="s2"&gt;"https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-7.3.0.5189-linux-x64.zip"&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; unzip /tmp/sonar.zip &lt;span class="nt"&gt;-d&lt;/span&gt; /opt &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;mv&lt;/span&gt; /opt/sonar-scanner-&lt;span class="k"&gt;*&lt;/span&gt; /opt/sonar-scanner &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;ln&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; /opt/sonar-scanner/bin/sonar-scanner /usr/bin/sonar-scanner &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; /tmp/sonar.zip

&lt;span class="c"&gt;# 10. Fix CA Trust (for both OS/git and JVM) (as root)&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;cp&lt;/span&gt; /tmp/ca.pem /usr/local/share/ca-certificates/cicd-stack-ca.crt &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; update-ca-certificates &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; keytool &lt;span class="nt"&gt;-importcert&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;        &lt;span class="nt"&gt;-keystore&lt;/span&gt; /opt/java/openjdk/lib/security/cacerts &lt;span class="se"&gt;\
&lt;/span&gt;        &lt;span class="nt"&gt;-storepass&lt;/span&gt; changeit &lt;span class="se"&gt;\
&lt;/span&gt;        &lt;span class="nt"&gt;-file&lt;/span&gt; /tmp/ca.pem &lt;span class="se"&gt;\
&lt;/span&gt;        &lt;span class="nt"&gt;-alias&lt;/span&gt; &lt;span class="s2"&gt;"CICD-Root-CA"&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;        &lt;span class="nt"&gt;-noprompt&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; /tmp/ca.pem

&lt;span class="c"&gt;# 11. Switch to the default agent user&lt;/span&gt;
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; jenkins&lt;/span&gt;

&lt;span class="c"&gt;# 12. Install user-specific toolchains (Rust and Julia)&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;curl https://sh.rustup.rs &lt;span class="nt"&gt;-sSf&lt;/span&gt; | sh &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://install.julialang.org | sh &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt;

&lt;span class="c"&gt;# 13. Install cargo-llvm-cov&lt;/span&gt;
&lt;span class="c"&gt;# We must source the env file in the *same* command&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.cargo/env"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; cargo &lt;span class="nb"&gt;install &lt;/span&gt;cargo-llvm-cov

&lt;span class="c"&gt;# 14. Add the new toolchains to the container's PATH&lt;/span&gt;
&lt;span class="c"&gt;# This ENV instruction makes them available to all subsequent&lt;/span&gt;
&lt;span class="c"&gt;# Jenkins 'sh' steps.&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PATH="/home/jenkins/.cargo/bin:/home/jenkins/.juliaup/bin:${PATH}"&lt;/span&gt;

&lt;span class="c"&gt;# 15. Set the Entrypoint (must be last)&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["java", "-jar", "/usr/share/jenkins/agent.jar"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Deconstructing &lt;code&gt;Dockerfile.agent&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This &lt;code&gt;Dockerfile&lt;/code&gt; is more complex than the controller's because it's responsible for building our actual "polyglot" toolchain.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Steps 1-8: Harvesting the Toolchain&lt;/strong&gt;&lt;br&gt;
Just like our controller, we switch to &lt;code&gt;root&lt;/code&gt; to install system software. The &lt;code&gt;apt-get&lt;/code&gt; command is a "harvested" list from our &lt;code&gt;dev-container&lt;/code&gt;, containing all the C++, Boost, and Python dependencies our "hero project" needs. We then run the &lt;em&gt;exact same&lt;/em&gt; build-from-source commands for &lt;strong&gt;GCC&lt;/strong&gt; and &lt;strong&gt;Python&lt;/strong&gt;. This is the key to solving the "it works on my machine" problem: our build agent's toolchain is now identical to our development environment's.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Step 9: Pre-installing Future Tools&lt;/strong&gt;&lt;br&gt;
We also take this opportunity to install the &lt;code&gt;sonar-scanner&lt;/code&gt; CLI. We aren't using it yet, but pre-installing it here means our agent will be ready for our SonarQube (Quality Assurance) article without needing another rebuild.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Step 10: The "Dual Trust" Fix&lt;/strong&gt;&lt;br&gt;
This &lt;code&gt;RUN&lt;/code&gt; command is critical. It solves the trust problem for &lt;em&gt;both&lt;/em&gt; toolchains on the agent:&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1.  `update-ca-certificates`: This makes the **Operating System** (and thus tools like `git` and `curl`) trust our internal CA.
2.  `keytool -importcert...`: This makes the **JVM** (and thus Java-based tools like `sonar-scanner`) trust our internal CA.
    Without this "dual fix," either our `git clone` or our future quality scan would fail.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Steps 11-14: The User-Context Switch&lt;/strong&gt;&lt;br&gt;
This is the solution to our &lt;code&gt;cargo: command not found&lt;/code&gt; debugging session. We switch to the &lt;code&gt;USER jenkins&lt;/code&gt; &lt;em&gt;before&lt;/em&gt; installing user-space tools. The &lt;code&gt;rustup&lt;/code&gt; and &lt;code&gt;juliaup&lt;/code&gt; installers now correctly place their binaries in &lt;code&gt;/home/jenkins/.cargo/bin&lt;/code&gt;. We then use the &lt;code&gt;ENV&lt;/code&gt; instruction to permanently add this new directory to the &lt;code&gt;jenkins&lt;/code&gt; user's &lt;code&gt;PATH&lt;/code&gt;, making &lt;code&gt;cargo&lt;/code&gt; and &lt;code&gt;rustc&lt;/code&gt; available to all pipeline steps.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Step 15: The &lt;code&gt;ENTRYPOINT&lt;/code&gt; Fix&lt;/strong&gt;&lt;br&gt;
This is the final, and most important, instruction. Our investigation with &lt;code&gt;docker image inspect&lt;/code&gt; revealed that the &lt;code&gt;jenkins/agent&lt;/code&gt; base image has no &lt;code&gt;ENTRYPOINT&lt;/code&gt;. This was the cause of our &lt;code&gt;exec: "-url": executable file not found&lt;/code&gt; error, as the Docker plugin was passing its connection arguments as a command. This line "promotes" our image from a simple "bag of tools" to a functional, executable agent. This Java command is the program that will run, consume the connection arguments, and successfully link our worker to the controller.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Chapter 5: Action Plan (Part 2) – The "Factory Layout" (JCasC)
&lt;/h1&gt;

&lt;p&gt;With our two "blueprints" (&lt;code&gt;Dockerfile.controller&lt;/code&gt; and &lt;code&gt;Dockerfile.agent&lt;/code&gt;) designed, we now need to write the "master plan" that tells the Jenkins controller how to operate. This is our Configuration-as-Code (JCasC) file, &lt;code&gt;jenkins.yaml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This file is the "factory layout" for our "Foreman's office." It defines everything: where the doors are, who has keys, what tools are in the cabinets, and how to contact the "hiring department."&lt;/p&gt;

&lt;p&gt;We won't write this file by hand. Instead, our &lt;code&gt;01-setup-jenkins.sh&lt;/code&gt; script will &lt;em&gt;generate&lt;/em&gt; it. This allows us to programmatically inject values like our port numbers and use environment variables for our secrets. This approach gives us the best of both worlds: a clean, version-controlled YAML structure and a secure way to handle sensitive passwords and tokens.&lt;/p&gt;

&lt;p&gt;Let's deconstruct the &lt;code&gt;jenkins.yaml&lt;/code&gt; file that our setup script will create.&lt;/p&gt;




&lt;h2&gt;
  
  
  5.1. The &lt;code&gt;jenkins.yaml&lt;/code&gt; File
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The &lt;code&gt;jenkins:&lt;/code&gt; Block: Core Configuration
&lt;/h3&gt;

&lt;p&gt;This is the main block for the controller itself.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jenkins&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;systemMessage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Jenkins&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Controller&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;CI/CD&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Stack&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;${HOSTNAME}"&lt;/span&gt;
  &lt;span class="na"&gt;numExecutors&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
  &lt;span class="na"&gt;slaveAgentPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10401&lt;/span&gt;
  &lt;span class="na"&gt;securityRealm&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;local&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;allowsSignup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="na"&gt;users&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;admin"&lt;/span&gt;
          &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${JENKINS_ADMIN_PASSWORD}"&lt;/span&gt;
  &lt;span class="na"&gt;authorizationStrategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;globalMatrix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;entries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;admin"&lt;/span&gt;
            &lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Overall/Administer"&lt;/span&gt;
        &lt;span class="c1"&gt;# ... (other permissions) ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's what we're defining:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;numExecutors: 0&lt;/code&gt;&lt;/strong&gt;: This is the most important setting for our architecture. We are explicitly telling the "Foreman" (Controller) that it is &lt;strong&gt;not allowed to run any builds itself&lt;/strong&gt;. Its executor count is zero. This enforces our "Controller/Agent" model, ensuring all work is delegated to ephemeral agents.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;slaveAgentPort: 10401&lt;/code&gt;&lt;/strong&gt;: This defines the internal port our agents will use to connect back to the controller (via the JNLP protocol). We define this here in JCasC, which is the modern, correct way to set it. We'll then expose this port in our &lt;code&gt;docker run&lt;/code&gt; command.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;securityRealm:&lt;/code&gt;&lt;/strong&gt;: This block creates our users. We define a local &lt;code&gt;admin&lt;/code&gt; user and set its password by reading from the &lt;code&gt;${JENKINS_ADMIN_PASSWORD}&lt;/code&gt; environment variable. This variable will be passed into the container from our &lt;code&gt;jenkins.env&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;authorizationStrategy:&lt;/code&gt;&lt;/strong&gt;: This is where our &lt;code&gt;matrix-auth&lt;/code&gt; plugin comes in. We use &lt;code&gt;globalMatrix&lt;/code&gt; to grant our &lt;code&gt;admin&lt;/code&gt; user full &lt;code&gt;Overall/Administer&lt;/code&gt; permissions. This locks down the instance so that only our &lt;code&gt;admin&lt;/code&gt; user can do anything, securing Jenkins from the moment it boots.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The &lt;code&gt;credentials:&lt;/code&gt; Block: The "Key Cabinet"
&lt;/h3&gt;

&lt;p&gt;This block is at the &lt;strong&gt;root level&lt;/strong&gt; (not nested under &lt;code&gt;jenkins:&lt;/code&gt;). This is one of those syntax details we discovered through debugging. This block defines all the secret tokens our Jenkins system needs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;system&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;domainCredentials&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;string&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gitlab-api-token"&lt;/span&gt;
              &lt;span class="na"&gt;scope&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GLOBAL&lt;/span&gt;
              &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GitLab&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;API&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Token&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;for&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Jenkins"&lt;/span&gt;
              &lt;span class="na"&gt;secret&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${GITLAB_API_TOKEN}"&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;usernamePassword&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gitlab-checkout-credentials"&lt;/span&gt;
              &lt;span class="na"&gt;scope&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GLOBAL&lt;/span&gt;
              &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GitLab&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Project&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Token&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;for&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;repo&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;checkout"&lt;/span&gt;
              &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gitlab-checkout-bot"&lt;/span&gt;
              &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${GITLAB_CHECKOUT_TOKEN}"&lt;/span&gt;
          &lt;span class="c1"&gt;# NEW: Modern credential for Plugin &amp;gt; v736&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;gitlabGroupAccessToken&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;articles-gat"&lt;/span&gt;
              &lt;span class="na"&gt;scope&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GLOBAL&lt;/span&gt;
              &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${ARTICLES_GAT}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we are creating two critical credentials:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;gitlab-api-token&lt;/code&gt;&lt;/strong&gt;: This is a &lt;code&gt;string&lt;/code&gt; (or secret text) credential. It holds our "all-powerful" GitLab PAT. The controller itself will use this token for API calls, like scanning repositories or reporting build status.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;gitlab-checkout-credentials&lt;/code&gt;&lt;/strong&gt;: This is a &lt;code&gt;usernamePassword&lt;/code&gt; credential. It holds the &lt;em&gt;Project Access Token&lt;/em&gt; we created for our &lt;code&gt;http-client&lt;/code&gt; project. The &lt;code&gt;username&lt;/code&gt; is just a descriptive label, and the &lt;code&gt;password&lt;/code&gt; is the token itself (read from &lt;code&gt;${GITLAB_CHECKOUT_TOKEN}&lt;/code&gt;). Our pipeline job will use this to &lt;code&gt;git clone&lt;/code&gt; the private repository.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;articles-gat&lt;/code&gt;&lt;/strong&gt; &lt;strong&gt;(New for 2025)&lt;/strong&gt;: Newer versions of the gitlab-branch-source plugin (v736+) require a strictly typed Group Access Token. We define this as a gitlabGroupAccessToken credential using the ${ARTICLES_GAT} variable. This ensures that if you are using a modern plugin version, you will have a valid credential available for checkout that avoids the 403 Forbidden error.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The &lt;code&gt;unclassified:&lt;/code&gt; Block: The "GitLab Bridge"
&lt;/h3&gt;

&lt;p&gt;This is another root-level block, and its name is not obvious. &lt;code&gt;unclassified&lt;/code&gt; is the JCasC key for plugins that don't have their own, cleaner top-level key. Here, we configure the &lt;code&gt;gitlab-branch-source&lt;/code&gt; plugin.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;unclassified&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;gitLabServers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;servers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Local&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;GitLab"&lt;/span&gt;
        &lt;span class="na"&gt;serverUrl&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://gitlab.cicd.local:10300"&lt;/span&gt;
        &lt;span class="na"&gt;credentialsId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gitlab-api-token"&lt;/span&gt;
        &lt;span class="na"&gt;manageWebHooks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This configuration tells Jenkins:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There is one GitLab server, and its name is "Local GitLab".&lt;/li&gt;
&lt;li&gt;Its URL is &lt;code&gt;https://gitlab.cicd.local:10300&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;To authenticate against its API, it should use the &lt;code&gt;gitlab-api-token&lt;/code&gt; credential we just defined.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;manageWebHooks: true&lt;/code&gt;: This is a powerful setting. It gives Jenkins permission to &lt;em&gt;automatically&lt;/em&gt; create webhooks in our GitLab projects for us.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The &lt;code&gt;clouds:&lt;/code&gt; Block: The "Hiring Department"
&lt;/h3&gt;

&lt;p&gt;This final root-level block is the most complex and important. It's where we configure the &lt;code&gt;docker-plugin&lt;/code&gt; and define our entire "Foreman/Worker" dynamic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;clouds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;docker&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;docker-local"&lt;/span&gt;
      &lt;span class="na"&gt;dockerApi&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;dockerHost&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;uri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unix:///var/run/docker.sock"&lt;/span&gt;
      &lt;span class="na"&gt;templates&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;general-purpose-agent"&lt;/span&gt;
          &lt;span class="na"&gt;labelString&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;general-purpose-agent"&lt;/span&gt;
          &lt;span class="na"&gt;remoteFs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/home/jenkins/agent"&lt;/span&gt;
          &lt;span class="na"&gt;pullStrategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;PULL_NEVER'&lt;/span&gt;
          &lt;span class="na"&gt;dockerCommand&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
          &lt;span class="na"&gt;removeVolumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
          &lt;span class="na"&gt;dockerTemplateBase&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;general-purpose-agent:latest"&lt;/span&gt;
            &lt;span class="na"&gt;network&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cicd-net"&lt;/span&gt;
          &lt;span class="na"&gt;connector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;jnlp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;jenkinsUrl&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://jenkins.cicd.local:10400/"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's deconstruct this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;dockerApi:&lt;/code&gt;&lt;/strong&gt;: We point the plugin to the DooD socket at &lt;code&gt;unix:///var/run/docker.sock&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;templates:&lt;/code&gt;&lt;/strong&gt;: This is the list of "worker types" our Foreman can hire. We define one:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;labelString: "general-purpose-agent"&lt;/code&gt;&lt;/strong&gt;: This is the "job title." Our &lt;code&gt;Jenkinsfile&lt;/code&gt; will request a worker with this label (&lt;code&gt;agent { label 'general-purpose-agent' }&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;pullStrategy: 'PULL_NEVER'&lt;/code&gt;&lt;/strong&gt;: This was a key discovery. It tells Jenkins to &lt;em&gt;never&lt;/em&gt; try to pull the image from Docker Hub and to &lt;em&gt;only&lt;/em&gt; use the local &lt;code&gt;general-purpose-agent:latest&lt;/code&gt; image we built.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;dockerCommand: ""&lt;/code&gt;&lt;/strong&gt;: This was our &lt;em&gt;other&lt;/em&gt; major debugging fix. It tells the plugin to &lt;em&gt;not&lt;/em&gt; override the container's &lt;code&gt;ENTRYPOINT&lt;/code&gt;, which is what we thought would finally solve our &lt;code&gt;exec: "-url": executable file not found&lt;/code&gt; error.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;removeVolumes: true&lt;/code&gt;&lt;/strong&gt;: This is a cleanup fix. It tells Jenkins to delete the agent's anonymous volumes when the container is removed, preventing our host from filling up with orphaned volumes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;dockerTemplateBase:&lt;/code&gt;&lt;/strong&gt;: This defines the agent's runtime.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;image: "general-purpose-agent:latest"&lt;/code&gt;&lt;/strong&gt;: The name of our custom-built image.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;network: "cicd-net"&lt;/code&gt;&lt;/strong&gt;: This is critical. It connects our agent to our private "city" network, allowing it to resolve &lt;code&gt;gitlab.cicd.local&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;&lt;code&gt;connector:&lt;/code&gt;&lt;/strong&gt;: This tells the agent &lt;em&gt;how&lt;/em&gt; to find the Foreman. We give it the full JNLP URL: &lt;code&gt;&lt;a href="https://jenkins.cicd.local:10400/" rel="noopener noreferrer"&gt;https://jenkins.cicd.local:10400/&lt;/a&gt;&lt;/code&gt;.&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  A Critical Note on JCasC and Credentials
&lt;/h3&gt;

&lt;p&gt;This &lt;code&gt;credentials:&lt;/code&gt; block highlights a fundamental, and critical, part of our Configuration-as-Code architecture that we discovered during our testing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. The JCasC File is the Single Source of Truth&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Our &lt;code&gt;jenkins.yaml&lt;/code&gt; file is the &lt;em&gt;authoritative&lt;/em&gt; definition of our Jenkins configuration. When the controller starts, the JCasC plugin reads this file and forces the system's configuration to match it.&lt;/p&gt;

&lt;p&gt;This means &lt;strong&gt;any credentials you add manually via the UI will be lost&lt;/strong&gt; when the container is restarted or re-deployed. We experienced this ourselves: after manually adding our &lt;code&gt;gitlab-checkout-credentials&lt;/code&gt; in the UI, a simple container restart wiped them out, causing our builds to fail again.&lt;/p&gt;

&lt;p&gt;The only way to create &lt;em&gt;permanent&lt;/em&gt; credentials that survive restarts is to define them here, in our &lt;code&gt;jenkins.yaml&lt;/code&gt; blueprint.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Understanding GitLab Tokens in JCasC&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In our file, we are using two &lt;em&gt;different&lt;/em&gt; credential &lt;em&gt;kinds&lt;/em&gt; for two different GitLab tokens. This is a deliberate choice for our architecture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;string&lt;/code&gt; (for &lt;code&gt;gitlab-api-token&lt;/code&gt;):&lt;/strong&gt; This is a &lt;strong&gt;Personal Access Token (PAT)&lt;/strong&gt; with &lt;code&gt;api&lt;/code&gt; scope. It's used by the &lt;code&gt;gitlab-branch-source&lt;/code&gt; plugin itself to scan repositories and report build statuses. The plugin is designed to read a &lt;code&gt;string&lt;/code&gt; (or "Secret Text") credential.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;usernamePassword&lt;/code&gt; (for &lt;code&gt;gitlab-checkout-credentials&lt;/code&gt;):&lt;/strong&gt; This is a &lt;strong&gt;Project Access Token (PAT)&lt;/strong&gt; with a much more limited &lt;code&gt;read_repository&lt;/code&gt; scope. This is the token our pipeline's &lt;code&gt;git clone&lt;/code&gt; command will use. We use the &lt;code&gt;usernamePassword&lt;/code&gt; type because it's what the Git checkout step expects.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The &lt;code&gt;username&lt;/code&gt; is just a label.&lt;/strong&gt; GitLab's token auth does not care what is in the &lt;code&gt;username&lt;/code&gt; field (&lt;code&gt;gitlab-checkout-bot&lt;/code&gt;). It only cares about the token. We are putting the token in the &lt;code&gt;password&lt;/code&gt; field, which is what &lt;code&gt;git&lt;/code&gt; will present to the server.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;You can use a Personal, Group, or Project token for the &lt;code&gt;usernamePassword&lt;/code&gt; credential. Our choice to use a &lt;strong&gt;Project Access Token&lt;/strong&gt; here is an example of the &lt;strong&gt;Principle of Least Privilege&lt;/strong&gt;: the build pipeline only gets permission to &lt;em&gt;read this one project&lt;/em&gt;, not our entire GitLab instance.&lt;/p&gt;

&lt;h1&gt;
  
  
  Chapter 6: Action Plan (Part 3) – The "Architect, Build, Deploy" Scripts
&lt;/h1&gt;

&lt;p&gt;With our "blueprints" (&lt;code&gt;Dockerfiles&lt;/code&gt;) and "factory layout" (the &lt;code&gt;jenkins.yaml&lt;/code&gt; design) complete, it's time to execute our plan. We will use a three-script "Architect, Build, Deploy" pattern, just as we did for our previous services.&lt;/p&gt;

&lt;p&gt;This separates our logic cleanly:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;01-setup-jenkins.sh&lt;/code&gt;&lt;/strong&gt;: The "Architect" script that runs on the host to &lt;em&gt;prepare&lt;/em&gt; all configuration.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;02-build-images.sh&lt;/code&gt;&lt;/strong&gt;: The "Build" script that &lt;em&gt;creates&lt;/em&gt; our custom Docker images.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;03-deploy-controller.sh&lt;/code&gt;&lt;/strong&gt;: The "Deploy" script that &lt;em&gt;runs&lt;/em&gt; the Jenkins controller.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's start with the "Architect."&lt;/p&gt;

&lt;h2&gt;
  
  
  6.1. The "Architect" Script (&lt;code&gt;01-setup-jenkins.sh&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;This is our master setup script. Its sole purpose is to run on the host machine and generate &lt;em&gt;all&lt;/em&gt; the configuration "artifacts" our other scripts and Dockerfiles will need. It's the "blueprint processor" that assembles all our plans and secrets into ready-to-use files.&lt;/p&gt;

&lt;p&gt;Here is the complete script.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;#               01-setup-jenkins.sh&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  This is the master "architect" script for Jenkins.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  It runs *once* on the host machine to generate all the&lt;/span&gt;
&lt;span class="c"&gt;#  "blueprints" (JCasC, etc.) needed to deploy our Controller.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  1. Scoped Env: Creates a 'jenkins.env' file.&lt;/span&gt;
&lt;span class="c"&gt;#  2. Keystore:   Generates the 'jenkins.p12' Java keystore.&lt;/span&gt;
&lt;span class="c"&gt;#  3. JCasC:      Writes the 'jenkins.yaml' file with the&lt;/span&gt;
&lt;span class="c"&gt;#                 correct Docker Cloud syntax.&lt;/span&gt;
&lt;span class="c"&gt;#  4. CA Trust:   Copies 'ca.pem' into the build context&lt;/span&gt;
&lt;span class="c"&gt;#     for both Dockerfiles to use.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🚀 Starting Jenkins 'Architect' Setup..."&lt;/span&gt;

&lt;span class="c"&gt;# --- 1. Define Core Paths &amp;amp; Variables ---&lt;/span&gt;

&lt;span class="c"&gt;# This is our main config directory from Article 1&lt;/span&gt;
&lt;span class="nv"&gt;JENKINS_CONFIG_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/cicd_stack/jenkins/config"&lt;/span&gt;

&lt;span class="c"&gt;# This is our Jenkins build context (current directory)&lt;/span&gt;
&lt;span class="nv"&gt;BUILD_CONTEXT_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Master "Secrets" file from Article 1&lt;/span&gt;
&lt;span class="nv"&gt;MASTER_ENV_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/cicd_stack/cicd.env"&lt;/span&gt;

&lt;span class="c"&gt;# Source the master secrets file to load them into this script&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"⛔ ERROR: Master env file not found at &lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi
&lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MASTER_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# --- 2. Define Ports &amp;amp; Passwords ---&lt;/span&gt;
&lt;span class="c"&gt;# We will use the 104xx block for Jenkins&lt;/span&gt;
&lt;span class="nv"&gt;JENKINS_HTTPS_PORT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"10400"&lt;/span&gt;
&lt;span class="nv"&gt;JENKINS_JNLP_PORT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"10401"&lt;/span&gt;

&lt;span class="c"&gt;# Generate passwords if they aren't set in the env file&lt;/span&gt;
: &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;JENKINS_ADMIN_PASSWORD&lt;/span&gt;:&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"admin-&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;openssl rand &lt;span class="nt"&gt;-hex&lt;/span&gt; 8&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
: &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;JENKINS_KEYSTORE_PASSWORD&lt;/span&gt;:&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"key-&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;openssl rand &lt;span class="nt"&gt;-hex&lt;/span&gt; 12&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🔧 Jenkins Ports Set:"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   - UI (HTTPS): &lt;/span&gt;&lt;span class="nv"&gt;$JENKINS_HTTPS_PORT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   - Agent (JNLP): &lt;/span&gt;&lt;span class="nv"&gt;$JENKINS_JNLP_PORT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# --- 3. Create "Scoped" jenkins.env File ---&lt;/span&gt;
&lt;span class="c"&gt;# This is our "Least Privilege" secrets file for the container&lt;/span&gt;
&lt;span class="nv"&gt;SCOPED_ENV_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BUILD_CONTEXT_DIR&lt;/span&gt;&lt;span class="s2"&gt;/jenkins.env"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🔑 Creating scoped 'jenkins.env' file..."&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt; "&lt;/span&gt;&lt;span class="nv"&gt;$SCOPED_ENV_FILE&lt;/span&gt;&lt;span class="sh"&gt;"
# This file is auto-generated by 01-setup-jenkins.sh
# It contains *only* the secrets needed by Jenkins.

# --- JCasC Variables ---
JENKINS_ADMIN_PASSWORD=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;JENKINS_ADMIN_PASSWORD&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;
GITLAB_API_TOKEN=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GITLAB_API_TOKEN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;
GITLAB_CHECKOUT_TOKEN=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GITLAB_CHECKOUT_TOKEN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;
ARTICLES_GAT=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ARTICLES_GAT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;

# --- Keystore Password ---
JENKINS_KEYSTORE_PASSWORD=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;JENKINS_KEYSTORE_PASSWORD&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-qxF&lt;/span&gt; &lt;span class="s2"&gt;"jenkins.env"&lt;/span&gt; .gitignore &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"jenkins.env"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; .gitignore
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Done."&lt;/span&gt;

&lt;span class="c"&gt;# --- 4. Prepare Certificate Assets ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🔐 Preparing SSL certificates..."&lt;/span&gt;

&lt;span class="c"&gt;# Create directories for the Keystore&lt;/span&gt;
&lt;span class="nv"&gt;SSL_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JENKINS_CONFIG_DIR&lt;/span&gt;&lt;span class="s2"&gt;/ssl"&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SSL_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Define CA and Service Cert paths from Article 2&lt;/span&gt;
&lt;span class="nv"&gt;CA_CERT_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/cicd_stack/ca/pki/certs/ca.pem"&lt;/span&gt;
&lt;span class="nv"&gt;SERVICE_CERT_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/cicd_stack/ca/pki/services/jenkins.cicd.local/jenkins.cicd.local.crt.pem"&lt;/span&gt;
&lt;span class="nv"&gt;SERVICE_KEY_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/cicd_stack/ca/pki/services/jenkins.cicd.local/jenkins.cicd.local.key.pem"&lt;/span&gt;
&lt;span class="nv"&gt;P12_KEYSTORE_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SSL_DIR&lt;/span&gt;&lt;span class="s2"&gt;/jenkins.p12"&lt;/span&gt;

&lt;span class="c"&gt;# 4a. Copy 'ca.pem' into our build context for both Dockerfiles&lt;/span&gt;
&lt;span class="c"&gt;# This will be .gitignore'd&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CA_CERT_PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BUILD_CONTEXT_DIR&lt;/span&gt;&lt;span class="s2"&gt;/ca.pem"&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-qxF&lt;/span&gt; &lt;span class="s2"&gt;"ca.pem"&lt;/span&gt; .gitignore &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ca.pem"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; .gitignore

&lt;span class="c"&gt;# 4b. Create the .p12 Java Keystore for the Controller's UI&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Generating 'jenkins.p12' Java Keystore..."&lt;/span&gt;
openssl pkcs12 &lt;span class="nt"&gt;-export&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-in&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SERVICE_CERT_PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-inkey&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SERVICE_KEY_PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-name&lt;/span&gt; jenkins &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-out&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$P12_KEYSTORE_PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-passout&lt;/span&gt; &lt;span class="s2"&gt;"pass:&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;JENKINS_KEYSTORE_PASSWORD&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Done."&lt;/span&gt;


&lt;span class="c"&gt;# --- 5. Generate JCasC 'jenkins.yaml' ---&lt;/span&gt;
&lt;span class="nv"&gt;JCAS_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JENKINS_CONFIG_DIR&lt;/span&gt;&lt;span class="s2"&gt;/jenkins.yaml"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"📝 Generating 'jenkins.yaml' (JCasC) blueprint..."&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt; "&lt;/span&gt;&lt;span class="nv"&gt;$JCAS_FILE&lt;/span&gt;&lt;span class="sh"&gt;"
#
# This file is auto-generated by 01-setup-jenkins.sh
# It is the "factory layout" (JCasC) for our Jenkins Controller.
#
jenkins:
  # Set the system message
  systemMessage: "Jenkins Controller - CI/CD Stack - &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOSTNAME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"

  # This is the fix for the "built-in node" security warning
  numExecutors: 0

  # Configure our JNLP agent port
  slaveAgentPort: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;JENKINS_JNLP_PORT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;

  # --- Security Configuration ---
  # Use the modern, nested 'entries' syntax
  authorizationStrategy:
    globalMatrix:
      entries:
        # Grant 'admin' full administrator permissions
        - user:
            name: "admin"
            permissions:
              - "Overall/Administer"
              - "Overall/Read"
              - "Agent/Build"
              - "Agent/Configure"
              - "Agent/Connect"
              - "Agent/Create"
              - "Agent/Delete"
              - "Agent/Disconnect"
              - "Credentials/Create"
              - "Credentials/Delete"
              - "Credentials/ManageDomains"
              - "Credentials/Update"
              - "Credentials/View"
              - "Job/Build"
              - "Job/Cancel"
              - "Job/Configure"
              - "Job/Create"
              - "Job/Delete"
              - "Job/Discover"
              - "Job/Move"
              - "Job/Read"
              - "Job/Workspace"
              - "Run/Delete"
              - "Run/Replay"
              - "Run/Update"
              - "View/Configure"
              - "View/Create"
              - "View/Delete"
              - "View/Read"
        # Grant 'anonymous' read-only access
        - group:
            name: "anonymous"
            permissions:
              - "Overall/Read"
              - "Job/Read"
              - "Job/Discover"
        # Grant 'authenticated' (all logged-in users) basic job rights
        - group:
            name: "authenticated"
            permissions:
              - "Overall/Read"
              - "Job/Read"
              - "Job/Build"
              - "Job/Discover"

  # 'securityRealm' is a child of 'jenkins:'
  securityRealm:
    local:
      allowsSignup: false
      users:
        # Create our 'admin' user with the password from our .env file
        - id: "admin"
          password: "&lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="sh"&gt;{JENKINS_ADMIN_PASSWORD}"

  # --- Cloud Configuration (The "Hiring Department") ---
  # 'clouds' is a valid top-level attribute of 'jenkins:'
  clouds:
    - docker:
        name: "docker-local"
        # Point to the DooD socket (permissions fixed in Dockerfile)
        dockerApi:
          dockerHost:
            uri: "unix:///var/run/docker.sock"
        # Define our "General Purpose Worker" template
        templates:
          - name: "general-purpose-agent"
            # This is the correct attribute for the label
            labelString: "general-purpose-agent"
            # The agent's working directory
            remoteFs: "/home/jenkins/agent"
            pullStrategy: 'PULL_NEVER'
            dockerCommand: ""
            removeVolumes: true
            # All base properties are nested inside dockerTemplateBase
            dockerTemplateBase:
              # The custom image we built
              image: "general-purpose-agent:latest"
              # The "road network" for our city
              network: "cicd-net"
            # The connector stays at the top level
            connector:
              jnlp:
                # This 'jenkinsUrl' is for the *agent* to find the controller
                jenkinsUrl: "https://jenkins.cicd.local:&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;JENKINS_HTTPS_PORT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;/"

# --- Tool Configuration ---
# This 'tool' block is at the ROOT level, not inside 'jenkins:'
tool:
  git:
    installations:
      - name: "Default"
        home: "git"

# --- Credentials Configuration ---
credentials:
  system:
    domainCredentials:
      - credentials:
          - string:
              id: "gitlab-api-token"
              scope: GLOBAL
              description: "GitLab API Token for Jenkins"
              secret: "&lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="sh"&gt;{GITLAB_API_TOKEN}"
          - usernamePassword:
              id: "gitlab-checkout-credentials"
              scope: GLOBAL
              description: "GitLab Project Token for repo checkout"
              username: "gitlab-checkout-bot"
              password: "&lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="sh"&gt;{GITLAB_CHECKOUT_TOKEN}"
          # NEW: Modern credential for Plugin &amp;gt; v736
          - gitlabGroupAccessToken:
              id: "articles-gat"
              scope: GLOBAL
              token: "&lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="sh"&gt;{ARTICLES_GAT}"              

# --- Plugin Configuration (The "Bridge" to GitLab) ---
unclassified:
  # The correct JCasC root for 'gitlab-branch-source' is 'gitLabServers'
  gitLabServers:
    servers:
      - name: "Local GitLab"
        serverUrl: "https://gitlab.cicd.local:10300"
        credentialsId: "gitlab-api-token"
        # We don't need 'clientBuilderId' for this plugin,
        # but we do need to enable hook management.
        manageWebHooks: true
        manageSystemHooks: false
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Done."&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✅ Jenkins 'Architect' setup is complete."&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   All blueprints (JCasC, env) are generated."&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   All certs are staged in the correct locations."&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   You can now run '02-build-images.sh'."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deconstructing the "Architect" Script
&lt;/h3&gt;

&lt;p&gt;This script performs five critical setup tasks before we ever build an image or run a container.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Sources the Master &lt;code&gt;cicd.env&lt;/code&gt; File:&lt;/strong&gt; It pulls in all our master secrets (like &lt;code&gt;GITLAB_API_TOKEN&lt;/code&gt;) so it can use them to populate other files.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Creates a "Scoped" &lt;code&gt;jenkins.env&lt;/code&gt; File:&lt;/strong&gt; This is a key security practice. Instead of passing all our secrets to the controller, this creates a new &lt;code&gt;jenkins.env&lt;/code&gt; file that contains &lt;em&gt;only&lt;/em&gt; the secrets Jenkins needs: its admin password, the GitLab API token, the checkout token, and the keystore password. This file is what we'll pass to our container.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Copies the &lt;code&gt;ca.pem&lt;/code&gt;:&lt;/strong&gt; It copies our CA certificate from Article 2 into the current directory. Our &lt;code&gt;Dockerfile.controller&lt;/code&gt; and &lt;code&gt;Dockerfile.agent&lt;/code&gt; will &lt;code&gt;COPY&lt;/code&gt; this file to "bake in" trust.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Generates the &lt;code&gt;.p12&lt;/code&gt; Keystore:&lt;/strong&gt; This is the Java SSL solution from Chapter 3. It runs the &lt;code&gt;openssl pkcs12 -export&lt;/code&gt; command, bundling our &lt;code&gt;jenkins.cicd.local&lt;/code&gt; certificate and key into the &lt;code&gt;jenkins.p12&lt;/code&gt; file that the controller's web server can understand.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Generates the &lt;code&gt;jenkins.yaml&lt;/code&gt; JCasC File:&lt;/strong&gt; This is the script's main job. It writes our entire "factory layout" to &lt;code&gt;jenkins.yaml&lt;/code&gt;, programmatically inserting our port numbers (&lt;code&gt;$JENKINS_HTTPS_PORT&lt;/code&gt;) and using the &lt;code&gt;\${VARIABLE}&lt;/code&gt; syntax to template the secrets. This generated file is the complete, final blueprint for our controller.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  6.2. The "Build" Script (&lt;code&gt;02-build-images.sh&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;With our "Architect" script having prepared all the blueprints and staged the &lt;code&gt;ca.pem&lt;/code&gt; file, we're ready to "build" our images. This script's job is to run the &lt;code&gt;docker build&lt;/code&gt; commands, feeding our &lt;code&gt;Dockerfiles&lt;/code&gt; all the build-time arguments they need.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;#               02-build-images.sh&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  This is the "build" script. It builds our two custom&lt;/span&gt;
&lt;span class="c"&gt;#  Docker images:&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  1. jenkins-controller: The "Foreman" (UI)&lt;/span&gt;
&lt;span class="c"&gt;#  2. general-purpose-agent: The "Worker" (Build Tools)&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  It's responsible for finding the host's 'docker' GID&lt;/span&gt;
&lt;span class="c"&gt;#  and passing all the correct build-time arguments to&lt;/span&gt;
&lt;span class="c"&gt;#  each Dockerfile.&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🚀 Starting Jenkins Image Build..."&lt;/span&gt;

&lt;span class="c"&gt;# --- 1. Find Host Docker GID ---&lt;/span&gt;
&lt;span class="c"&gt;# We need this for the "build-time" GID fix in Dockerfile.controller&lt;/span&gt;
&lt;span class="nv"&gt;HOST_DOCKER_GID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;getent group docker | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;: &lt;span class="nt"&gt;-f3&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOST_DOCKER_GID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"⛔ ERROR: 'docker' group not found on host."&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Please ensure the docker group exists and your user is a member."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi
&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🔧 Host 'docker' GID found: &lt;/span&gt;&lt;span class="nv"&gt;$HOST_DOCKER_GID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# --- 2. Define Toolchain Build Arguments ---&lt;/span&gt;
&lt;span class="c"&gt;# These ARGs must match what Dockerfile.agent expects&lt;/span&gt;
&lt;span class="nv"&gt;PY312&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"3.12.12"&lt;/span&gt;
&lt;span class="nv"&gt;PY313&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"3.13.9"&lt;/span&gt;
&lt;span class="nv"&gt;PY314&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"3.14.0"&lt;/span&gt;
&lt;span class="nv"&gt;GCC15&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"15.2.0"&lt;/span&gt;

&lt;span class="c"&gt;# --- 3. Build the Controller Image ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Building 'jenkins-controller:latest' ---"&lt;/span&gt;
docker build &lt;span class="nt"&gt;--progress&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;plain &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--build-arg&lt;/span&gt; &lt;span class="nv"&gt;HOST_DOCKER_GID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HOST_DOCKER_GID&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-f&lt;/span&gt; Dockerfile.controller &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-t&lt;/span&gt; jenkins-controller:latest &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✅ 'jenkins-controller' build complete."&lt;/span&gt;


&lt;span class="c"&gt;# --- 4. Build the Agent Image ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Building 'general-purpose-agent:latest' ---"&lt;/span&gt;
docker build &lt;span class="nt"&gt;--progress&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;plain &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--build-arg&lt;/span&gt; &lt;span class="nv"&gt;py312&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$PY312&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--build-arg&lt;/span&gt; &lt;span class="nv"&gt;py313&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$PY313&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--build-arg&lt;/span&gt; &lt;span class="nv"&gt;py314&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$PY314&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--build-arg&lt;/span&gt; &lt;span class="nv"&gt;gcc15&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$GCC15&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-f&lt;/span&gt; Dockerfile.agent &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-t&lt;/span&gt; general-purpose-agent:latest &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✅ 'general-purpose-agent' build complete."&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🎉 Both Jenkins images are built and ready."&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   You can now run '03-deploy-controller.sh'."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deconstructing the "Build" Script
&lt;/h3&gt;

&lt;p&gt;This script is straightforward but performs one vital task:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Find Host Docker GID:&lt;/strong&gt; The script starts by finding the numerical GID of the &lt;code&gt;docker&lt;/code&gt; group on the host. This is the solution to our DooD permission challenge.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Build the Controller Image:&lt;/strong&gt; It runs the first &lt;code&gt;docker build&lt;/code&gt; command, using &lt;code&gt;-f Dockerfile.controller&lt;/code&gt; to specify our "Foreman" blueprint. Critically, it passes the &lt;code&gt;--build-arg HOST_DOCKER_GID=$HOST_DOCKER_GID&lt;/code&gt; flag. This injects the host's GID into the build, allowing our &lt;code&gt;Dockerfile.controller&lt;/code&gt; to "bake in" the correct permissions.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Build the Agent Image:&lt;/strong&gt; It runs the second &lt;code&gt;docker build&lt;/code&gt; command, using &lt;code&gt;-f Dockerfile.agent&lt;/code&gt;. This build doesn't need the GID but &lt;em&gt;does&lt;/em&gt; need the toolchain version arguments (&lt;code&gt;PY312&lt;/code&gt;, &lt;code&gt;GCC15&lt;/code&gt;, etc.), which we've hardcoded here to match our &lt;code&gt;dev-container&lt;/code&gt; environment.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With this script, we now have two custom images, &lt;code&gt;jenkins-controller:latest&lt;/code&gt; and &lt;code&gt;general-purpose-agent:latest&lt;/code&gt;, built locally and ready for deployment.&lt;/p&gt;




&lt;h2&gt;
  
  
  6.3. The "Deploy" Script (&lt;code&gt;03-deploy-controller.sh&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;This is the final assembly. This script takes our newly built &lt;code&gt;jenkins-controller&lt;/code&gt; image and launches it as a container, connecting all the pieces we've built across this entire series. It connects our network, mounts our configs, passes our secrets, and enables our security.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;#               03-deploy-controller.sh&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  This is the "deploy" script. It runs the 'docker run'&lt;/span&gt;
&lt;span class="c"&gt;#  command to launch our 'jenkins-controller' container.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  It's responsible for connecting all our "first principles"&lt;/span&gt;
&lt;span class="c"&gt;#  components together:&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  1. Network:    Connects to 'cicd-net' with hostname 'jenkins'.&lt;/span&gt;
&lt;span class="c"&gt;#  2. Ports:      Publishes the UI (10400) and Agent (10401) ports.&lt;/span&gt;
&lt;span class="c"&gt;#  3. Secrets:    Passes the *scoped* 'jenkins.env' file.&lt;/span&gt;
&lt;span class="c"&gt;#  4. Volumes:    Mounts our JCasC config, our .p12 keystore,&lt;/span&gt;
&lt;span class="c"&gt;#                 the 'jenkins-home' data volume, and the&lt;/span&gt;
&lt;span class="c"&gt;#                 'docker.sock' for DooD.&lt;/span&gt;
&lt;span class="c"&gt;#  5. HTTPS:      Passes 'JENKINS_OPTS' to enable SSL using&lt;/span&gt;
&lt;span class="c"&gt;#                 our .p12 keystore and its password.&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🚀 Deploying Jenkins Controller..."&lt;/span&gt;

&lt;span class="c"&gt;# --- 1. Define Paths ---&lt;/span&gt;
&lt;span class="nv"&gt;JENKINS_CONFIG_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/cicd_stack/jenkins/config"&lt;/span&gt;
&lt;span class="nv"&gt;SCOPED_ENV_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;/jenkins.env"&lt;/span&gt;

&lt;span class="c"&gt;# --- 2. Stop and Remove Old Container (if it exists) ---&lt;/span&gt;
&lt;span class="c"&gt;# This ensures a clean start&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;jenkins-controller&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Stopping existing 'jenkins-controller'..."&lt;/span&gt;
    docker stop jenkins-controller
&lt;span class="k"&gt;fi
if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;-aq&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;jenkins-controller&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Removing existing 'jenkins-controller'..."&lt;/span&gt;
    docker &lt;span class="nb"&gt;rm &lt;/span&gt;jenkins-controller
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# --- 3. Source Keystore Password from Scoped Env File ---&lt;/span&gt;
&lt;span class="c"&gt;# We need this *one* variable on the host to build the JENKINS_OPTS string&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SCOPED_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"⛔ ERROR: Scoped 'jenkins.env' file not found."&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Please run '01-setup-jenkins.sh' first."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="c"&gt;# Source the file to load its variables into our script&lt;/span&gt;
&lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SCOPED_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JENKINS_KEYSTORE_PASSWORD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"⛔ ERROR: JENKINS_KEYSTORE_PASSWORD not found in 'jenkins.env'."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🔐 Keystore password loaded."&lt;/span&gt;

&lt;span class="c"&gt;# --- 4. Define Ports (from our 01-setup.sh) ---&lt;/span&gt;
&lt;span class="nv"&gt;JENKINS_HTTPS_PORT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"10400"&lt;/span&gt;
&lt;span class="nv"&gt;JENKINS_JNLP_PORT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"10401"&lt;/span&gt;

&lt;span class="c"&gt;# --- 5. Run the Controller Container ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Starting 'jenkins-controller' container ---"&lt;/span&gt;

docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"jenkins-controller"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--restart&lt;/span&gt; always &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network&lt;/span&gt; &lt;span class="s2"&gt;"cicd-net"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--hostname&lt;/span&gt; &lt;span class="s2"&gt;"jenkins.cicd.local"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--publish&lt;/span&gt; &lt;span class="s2"&gt;"127.0.0.1:&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;JENKINS_HTTPS_PORT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;JENKINS_HTTPS_PORT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--publish&lt;/span&gt; &lt;span class="s2"&gt;"127.0.0.1:&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;JENKINS_JNLP_PORT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;JENKINS_JNLP_PORT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--env-file&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SCOPED_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--env&lt;/span&gt; &lt;span class="s2"&gt;"CASC_JENKINS_CONFIG=/var/jenkins_home/casc_configs/"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; &lt;span class="s2"&gt;"jenkins-home:/var/jenkins_home"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JENKINS_CONFIG_DIR&lt;/span&gt;&lt;span class="s2"&gt;:/var/jenkins_home/casc_configs:ro"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; &lt;span class="s2"&gt;"/var/run/docker.sock:/var/run/docker.sock"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--env&lt;/span&gt; &lt;span class="nv"&gt;JENKINS_OPTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"--httpPort=-1 &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
--httpsPort=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;JENKINS_HTTPS_PORT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
--httpsKeyStore=/var/jenkins_home/casc_configs/ssl/jenkins.p12 &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
--httpsKeyStorePassword=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;JENKINS_KEYSTORE_PASSWORD&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
--webroot=/var/jenkins_home/war &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
--sessionTimeout=3600 &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
--sessionEviction=3600"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  jenkins-controller:latest

&lt;span class="c"&gt;# We no longer override the entrypoint. The image will&lt;/span&gt;
&lt;span class="c"&gt;# run its default 'jenkins.sh' command.&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✅ Jenkins Controller is starting."&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Monitor logs with: docker logs -f jenkins-controller"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Wait for the 'Jenkins is fully up and running' log message."&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   Then, access the UI at: https://jenkins.cicd.local:10400"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"   (Remember to add '127.0.0.1 jenkins.cicd.local' to your /etc/hosts file!)"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deconstructing the "Deploy" Script
&lt;/h3&gt;

&lt;p&gt;This &lt;code&gt;docker run&lt;/code&gt; command is the final assembly of our entire architecture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;--network "cicd-net"&lt;/code&gt; &amp;amp; &lt;code&gt;--hostname "jenkins.cicd.local"&lt;/code&gt;: Connects our "Foreman" to our "city" network and gives it the FQDN that our CA certificate and GitLab are expecting.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--publish "127.0.0.1:..."&lt;/code&gt;: Binds the UI (&lt;code&gt;10400&lt;/code&gt;) and JNLP (&lt;code&gt;10401&lt;/code&gt;) ports &lt;em&gt;only&lt;/em&gt; to the host's &lt;code&gt;localhost&lt;/code&gt; interface for security.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--env-file "$SCOPED_ENV_FILE"&lt;/code&gt;: Passes in our "scoped" &lt;code&gt;jenkins.env&lt;/code&gt; file, providing all the &lt;code&gt;${...}&lt;/code&gt; variables that our &lt;code&gt;jenkins.yaml&lt;/code&gt; needs for secrets.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--env "CASC_JENKINS_CONFIG=..."&lt;/code&gt;: This is the magic flag that activates the JCasC plugin and tells it &lt;em&gt;where&lt;/em&gt; to find our &lt;code&gt;jenkins.yaml&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--volume "jenkins-home:..."&lt;/code&gt;: Connects our Docker-managed volume (from Article 1) to store all of Jenkins's data (jobs, build history, etc.).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--volume "$JENKINS_CONFIG_DIR:..."&lt;/code&gt;: This is our JCasC mount. We mount our host's &lt;code&gt;config&lt;/code&gt; directory (which contains our &lt;code&gt;jenkins.yaml&lt;/code&gt; and the &lt;code&gt;ssl/jenkins.p12&lt;/code&gt; keystore) into the location specified by &lt;code&gt;CASC_JENKINS_CONFIG&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--volume "/var/run/docker.sock:..."&lt;/code&gt;: This provides the DooD capability, allowing the controller to spawn agents.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Missing Flag:&lt;/strong&gt; Notice there is no &lt;code&gt;--group-add&lt;/code&gt; flag. Because we "baked" the GID fix into our &lt;code&gt;Dockerfile.controller&lt;/code&gt;, this runtime flag is no longer necessary.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--env JENKINS_OPTS="..."&lt;/code&gt;: This is the final and most critical piece. We use this environment variable to pass start-up commands to the Jenkins Java process. We tell it:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;--httpPort=-1&lt;/code&gt;: &lt;strong&gt;Disable HTTP entirely.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--httpsPort=${JENKINS_HTTPS_PORT}&lt;/code&gt;: Enable HTTPS on our specified port.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--httpsKeyStore=...&lt;/code&gt;: Point to the &lt;code&gt;.p12&lt;/code&gt; keystore file we just mounted.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--httpsKeyStorePassword=...&lt;/code&gt;: Provide the password (which we sourced from &lt;code&gt;jenkins.env&lt;/code&gt;) to unlock the keystore.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;This command launches our controller, which will now boot up, read our &lt;code&gt;jenkins.yaml&lt;/code&gt;, configure itself, and be fully secured with our custom SSL certificate.&lt;/p&gt;

&lt;h1&gt;
  
  
  Chapter 7: Verification &amp;amp; First Login
&lt;/h1&gt;

&lt;p&gt;With our &lt;code&gt;03-deploy-controller.sh&lt;/code&gt; script running, the Jenkins controller will take a few minutes to boot, load all the plugins, and process our &lt;code&gt;jenkins.yaml&lt;/code&gt; file. You can monitor this process with &lt;code&gt;docker logs -f jenkins-controller&lt;/code&gt;. Once you see the "Jenkins is fully up and running" log message, our "Foreman" is ready for inspection.&lt;/p&gt;




&lt;h2&gt;
  
  
  7.1. Verification (UI): First Login &amp;amp; The JCasC Payoff
&lt;/h2&gt;

&lt;p&gt;First, we'll verify the User Interface. Open your browser and navigate to the controller's FQDN:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;https://jenkins.cicd.local:10400&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;(Remember, this only works because you've edited your &lt;code&gt;/etc/hosts&lt;/code&gt; file, as we did in the GitLab article).&lt;/p&gt;

&lt;p&gt;You should immediately see three signs of success:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;A Secure Lock Icon:&lt;/strong&gt; Your browser trusts the Jenkins UI. This proves our entire SSL chain is working: your host's OS trusts our Local CA (from Article 2), and the controller is correctly serving its &lt;code&gt;jenkins.p12&lt;/code&gt; keystore (from our &lt;code&gt;JENKINS_OPTS&lt;/code&gt; flag).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;No "Unlock Jenkins" Screen:&lt;/strong&gt; The JCasC plugin has worked. We are not asked to find a secret password in a log file.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;A Login Page:&lt;/strong&gt; We are taken directly to the login page.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, log in using the credentials we defined in our &lt;code&gt;jenkins.yaml&lt;/code&gt; file (via the &lt;code&gt;jenkins.env&lt;/code&gt;):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Username:&lt;/strong&gt; &lt;code&gt;admin&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Password:&lt;/strong&gt; (The &lt;code&gt;JENKINS_ADMIN_PASSWORD&lt;/code&gt; you set)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once logged in, let's confirm JCasC did its job. Navigate to &lt;strong&gt;Manage Jenkins&lt;/strong&gt; in the left sidebar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Check Credentials:&lt;/strong&gt; Go to &lt;strong&gt;Credentials&lt;/strong&gt;. You should see our two JCasC-defined credentials: &lt;strong&gt;&lt;code&gt;gitlab-api-token&lt;/code&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;code&gt;gitlab-checkout-credentials&lt;/code&gt;&lt;/strong&gt;. This proves the &lt;code&gt;credentials:&lt;/code&gt; block worked.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check Cloud Config:&lt;/strong&gt; Go to &lt;strong&gt;Clouds&lt;/strong&gt;. You will see our &lt;strong&gt;&lt;code&gt;docker-local&lt;/code&gt;&lt;/strong&gt; cloud. Click &lt;strong&gt;Configure&lt;/strong&gt;. You'll see it's correctly set up to use our &lt;code&gt;general-purpose-agent&lt;/code&gt; and &lt;code&gt;cicd-net&lt;/code&gt;. This proves the &lt;code&gt;clouds:&lt;/code&gt; block worked.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  7.2. Verification (API): The &lt;code&gt;403 Forbidden&lt;/code&gt; Debugging Journey
&lt;/h2&gt;

&lt;p&gt;The UI is working, but the most critical verification is to test the API. We will use our &lt;code&gt;04-verify-jenkins.py&lt;/code&gt; script to do this.&lt;/p&gt;

&lt;p&gt;This verification was one of our most complex debugging challenges. Our first attempts to connect—even with a valid user and password—failed with an &lt;code&gt;HTTP Error 403: Forbidden&lt;/code&gt;. This wasn't an &lt;em&gt;authentication&lt;/em&gt; failure (the server knew who we were); it was an &lt;em&gt;authorization&lt;/em&gt; failure (it was blocking our request).&lt;/p&gt;

&lt;p&gt;Our investigation revealed two key facts about the Jenkins API:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;API Tokens are Required:&lt;/strong&gt; Jenkins blocks password-based authentication for most of its API, especially high-security endpoints like &lt;code&gt;/scriptText&lt;/code&gt;. The correct way to authenticate is with an API Token.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;urllib&lt;/code&gt; Auth is Complex:&lt;/strong&gt; Our attempts to use &lt;code&gt;urllib&lt;/code&gt;'s "smart" handlers (like &lt;code&gt;HTTPBasicAuthHandler&lt;/code&gt; and &lt;code&gt;HTTPCookieProcessor&lt;/code&gt;) created a state-management conflict. The handlers would send a session cookie &lt;em&gt;and&lt;/em&gt; a token, or a token &lt;em&gt;and&lt;/em&gt; a crumb, confusing the server and resulting in a 403.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The solution was to stop being "smart" and to be explicit. We must &lt;strong&gt;manually build the &lt;code&gt;Authorization: Basic&lt;/code&gt; header&lt;/strong&gt; ourselves and send &lt;em&gt;only&lt;/em&gt; that. This is the most direct and reliable way to authenticate.&lt;/p&gt;

&lt;h3&gt;
  
  
  Action 1: Manually Generate an API Token
&lt;/h3&gt;

&lt;p&gt;First, we must generate the API token for our &lt;code&gt;admin&lt;/code&gt; user. JCasC cannot do this for us.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; In the Jenkins UI, click your &lt;code&gt;admin&lt;/code&gt; username (top right).&lt;/li&gt;
&lt;li&gt; In the left-hand sidebar, click &lt;strong&gt;Security&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; Find the &lt;strong&gt;"API Token"&lt;/strong&gt; section.&lt;/li&gt;
&lt;li&gt; Click &lt;strong&gt;"Add new Token"&lt;/strong&gt;, give it a name (like &lt;code&gt;admin-api-token&lt;/code&gt;), and click &lt;strong&gt;"Generate"&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Copy the generated token immediately.&lt;/strong&gt; You will not see it again.&lt;/li&gt;
&lt;li&gt; Open your &lt;code&gt;jenkins.env&lt;/code&gt; file (in your &lt;code&gt;0008_...&lt;/code&gt; article directory) and add this token as &lt;code&gt;JENKINS_API_TOKEN&lt;/code&gt;. (Our &lt;code&gt;01-setup-jenkins.sh&lt;/code&gt; already added &lt;code&gt;GITLAB_CHECKOUT_TOKEN&lt;/code&gt;, but we must add this one manually).&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Action 2: Run the Verification Script
&lt;/h3&gt;

&lt;p&gt;The following script, &lt;code&gt;04-verify-jenkins.py&lt;/code&gt;, is our final, working solution. It reads the &lt;code&gt;JENKINS_API_TOKEN&lt;/code&gt; from our &lt;code&gt;jenkins.env&lt;/code&gt; file, manually base64-encodes it into a Basic Auth header, and sends it with a simple Groovy script to test our access.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env python3
&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ssl&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;urllib.request&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;urllib.parse&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;

&lt;span class="c1"&gt;# --- Configuration ---
&lt;/span&gt;&lt;span class="n"&gt;ENV_FILE_PATH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;jenkins.env&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;JENKINS_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://jenkins.cicd.local:10400&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;JENKINS_USER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;admin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# --- 1. Standard Library .env parser ---
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;load_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env_path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Reads a .env file and loads its variables into os.environ.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Loading environment from: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;env_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;env_path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;⛔ ERROR: Environment file not found at &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;env_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Please run &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;01-setup-jenkins.sh&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; first.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;

    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'"&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;

&lt;span class="c1"&gt;# --- 2. Main Verification ---
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;verify_jenkins_api&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;api_token&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Connects to Jenkins using a manually-crafted Basic Auth
    header (token) and attempts an authenticated API call.
    This method does not use or need a CSRF crumb.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Creating default SSL context...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# This proves our host's CA trust (from Article 2) is working
&lt;/span&gt;    &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_default_context&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Attempting authenticated API call (Groovy script)...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;script_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;base_url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/scriptText&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="c1"&gt;# This simple script just tests the connection.
&lt;/span&gt;    &lt;span class="n"&gt;groovy_script&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;return jenkins.model.Jenkins.get().getSystemMessage()&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;urlencode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;script&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;groovy_script&lt;/span&gt;&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# --- Manually build the Authorization Header ---
&lt;/span&gt;    &lt;span class="c1"&gt;# This was the solution to our 403 Forbidden errors.
&lt;/span&gt;    &lt;span class="n"&gt;auth_string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;api_token&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;auth_bytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;auth_string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;auth_base64&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auth_bytes&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ascii&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Basic &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;auth_base64&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Content-Type&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;application/x-www-form-urlencoded&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;# We do NOT send a Jenkins-Crumb header.
&lt;/span&gt;
    &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;script_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# We use the default urlopen, passing our context.
&lt;/span&gt;        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;✅✅✅ Jenkins Verification SUCCESS! ✅✅✅&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authenticated API call returned: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;⛔ ERROR: API call failed. Status: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   Response: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;URLError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;⛔ ERROR: Connection failed. Did you add &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;127.0.0.1 jenkins.cicd.local&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; to /etc/hosts?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   Details: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;hasattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;read&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  Response: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;⛔ ERROR: API call failed.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   Details: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;hasattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;read&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  Response: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# --- 6. Main execution ---
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;load_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ENV_FILE_PATH&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;JENKINS_TOKEN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;JENKINS_API_TOKEN&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;JENKINS_TOKEN&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;⛔ ERROR: JENKINS_API_TOKEN not found in &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;jenkins.env&lt;/span&gt;&lt;span class="sh"&gt;'"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Please generate one in the UI and add it to the file.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;verify_jenkins_api&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JENKINS_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;JENKINS_USER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;JENKINS_TOKEN&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Payoff
&lt;/h3&gt;

&lt;p&gt;When you run this script (&lt;code&gt;python3 04-verify-jenkins.py&lt;/code&gt;), you will see the successful output. This proves our API is accessible, our token is valid, and our JCasC-defined &lt;code&gt;admin&lt;/code&gt; user has the correct &lt;code&gt;Overall/Administer&lt;/code&gt; permissions to access the script console. Our "Foreman" is fully operational and ready to work.&lt;/p&gt;

&lt;h1&gt;
  
  
  Chapter 8: Practical Application - The "Hero Project" Pipeline (V1)
&lt;/h1&gt;

&lt;p&gt;Our "Factory Foreman" (Jenkins) is fully operational. We've verified its UI is secure, its API is accessible, and its "hiring department" (the Docker Cloud) is correctly configured. The infrastructure is complete.&lt;/p&gt;

&lt;p&gt;Now it's time to put it to work.&lt;/p&gt;

&lt;p&gt;The goal of this chapter is to connect our "Factory" to our "Central Library" (GitLab) and run our first &lt;em&gt;real&lt;/em&gt; build. We will connect Jenkins to our &lt;code&gt;0004_std_lib_http_client&lt;/code&gt; "hero project" and use its "polyglot" build agent to compile, test, and run our C++, Rust, and Python code.&lt;/p&gt;




&lt;h2&gt;
  
  
  8.1. Action 1: The &lt;code&gt;Jenkinsfile&lt;/code&gt; (V1)
&lt;/h2&gt;

&lt;p&gt;Our first step is to create the "assembly line" instructions. As we discussed in Chapter 2, these instructions live &lt;em&gt;with the code&lt;/em&gt;. We will create a new file named &lt;code&gt;Jenkinsfile&lt;/code&gt; (with no extension) in the root of our &lt;code&gt;0004_std_lib_http_client&lt;/code&gt; repository.&lt;/p&gt;

&lt;p&gt;This file is a "declarative pipeline" script. It tells the Jenkins agent &lt;em&gt;what&lt;/em&gt; to do and in &lt;em&gt;what order&lt;/em&gt;. Here is the complete V1 &lt;code&gt;Jenkinsfile&lt;/code&gt; for our project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Jenkinsfile&lt;/span&gt;

&lt;span class="n"&gt;pipeline&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// 1. Define our "Worker"&lt;/span&gt;
    &lt;span class="c1"&gt;// This tells Jenkins to spin up our custom-built agent&lt;/span&gt;
    &lt;span class="c1"&gt;// which already has all system dependencies (cmake, rust, python).&lt;/span&gt;
    &lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="s1"&gt;'general-purpose-agent'&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;stages&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// 2. Setup &amp;amp; Build Stage&lt;/span&gt;
        &lt;span class="c1"&gt;// This runs the project's own setup.sh.&lt;/span&gt;
        &lt;span class="c1"&gt;// It will create the Python venv, install pip requirements,&lt;/span&gt;
        &lt;span class="c1"&gt;// and compile both the Debug and Release builds.&lt;/span&gt;
        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Setup &amp;amp; Build'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'--- Running project setup.sh ---'&lt;/span&gt;
                &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'chmod +x ./setup.sh'&lt;/span&gt;
                &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'./setup.sh'&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// 3. Test &amp;amp; Coverage Stage&lt;/span&gt;
        &lt;span class="c1"&gt;// This runs the project's coverage script, which&lt;/span&gt;
        &lt;span class="c1"&gt;// depends on the 'build_debug' created in the prior stage.&lt;/span&gt;
        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Test &amp;amp; Coverage'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'--- Running CTest, Cargo-Cov, and Pytest ---'&lt;/span&gt;
                &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'chmod +x ./run-coverage.sh'&lt;/span&gt;
                &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'./run-coverage.sh'&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deconstructing the &lt;code&gt;Jenkinsfile&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This simple file is incredibly powerful because it leans on the work we've already done.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;agent { label 'general-purpose-agent' }&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
This is the "hiring" instruction. It tells the Jenkins controller to request a new "worker" from the cloud named &lt;code&gt;docker-local&lt;/code&gt; that has the label &lt;code&gt;general-purpose-agent&lt;/code&gt;. This label matches &lt;em&gt;exactly&lt;/em&gt; what we defined in our &lt;code&gt;jenkins.yaml&lt;/code&gt; file, which in turn points to our &lt;code&gt;general-purpose-agent:latest&lt;/code&gt; Docker image.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;stage('Setup &amp;amp; Build')&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
This is our first assembly line station. Instead of cluttering our pipeline with &lt;code&gt;cmake&lt;/code&gt; and &lt;code&gt;python&lt;/code&gt; commands, we simply make our scripts executable (&lt;code&gt;chmod +x&lt;/code&gt;) and run the project's own &lt;code&gt;setup.sh&lt;/code&gt;. This is a clean separation of concerns: the &lt;code&gt;Jenkinsfile&lt;/code&gt; orchestrates &lt;em&gt;what&lt;/em&gt; to do, and the &lt;code&gt;setup.sh&lt;/code&gt; script knows &lt;em&gt;how&lt;/em&gt; to do it. This stage will create the Python venv and build both the Debug and Release versions of our libraries.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;stage('Test &amp;amp; Coverage')&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
This is our "quality assurance" station. Once the build is complete, it runs the &lt;code&gt;run-coverage.sh&lt;/code&gt; script. This script executes all three test suites (C/C++ &lt;code&gt;ctest&lt;/code&gt;, Rust &lt;code&gt;cargo llvm-cov&lt;/code&gt;, and Python &lt;code&gt;pytest&lt;/code&gt;) against the &lt;code&gt;build_debug&lt;/code&gt; directory created in the previous stage.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, &lt;strong&gt;add this &lt;code&gt;Jenkinsfile&lt;/code&gt; to the root of your &lt;code&gt;0004_std_lib_http_client&lt;/code&gt; project, commit it, and push it to your GitLab server.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Our "blueprint" is now in the "Library," waiting for the "Foreman" to find it.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Note on Credentials: Creating Your Project Access Token
&lt;/h3&gt;

&lt;p&gt;Before our &lt;code&gt;Jenkinsfile&lt;/code&gt; can work, it needs permission to clone our private &lt;code&gt;0004_std_lib_http_client&lt;/code&gt; repository. We will give it this permission by creating a new, limited-scope &lt;strong&gt;Project Access Token&lt;/strong&gt; in GitLab.&lt;/p&gt;

&lt;p&gt;This token is the value we will store as &lt;code&gt;GITLAB_CHECKOUT_TOKEN&lt;/code&gt; in our &lt;code&gt;cicd.env&lt;/code&gt; file. It's crucial to do this &lt;em&gt;before&lt;/em&gt; you deploy Jenkins, so that when the controller starts, our JCasC file can read this token and create the permanent &lt;code&gt;gitlab-checkout-credentials&lt;/code&gt; credential.&lt;/p&gt;

&lt;p&gt;If you haven't created this token yet, follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Navigate to Your Project:&lt;/strong&gt; Open your GitLab instance and go to the &lt;code&gt;0004_std_lib_http_client&lt;/code&gt; project.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Go to Access Tokens:&lt;/strong&gt; In the project's left-hand sidebar, navigate to &lt;strong&gt;Settings &amp;gt; Access Tokens&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Add New Token:&lt;/strong&gt; Click the &lt;strong&gt;"Add new token"&lt;/strong&gt; button.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Fill out the form:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;jenkins-checkout-token&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Role:&lt;/strong&gt; Select &lt;code&gt;Developer&lt;/code&gt;. This provides just enough permission to clone the repository.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scopes:&lt;/strong&gt; Check the box for &lt;strong&gt;&lt;code&gt;read_repository&lt;/code&gt;&lt;/strong&gt;. This is the &lt;em&gt;only&lt;/em&gt; scope this token needs, following the Principle of Least Privilege.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Create and Copy:&lt;/strong&gt; Click the &lt;strong&gt;"Create project access token"&lt;/strong&gt; button. GitLab will display the token one time. Copy this token.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Update Your &lt;code&gt;cicd.env&lt;/code&gt;:&lt;/strong&gt; Open your master &lt;code&gt;cicd.env&lt;/code&gt; file (in &lt;code&gt;~/cicd_stack/&lt;/code&gt;) and add this token:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GITLAB_CHECKOUT_TOKEN="glpat-..."
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Update &lt;code&gt;jenkins.env&lt;/code&gt;:&lt;/strong&gt; Open your &lt;code&gt;jenkins.env&lt;/code&gt; file (in the Jenkins article directory) and add the same line:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GITLAB_CHECKOUT_TOKEN="glpat-..."
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, when you run your &lt;code&gt;01-setup-jenkins.sh&lt;/code&gt; and &lt;code&gt;03-deploy-controller.sh&lt;/code&gt; scripts, our JCasC file will automatically read this token and create the permanent &lt;code&gt;gitlab-checkout-credentials&lt;/code&gt; in Jenkins. If you add this credential manually in the UI, &lt;strong&gt;it will be deleted the next time your controller restarts.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UPDATE 2025-12-09: Creating the Group Access Token&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To satisfy the &lt;code&gt;articles-gat&lt;/code&gt; requirement for newer plugins, you must also create a &lt;strong&gt;Group Access Token&lt;/strong&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Navigate to the Group:&lt;/strong&gt; Go to your top-level &lt;strong&gt;"Articles"&lt;/strong&gt; group in GitLab (not the specific project).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Access Tokens:&lt;/strong&gt; Go to &lt;strong&gt;Settings &amp;gt; Access Tokens&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create Token:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;jenkins-group-token&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Role:&lt;/strong&gt; &lt;code&gt;Maintainer&lt;/code&gt; (recommended for full webhook management) or &lt;code&gt;Developer&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scopes:&lt;/strong&gt; Select &lt;code&gt;api&lt;/code&gt; and &lt;code&gt;read_repository&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Save:&lt;/strong&gt; Copy the token (&lt;code&gt;glpat-...&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Update &lt;code&gt;cicd.env&lt;/code&gt;:&lt;/strong&gt; Add this token to your &lt;code&gt;cicd.env&lt;/code&gt; file as &lt;code&gt;ARTICLES_GAT&lt;/code&gt;.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;ARTICLES_GAT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"glpat-..."&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  8.2. Action 2: Jenkins UI (Create the Job)
&lt;/h2&gt;

&lt;p&gt;With our &lt;code&gt;Jenkinsfile&lt;/code&gt; pushed to the repository, our "Foreman" (Jenkins) is ready to be given its assignment. We need to create a job that tells Jenkins to "watch" this specific GitLab project.&lt;/p&gt;

&lt;p&gt;We will use the &lt;strong&gt;"Multibranch Pipeline"&lt;/strong&gt; job type. This is a powerful feature provided by our &lt;code&gt;gitlab-branch-source&lt;/code&gt; plugin. Instead of creating a separate, static job for our &lt;code&gt;main&lt;/code&gt; branch, we will create one "project" job that &lt;em&gt;automatically&lt;/em&gt; discovers all branches, merge requests, and tags that contain a &lt;code&gt;Jenkinsfile&lt;/code&gt; and creates jobs for them.&lt;/p&gt;

&lt;p&gt;Here are the steps to set this up:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Log in to Jenkins&lt;/strong&gt; at &lt;code&gt;https://jenkins.cicd.local:10400&lt;/code&gt; as your &lt;code&gt;admin&lt;/code&gt; user.&lt;/li&gt;
&lt;li&gt; From the dashboard, you can create a folder for organization (e.g., "Articles") or create the job directly. Click &lt;strong&gt;"New Item"&lt;/strong&gt; in the left sidebar.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Enter an item name:&lt;/strong&gt; &lt;code&gt;0004_std_lib_http_client&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt; Select &lt;strong&gt;"Multibranch Pipeline"&lt;/strong&gt; from the list of job types and click &lt;strong&gt;"OK"&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; You'll be taken to the configuration page. Scroll down to the &lt;strong&gt;"Branch Sources"&lt;/strong&gt; section.&lt;/li&gt;
&lt;li&gt; Click &lt;strong&gt;"Add source"&lt;/strong&gt; and select &lt;strong&gt;"GitLab project"&lt;/strong&gt;. This option is only here because we installed the &lt;code&gt;gitlab-branch-source&lt;/code&gt; plugin.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Fill out the source configuration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Server:&lt;/strong&gt; Select &lt;strong&gt;"Local GitLab"&lt;/strong&gt;. This is the server connection we defined in our &lt;code&gt;jenkins.yaml&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Owner:&lt;/strong&gt; Select your GitLab group, &lt;strong&gt;"Articles"&lt;/strong&gt;. Jenkins will use the &lt;code&gt;gitlab-api-token&lt;/code&gt; (defined in JCasC) to scan this group for projects.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Project:&lt;/strong&gt; A dropdown will appear. Select &lt;strong&gt;&lt;code&gt;0004_std_lib_http_client&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Checkout Credentials:&lt;/strong&gt; This is the solution to our "authentication failed" problem. In the dropdown, select our JCasC-defined credential: &lt;strong&gt;`gitlab-checkout-bot / **&lt;/strong&gt;&lt;strong&gt;`&lt;/strong&gt;. This tells Jenkins to use our limited-scope Project Access Token for all &lt;code&gt;git clone&lt;/code&gt; operations.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click &lt;strong&gt;"Save"&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As soon as you save, Jenkins will automatically perform an initial "Branch Indexing" scan. You can watch the log for this scan in the "Scan Multibranch Pipeline Log" in the sidebar. It will connect to GitLab (proving our controller's JVM trust), find your &lt;code&gt;main&lt;/code&gt; branch, see the &lt;code&gt;Jenkinsfile&lt;/code&gt;, and create a new job for it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UPDATE 2025-12-09&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 7 (Checkout Credentials):&lt;/strong&gt;&lt;br&gt;
When selecting credentials, the dropdown behavior depends on your plugin version:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Legacy Plugin:&lt;/strong&gt; Select &lt;code&gt;gitlab-checkout-bot&lt;/code&gt; (Username/Password).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Modern Plugin:&lt;/strong&gt; Select &lt;code&gt;articles-gat&lt;/code&gt; (Group Access Token). If you see "403" errors or if the legacy credential is missing from the list, choose this option.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  8.3. Action 3: GitLab UI (Set the Webhook)
&lt;/h2&gt;

&lt;p&gt;At this point, our job exists, but it doesn't know &lt;em&gt;when&lt;/em&gt; we push new code. We could tell Jenkins to "Periodically scan," but this is inefficient. We want an instant, event-driven trigger.&lt;/p&gt;

&lt;p&gt;The solution is a &lt;strong&gt;webhook&lt;/strong&gt;. We need to tell GitLab to send a "ping" to Jenkins every time we create a Merge Request.&lt;/p&gt;

&lt;p&gt;Normally, this is a manual step, but our plugin stack has a final surprise for us:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; In your browser, go to your GitLab project: &lt;code&gt;https://gitlab.cicd.local:10300/Articles/0004_std_lib_http_client&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; In the left sidebar, go to &lt;strong&gt;Settings &amp;gt; Webhooks&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You will see that a webhook pointing to our Jenkins instance &lt;strong&gt;already exists&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This is the final payoff of our JCasC setup. When we configured the "Local GitLab" server in our &lt;code&gt;jenkins.yaml&lt;/code&gt; and set &lt;code&gt;manageWebHooks: true&lt;/code&gt;, we gave Jenkins (using its &lt;code&gt;gitlab-api-token&lt;/code&gt;) permission to &lt;em&gt;automatically&lt;/em&gt; create this webhook for us when we created the Multibranch job.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;All we have to do is test it and tweak it:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Click the &lt;strong&gt;"Edit"&lt;/strong&gt; button for the auto-generated webhook.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Tweak Triggers:&lt;/strong&gt; By default, it's likely set for "Push events." We want to follow our professional workflow. &lt;strong&gt;Uncheck "Push events"&lt;/strong&gt; and ensure &lt;strong&gt;"Merge request events"&lt;/strong&gt; is &lt;strong&gt;checked&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Test the Connection:&lt;/strong&gt; Scroll down and click the &lt;strong&gt;"Test"&lt;/strong&gt; button. Select &lt;strong&gt;"Merge request events"&lt;/strong&gt; from the dropdown and click &lt;strong&gt;"Test webhook"&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;At the top of the page, you will see a green &lt;strong&gt;&lt;code&gt;Hook executed successfully: HTTP 200&lt;/code&gt;&lt;/strong&gt; message.&lt;/p&gt;

&lt;p&gt;This single message proves our entire security architecture is working end-to-end. It confirms that GitLab (whose JVM trusts our CA) successfully sent a secure HTTPS request to our Jenkins controller (which is serving traffic using its &lt;code&gt;.p12&lt;/code&gt; keystore). The "Factory" and the "Library" are now fully and securely connected.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Note on Advanced Triggers: The V2 Pipeline
&lt;/h3&gt;

&lt;p&gt;For our V1 pipeline, we've configured the webhook to trigger &lt;em&gt;only&lt;/em&gt; on "Merge request events." This is a robust and common workflow that ensures we only run our full, expensive build and test suite when we're preparing to merge code.&lt;/p&gt;

&lt;p&gt;However, this is just the beginning. A more sophisticated, real-world pipeline would use &lt;strong&gt;conditional logic&lt;/strong&gt; to run &lt;em&gt;different&lt;/em&gt; sets of tasks based on the &lt;em&gt;type&lt;/em&gt; of trigger.&lt;/p&gt;

&lt;p&gt;It is entirely possible (and standard practice) to enable both &lt;strong&gt;"Push events"&lt;/strong&gt; and &lt;strong&gt;"Merge request events."&lt;/strong&gt; We would then make our &lt;code&gt;Jenkinsfile&lt;/code&gt; "smarter" by using its &lt;code&gt;when&lt;/code&gt; directive to inspect the build's context.&lt;/p&gt;

&lt;p&gt;For example, in a V2 pipeline, we could configure:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;On a simple &lt;code&gt;git push&lt;/code&gt; to a feature branch:&lt;/strong&gt; The &lt;code&gt;Jenkinsfile&lt;/code&gt; would detect &lt;code&gt;env.BRANCH_NAME != 'main'&lt;/code&gt; and &lt;code&gt;when { NOT { changeRequest() } }&lt;/code&gt;. It would then &lt;em&gt;only&lt;/em&gt; run fast jobs like linting and unit tests, giving the developer feedback in seconds.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;On a &lt;code&gt;push&lt;/code&gt; to a Merge Request:&lt;/strong&gt; The &lt;code&gt;Jenkinsfile&lt;/code&gt; would detect &lt;code&gt;when { changeRequest() }&lt;/code&gt;. This would trigger our &lt;em&gt;full&lt;/em&gt; validation pipeline: build, all tests (C++, Rust, Python), and coverage. This is our "quality gate."&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;On a merge to the &lt;code&gt;main&lt;/code&gt; branch (or a Git tag):&lt;/strong&gt; The &lt;code&gt;Jenkinsfile&lt;/code&gt; would detect &lt;code&gt;env.BRANCH_NAME == 'main'&lt;/code&gt;. This would run the full pipeline &lt;em&gt;plus&lt;/em&gt; our new "Publish" stage (which we'll build in the next article) to save the compiled artifacts to Artifactory.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We have started with a simple, effective "build everything on MR" strategy. We will build on this foundation later to create these more complex, conditional workflows.&lt;/p&gt;

&lt;h1&gt;
  
  
  Chapter 9: The Payoff - The First Automated Build
&lt;/h1&gt;

&lt;p&gt;Our infrastructure is 100% complete. Our &lt;code&gt;Jenkinsfile&lt;/code&gt; is in the repository, our Jenkins job is configured to use our JCasC-defined credentials, and the GitLab webhook is automatically set up and tested.&lt;/p&gt;

&lt;p&gt;All that's left is to see it in action.&lt;/p&gt;




&lt;h2&gt;
  
  
  9.1. The "Scan"
&lt;/h2&gt;

&lt;p&gt;When you first created the "Multibranch Pipeline" job in the last chapter, Jenkins automatically performed an initial "Branch Indexing" scan. In the "Scan Multibranch Pipeline Log" (or by clicking "Scan Repository Now"), you would have seen Jenkins:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Connect to &lt;code&gt;https://gitlab.cicd.local&lt;/code&gt; (proving the controller's JVM trust).&lt;/li&gt;
&lt;li&gt; Use the &lt;code&gt;gitlab-api-token&lt;/code&gt; to scan the &lt;code&gt;Articles/0004_std_lib_http_client&lt;/code&gt; project.&lt;/li&gt;
&lt;li&gt; Discover the &lt;code&gt;main&lt;/code&gt; branch.&lt;/li&gt;
&lt;li&gt; Find the &lt;code&gt;Jenkinsfile&lt;/code&gt; in that branch.&lt;/li&gt;
&lt;li&gt; Automatically create a new pipeline job named "main" and queue it for a build.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This first build is the complete validation of our setup.&lt;/p&gt;




&lt;h2&gt;
  
  
  9.2. The "Build": Deconstructing the Success Log
&lt;/h2&gt;

&lt;p&gt;When you open the log for that first successful build, you are seeing the payoff for every single piece of our architecture. Let's narrate the log:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;Provisioning 'general-purpose-agent...'&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
This first line is a triumph. It proves our "Foreman" (Controller) has successfully used the &lt;strong&gt;Docker Plugin&lt;/strong&gt; (configured by JCasC) to begin "hiring" our worker.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No "Pulling image..." Step&lt;/strong&gt;&lt;br&gt;
You'll notice the log does &lt;em&gt;not&lt;/em&gt; show a "Pulling image..." step. It immediately moves to creating the container. This proves our &lt;strong&gt;&lt;code&gt;pullStrategy: 'PULL_NEVER'&lt;/code&gt;&lt;/strong&gt; setting in &lt;code&gt;jenkins.yaml&lt;/code&gt; is working, forcing the controller to use our local image.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;Started container ID ...&lt;/code&gt; &amp;amp; &lt;code&gt;Accepted JNLP4-connect connection...&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
This is the proof that our agent is working. The container started &lt;em&gt;without&lt;/em&gt; the &lt;code&gt;exec: "-url": executable file not found&lt;/code&gt; error. This confirms our &lt;strong&gt;&lt;code&gt;ENTRYPOINT ["java", "-jar", ...]&lt;/code&gt;&lt;/strong&gt; instruction in &lt;code&gt;Dockerfile.agent&lt;/code&gt; is correct. The agent is up and connected.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;using credential std-lib-http-client&lt;/code&gt; &amp;amp; &lt;code&gt;git clone ...&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
The &lt;code&gt;git clone&lt;/code&gt; succeeds. This proves two things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Our JCasC-defined &lt;strong&gt;&lt;code&gt;gitlab-checkout-credentials&lt;/code&gt;&lt;/strong&gt; are working.&lt;/li&gt;
&lt;li&gt;Our agent's &lt;strong&gt;OS-level trust&lt;/strong&gt; (&lt;code&gt;update-ca-certificates&lt;/code&gt;) is working, allowing &lt;code&gt;git&lt;/code&gt; to trust &lt;code&gt;https://gitlab.cicd.local&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;Found Rust: /home/jenkins/.rustup/toolchains...&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
This confirms our &lt;code&gt;Dockerfile.agent&lt;/code&gt; build process was correct. &lt;code&gt;cmake&lt;/code&gt; is running as the &lt;code&gt;jenkins&lt;/code&gt; user and is finding the Rust toolchain in &lt;code&gt;/home/jenkins/.cargo/bin&lt;/code&gt; because we installed it under the correct user and added it to the &lt;code&gt;PATH&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;[100%] Built target...&lt;/code&gt; &amp;amp; &lt;code&gt;100% tests passed...&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
The &lt;code&gt;setup.sh&lt;/code&gt; and &lt;code&gt;run-coverage.sh&lt;/code&gt; scripts complete. Our polyglot build—C++, Rust, and Python—has been successfully built and tested by our custom, ephemeral agent.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;Finished: SUCCESS&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
The pipeline is complete. The agent container is automatically destroyed, along with all its build files.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  9.3. The "Trigger": The Final Verification
&lt;/h2&gt;

&lt;p&gt;The manual scan proves the build works. This final test proves the &lt;em&gt;automation&lt;/em&gt; works. We will now simulate a developer's workflow and watch the entire loop trigger automatically.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;First, we'll go to the GitLab Webhooks page and show that the &lt;code&gt;gitlab-branch-source&lt;/code&gt; plugin &lt;strong&gt;automatically created the webhook for us&lt;/strong&gt;. We'll use the "Test" button to prove the &lt;code&gt;HTTP 200&lt;/code&gt; connection.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Then, we'll guide the user to make a &lt;em&gt;real&lt;/em&gt; change: &lt;code&gt;git checkout -b feature/test-trigger&lt;/code&gt;, add a comment, &lt;code&gt;git push&lt;/code&gt;, and &lt;strong&gt;open a new Merge Request in GitLab&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We'll watch the Jenkins UI as the &lt;code&gt;MR-1&lt;/code&gt; build appears &lt;em&gt;instantly&lt;/em&gt;, proving the webhook is functioning perfectly.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This confirms our "Factory" and "Library" are perfectly connected.&lt;/p&gt;

&lt;h1&gt;
  
  
  Chapter 10: Conclusion
&lt;/h1&gt;

&lt;h2&gt;
  
  
  10.1. What We've Built
&lt;/h2&gt;

&lt;p&gt;We have successfully built a complete, automated, and scalable CI (Continuous Integration) system. Our "Factory Foreman" (Jenkins) is fully operational and perfectly integrated with our "Central Library" (GitLab).&lt;/p&gt;

&lt;p&gt;Let's summarize what our new system accomplishes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fully Automated Builds:&lt;/strong&gt; When a developer opens a Merge Request, a build is triggered &lt;em&gt;instantly&lt;/em&gt; and automatically, with no human intervention.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secure &amp;amp; Trusted:&lt;/strong&gt; The entire connection is secured with our internal CA. GitLab sends a secure webhook to Jenkins, and Jenkins clones from GitLab over a trusted HTTPS connection.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalable &amp;amp; Isolated Compute:&lt;/strong&gt; We are using a modern "Controller/Agent" architecture. The controller only orchestrates, and the &lt;em&gt;real&lt;/em&gt; work is done by our &lt;code&gt;general-purpose-agent&lt;/code&gt; containers. This means we can run multiple builds in parallel, each in its own clean, isolated environment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistent, Polyglot Environment:&lt;/strong&gt; Our custom agent image, built with our "dual-trust" fix and the correct user-context, is proven to work. It can build and test our complex C++, Rust, and Python project in a single, unified pipeline.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  10.2. The New Problem: The "Ephemeral Artifact"
&lt;/h2&gt;

&lt;p&gt;Our pipeline log &lt;code&gt;Finished: SUCCESS&lt;/code&gt; is a huge victory, but it also highlights our next major challenge. Our &lt;code&gt;setup.sh&lt;/code&gt; script successfully compiled our C++ library (&lt;code&gt;libhttpc.so&lt;/code&gt;), our Rust binaries, and our Python wheel (&lt;code&gt;.whl&lt;/code&gt;), and &lt;code&gt;run-coverage.sh&lt;/code&gt; proved they work.&lt;/p&gt;

&lt;p&gt;But what happened to them?&lt;/p&gt;

&lt;p&gt;They were created inside the &lt;code&gt;general-purpose-agent&lt;/code&gt; container. The moment the build finished, that container was &lt;strong&gt;immediately and permanently destroyed&lt;/strong&gt;, taking all those valuable, compiled "finished goods" with it.&lt;/p&gt;

&lt;p&gt;Our factory is running at 100% capacity, but we're just throwing every finished product into the incinerator. We have a "build," but we have no &lt;em&gt;artifacts&lt;/em&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  10.3. Next Steps: Building the "Secure Warehouse"
&lt;/h2&gt;

&lt;p&gt;Our "factory" is missing its "loading dock" and "warehouse."&lt;/p&gt;

&lt;p&gt;We need a central, persistent, and secure location to &lt;em&gt;store&lt;/em&gt; our finished products. We need a system that can:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Receive the compiled &lt;code&gt;libhttpc.so&lt;/code&gt;, the &lt;code&gt;httprust_client&lt;/code&gt; binary, and the &lt;code&gt;httppy-0.1.0-py3-none-any.whl&lt;/code&gt; file from our build agent.&lt;/li&gt;
&lt;li&gt; Store them permanently and give them a version number.&lt;/li&gt;
&lt;li&gt; Allow other developers or servers to download and use these "blessed" artifacts without having to rebuild the entire project themselves.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is the exact role of an &lt;strong&gt;Artifact Repository Manager&lt;/strong&gt;. In the next article, we will build our "Secure Warehouse": &lt;strong&gt;JFrog Artifactory&lt;/strong&gt;. We will then add a final "Publish" stage to our &lt;code&gt;Jenkinsfile&lt;/code&gt; to solve this "ephemeral artifact" challenge once and for all.&lt;/p&gt;

</description>
      <category>jenkins</category>
      <category>cicd</category>
      <category>devops</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Part 03: Building a Sovereign Software Factory: Self-Hosted GitLab &amp; Secrets Management</title>
      <dc:creator>Warren Jitsing</dc:creator>
      <pubDate>Wed, 17 Dec 2025 05:45:49 +0000</pubDate>
      <link>https://dev.to/warren_jitsing_dd1c1d6fc6/part-03-building-a-sovereign-software-factory-self-hosted-gitlab-secrets-management-209e</link>
      <guid>https://dev.to/warren_jitsing_dd1c1d6fc6/part-03-building-a-sovereign-software-factory-self-hosted-gitlab-secrets-management-209e</guid>
      <description>&lt;p&gt;GitHub: &lt;a href="https://github.com/InfiniteConsult/0007_cicd_part03_gitlab" rel="noopener noreferrer"&gt;https://github.com/InfiniteConsult/0007_cicd_part03_gitlab&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; In this installment, we construct the &lt;strong&gt;"Central Library"&lt;/strong&gt; of our city. We deploy a self-hosted &lt;strong&gt;GitLab CE&lt;/strong&gt; instance inside our private network, rejecting the "SaaS Pain Points" of public GitHub (latency, data sovereignty, and paid features). We implement a "Blueprint First" deployment strategy to solve the Docker "First Run" password conflict, configure SMTP for notifications, and verify our internal DNS and SSL trust chain by pushing code securely from our Control Center.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Sovereign Software Factory Series:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Part 01:&lt;/strong&gt; Building a Sovereign Software Factory: Docker Networking &amp;amp; Persistence&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 02:&lt;/strong&gt; Building a Sovereign Software Factory: The Local Root CA &amp;amp; Trust Chains&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 03: Building a Sovereign Software Factory: Self-Hosted GitLab &amp;amp; Secrets Management&lt;/strong&gt; (You are here)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 04:&lt;/strong&gt; Building a Sovereign Software Factory: Jenkins Configuration as Code (JCasC)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 05:&lt;/strong&gt; Building a Sovereign Software Factory: Artifactory &amp;amp; The "Strict TLS" Trap&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 06:&lt;/strong&gt; Building a Sovereign Software Factory: SonarQube Quality Gates&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 07:&lt;/strong&gt; Building a Sovereign Software Factory: ChatOps with Mattermost&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 08:&lt;/strong&gt; Building a Sovereign Software Factory: Observability with the ELK Stack&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 09:&lt;/strong&gt; Building a Sovereign Software Factory: Monitoring with Prometheus &amp;amp; Grafana&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 10:&lt;/strong&gt; Building a Sovereign Software Factory: The Python API Package (Capstone)&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Chapter 1: The "Why" - Rejecting the SaaS "Pain Point"
&lt;/h1&gt;

&lt;h2&gt;
  
  
  1.1 Goal: Building the "Central Library"
&lt;/h2&gt;

&lt;p&gt;We have successfully built our "city" foundations. We have a secure "Control Center" (DooD), private "roads" (&lt;code&gt;cicd-net&lt;/code&gt;), persistent "foundations" (our hybrid volume strategy), and a trusted "security office" (our Local CA).&lt;/p&gt;

&lt;p&gt;We are &lt;em&gt;ready&lt;/em&gt; to build, but we have no "blueprints." Our code—the very thing we want to build, test, and deploy—has no home.&lt;/p&gt;

&lt;p&gt;This is the "chaos" pain point. Without a &lt;strong&gt;Source Code Manager (SCM)&lt;/strong&gt;, our projects live in a state of entropy: &lt;code&gt;project-final-v2.zip&lt;/code&gt;, &lt;code&gt;project-final-v3-JOHNS-EDITS.zip&lt;/code&gt;. It's impossible to know who changed what or which version is the "real" one.&lt;/p&gt;

&lt;p&gt;We will solve this by deploying &lt;strong&gt;GitLab Community Edition (CE)&lt;/strong&gt;. This will be the first "skyscraper" in our city and will serve as the "Central Library"—the single source of truth for all our code.&lt;/p&gt;




&lt;h2&gt;
  
  
  1.2 The "First Principles" Question: Why Not Just Use GitHub.com?
&lt;/h2&gt;

&lt;p&gt;Before we run a single command, we must address the obvious "easy button" solution. Why go through the trouble of deploying a complex, self-hosted SCM when &lt;code&gt;GitHub.com&lt;/code&gt; exists?&lt;/p&gt;

&lt;p&gt;The answer is that &lt;code&gt;GitHub.com&lt;/code&gt; is a &lt;strong&gt;SaaS (Software-as-a-Service)&lt;/strong&gt; solution. It's a "black box" that introduces "pain points" that are in direct conflict with the "first principles" architecture we have so carefully built.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The Analogy:&lt;/strong&gt; We are building a private, secure, self-contained "city." &lt;code&gt;GitHub.com&lt;/code&gt; is the &lt;em&gt;public library&lt;/em&gt; located across town, on the other side of the ocean.&lt;/p&gt;

&lt;p&gt;To build our "factories" (Jenkins) and "warehouses" (Artifactory) &lt;em&gt;inside&lt;/em&gt; our city walls and then have them rely on a library on another continent is an architectural flaw. It creates dependencies, security risks, and latency.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;only&lt;/em&gt; architecturally-sound solution is to build our &lt;em&gt;own&lt;/em&gt; "Central Library" &lt;em&gt;inside&lt;/em&gt; our city walls, on our private "road network."&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  1.3 Deconstructing the SaaS "Pain Points"
&lt;/h2&gt;

&lt;p&gt;Relying on an external &lt;code&gt;GitHub.com&lt;/code&gt; fails our "first principles" test by creating three critical "pain points."&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The Network Isolation Pain Point
&lt;/h3&gt;

&lt;p&gt;This is the most immediate problem. &lt;code&gt;GitHub.com&lt;/code&gt; lives on the public internet. Our entire CI/CD stack lives &lt;em&gt;inside&lt;/em&gt; our private, isolated &lt;strong&gt;&lt;code&gt;cicd-net&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This breaks the integration. How can &lt;code&gt;GitHub.com&lt;/code&gt; (on the public internet) send a webhook to &lt;code&gt;https://jenkins.cicd.local:8443&lt;/code&gt; (a private hostname on our &lt;code&gt;cicd-net&lt;/code&gt;)? It can't. It's like shouting from the public library and expecting our "factory foreman" (Jenkins) to hear it inside our walled city.&lt;/p&gt;

&lt;p&gt;To make this work, we would have to punch dangerous holes in our host firewall, set up complex reverse proxies, and expose our internal Jenkins server to the entire internet—a massive security risk.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The Data Sovereignty &amp;amp; Control Pain Point
&lt;/h3&gt;

&lt;p&gt;When you use &lt;code&gt;GitHub.com&lt;/code&gt;, you are a &lt;strong&gt;"tenant"&lt;/strong&gt; on Microsoft's platform. Your source code, your intellectual property, and your project's metadata are all stored on their servers.&lt;/p&gt;

&lt;p&gt;By self-hosting GitLab CE, we become the &lt;strong&gt;"landlord."&lt;/strong&gt; We have 100% control over our own data. Our code lives on our &lt;code&gt;gitlab-data&lt;/code&gt; volume, secured by our Local CA, and is never exposed to a third party.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. The Cost &amp;amp; Feature "Pain Point"
&lt;/h3&gt;

&lt;p&gt;The "free" tier on SaaS platforms is feature-gated to push you to paid plans.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CI/CD "Minutes":&lt;/strong&gt; Public CI services (like GitHub Actions) charge you for compute time, often after a small free quota. This creates a "pain point" of unpredictable costs and limits how often you can build.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Protected Branches":&lt;/strong&gt; This is the most critical pedagogical "gotcha." On &lt;code&gt;GitHub.com&lt;/code&gt;, the ability to "protect" a branch (e.g., to force all changes to go through a Merge Request) is a &lt;strong&gt;paid feature&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This breaks our workflow. The entire &lt;em&gt;point&lt;/em&gt; of our CI pipeline is to run on Merge Requests. By self-hosting &lt;strong&gt;GitLab CE&lt;/strong&gt;, this critical "Protected Branch" feature is &lt;strong&gt;100% free&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  1.4 The Solution: Our Self-Hosted Foundation
&lt;/h2&gt;

&lt;p&gt;A self-hosted GitLab CE instance is the only choice that respects our architecture. It solves all three "pain points":&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; It will live &lt;strong&gt;inside&lt;/strong&gt; our &lt;code&gt;cicd-net&lt;/code&gt;, allowing it to talk to &lt;code&gt;jenkins&lt;/code&gt; and our other tools securely.&lt;/li&gt;
&lt;li&gt; It gives us &lt;strong&gt;100% data sovereignty&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; It gives us critical enterprise features like "Protected Branches" for &lt;strong&gt;free&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We will now build our "Central Library" on the "foundation" we prepared in Articles 1 and 2.&lt;/p&gt;

&lt;h1&gt;
  
  
  Chapter 2: The "What" - Core SCM &amp;amp; GitLab Concepts
&lt;/h1&gt;

&lt;h2&gt;
  
  
  2.1. The "First Principles" of Source Code Management
&lt;/h2&gt;

&lt;p&gt;To understand &lt;em&gt;why&lt;/em&gt; GitLab is the "Central Library" for our "city," we must first deconstruct the problem it solves. This isn't just a GitLab concept; it's the fundamental "pain point" of all software development: &lt;strong&gt;managing change over time.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The "Chaos" (No SCM)
&lt;/h3&gt;

&lt;p&gt;Without an SCM, we live in a world of chaos. We use file naming conventions that are, in effect, a cry for help:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;project-v1.zip&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;project-v2-final.zip&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;project-v2-final-REVISED.zip&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;project-v2-final-JOHNS-EDITS.zip&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This "shared folder" model has no "first principles." There is no history, no accountability, and no way to safely merge changes from two developers.&lt;/p&gt;

&lt;h3&gt;
  
  
  The "Old" Solution: Centralized SCM (e.g., Subversion)
&lt;/h3&gt;

&lt;p&gt;The first generation of tools solved this by introducing a "central server." These are &lt;strong&gt;Centralized Version Control Systems (CVCS)&lt;/strong&gt;. The most famous example is &lt;strong&gt;Subversion (SVN)&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The Analogy:&lt;/strong&gt; An SVN server is a &lt;strong&gt;"Central Library" with a single, strict "Librarian."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To edit a file, you must "check it out" from the librarian. While you have it, no one else can edit it (a "lock"). When you are done, you "check it in," and the librarian saves the new version.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This was a massive improvement, but it created a new, critical "pain point":&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;It's a Single Point of Failure:&lt;/strong&gt; If the "Central Library" (the SVN server) crashes or the network goes down, &lt;em&gt;all development stops&lt;/em&gt;. No one can "check in" or "check out" code.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;It's Not Truly "Versioned":&lt;/strong&gt; Your local machine only has the &lt;em&gt;one version&lt;/em&gt; you checked out. You do not have the &lt;em&gt;entire history&lt;/em&gt; of the project.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The "Modern" Solution: Distributed SCM (i.e., Git)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Git&lt;/strong&gt; is the "first principles" solution to the "pain points" of SVN. It is a &lt;strong&gt;Distributed Version Control System (DVCS)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This is the most critical concept to understand:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The Analogy:&lt;/strong&gt; With Git, you don't just "check out a book" from the library. You &lt;strong&gt;"clone" the entire library.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every single developer on your team has a full, 1-to-1 copy of the &lt;em&gt;entire project and its complete history&lt;/em&gt; on their local machine.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This solves all of SVN's "pain points":&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;There is No Single Point of Failure:&lt;/strong&gt; If the "Central Library" server (which we will build with GitLab) goes down, you don't even notice. You can still browse the full history, create new commits, and switch branches, all on your local machine.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;It's Blazing Fast:&lt;/strong&gt; Since the entire "library" (the &lt;code&gt;.git&lt;/code&gt; directory) is on your local disk, checking history or creating a branch is instantaneous.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The "First Principles" of Git (The Commands)
&lt;/h3&gt;

&lt;p&gt;This "distributed" model is what gives us the core "first principle" concepts that GitLab is built on top of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Repository (Repo):&lt;/strong&gt; This is the "library" itself—the project folder containing all your code and the hidden &lt;code&gt;.git&lt;/code&gt; directory (the "ledger").&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;git init&lt;/code&gt; (Creates a new, empty "library")&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git clone &amp;lt;url&amp;gt;&lt;/code&gt; (Copies an &lt;em&gt;entire existing library&lt;/em&gt; to your machine)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Commit:&lt;/strong&gt; This is a "snapshot" or a "save point" in your local library. It's a permanent record of a change.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;git add .&lt;/code&gt; (Stages your changes for the snapshot)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git commit -m "My change description"&lt;/code&gt; (Creates the permanent snapshot in your local history)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Branch:&lt;/strong&gt; This is an "independent line of development." It's a "copy" of the "master blueprint" that you can safely work on without disturbing the main, stable version.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;git checkout -b my-new-feature&lt;/code&gt; (Creates a new branch &lt;em&gt;and&lt;/em&gt; switches to it)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git checkout main&lt;/code&gt; (Switches back to the main, stable branch)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;This is the "engine" that runs &lt;em&gt;locally&lt;/em&gt;. This leaves one final "pain point": if everyone has their own "copy of the library," how does everyone &lt;em&gt;sync up&lt;/em&gt; their changes?&lt;/p&gt;

&lt;p&gt;That is the role of &lt;strong&gt;GitLab&lt;/strong&gt;. It is the "Central Library" that we &lt;em&gt;designate&lt;/em&gt; as the "single source of truth." It's the "public square" where all developers bring their local changes (&lt;code&gt;git push&lt;/code&gt;) and share them with the team (&lt;code&gt;git pull&lt;/code&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  2.2. The "Collaboration" Principle: The Merge Request
&lt;/h2&gt;

&lt;p&gt;We have established the "first principle" of Git: everyone has a &lt;em&gt;local&lt;/em&gt; "copy of the library" (&lt;code&gt;.git&lt;/code&gt; directory) and can make &lt;em&gt;local&lt;/em&gt; "snapshots" (&lt;code&gt;git commit&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;This leaves us with the final and most important operational "pain point": &lt;strong&gt;How do we safely merge all these individual, distributed changes back into the "Central Library" (GitLab)?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The "easy" or "naive" way is for every developer to simply push their changes directly to the &lt;code&gt;main&lt;/code&gt; branch:&lt;br&gt;
&lt;code&gt;git push origin main&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This is architecturally reckless and creates a new "pain point" of &lt;strong&gt;workflow chaos&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The Analogy:&lt;/strong&gt; Allowing direct pushes to &lt;code&gt;main&lt;/code&gt; is like letting &lt;em&gt;any&lt;/em&gt; engineer walk into the "Central Library" at &lt;em&gt;any&lt;/em&gt; time and scribble directly on the &lt;strong&gt;master blueprint&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;What if their change is wrong? What if it's incomplete? What if two engineers try to edit the same page at the same time? The "master blueprint" becomes a mess of conflicting, untested, and unverified changes. This breaks the build for everyone and defeats the purpose of having a "single source of truth."&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h3&gt;
  
  
  The "First Principles" Solution: The Merge Request
&lt;/h3&gt;

&lt;p&gt;To solve this "collaboration pain point," we introduce the &lt;strong&gt;Merge Request (MR)&lt;/strong&gt;. (This is known as a "Pull Request" or "PR" on GitHub, but we will use GitLab's terminology).&lt;/p&gt;

&lt;p&gt;A Merge Request is the "first principle" of professional, team-based collaboration. It is a formal &lt;em&gt;process&lt;/em&gt; for managing change, not just a &lt;em&gt;command&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The workflow is as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; A developer &lt;strong&gt;never&lt;/strong&gt; works on the &lt;code&gt;main&lt;/code&gt; branch.&lt;/li&gt;
&lt;li&gt; They create a new &lt;strong&gt;branch&lt;/strong&gt; (their "private copy" of the blueprint) (e.g., &lt;code&gt;git checkout -b feature/add-login-button&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt; They make all their &lt;strong&gt;commits&lt;/strong&gt; safely on this private branch.&lt;/li&gt;
&lt;li&gt; When their work is complete, they &lt;code&gt;git push&lt;/code&gt; their &lt;em&gt;branch&lt;/em&gt; (not &lt;code&gt;main&lt;/code&gt;) to GitLab.&lt;/li&gt;
&lt;li&gt; Finally, they go to the GitLab UI and open a &lt;strong&gt;Merge Request&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The Analogy:&lt;/strong&gt; A Merge Request is a &lt;strong&gt;"formal proposal"&lt;/strong&gt; submitted to the "head librarian" (the project maintainers).&lt;/p&gt;

&lt;p&gt;You are not &lt;em&gt;making&lt;/em&gt; the change to the master blueprint. You are &lt;em&gt;proposing&lt;/em&gt; it. You are saying:&lt;/p&gt;

&lt;p&gt;"I have finished my work on my private copy (my 'feature' branch). Here is a summary of my changes. Please review them. If you and the rest of the team approve, please &lt;em&gt;merge&lt;/em&gt; my proposal into the master blueprint ('main')."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This simple process changes everything. It creates a central, auditable "lobby" for every proposed change. It's the place where your team can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Review the Code:&lt;/strong&gt; Leave comments, suggest improvements, and ensure quality.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Discuss the Change:&lt;/strong&gt; Debate the solution and its impact.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify the Solution:&lt;/strong&gt; This is the most critical part.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This &lt;strong&gt;Merge Request&lt;/strong&gt; is the &lt;strong&gt;key trigger&lt;/strong&gt; for our entire CI/CD stack. It is the "starting gun" for our whole automation pipeline. The moment an MR is opened, our "Central Library" (GitLab) will send a signal (a webhook) to our "Factory Foreman" (Jenkins), which will automatically run all the builds, tests, and quality scans.&lt;/p&gt;

&lt;p&gt;This is the &lt;em&gt;entire reason&lt;/em&gt; we are building our stack: to automate the &lt;em&gt;verification&lt;/em&gt; of these "proposals" &lt;em&gt;before&lt;/em&gt; they are ever merged into our "master blueprint."&lt;/p&gt;
&lt;h2&gt;
  
  
  2.3. The "Permissions Pain Point" (GitHub vs. GitLab)
&lt;/h2&gt;

&lt;p&gt;We have our "Central Library" (GitLab) and a formal "proposal" process (Merge Requests). This introduces the final core concept: &lt;strong&gt;access control&lt;/strong&gt;. Who is allowed in the library, and what are they allowed to do?&lt;/p&gt;

&lt;p&gt;This is a massive "pain point" in any large organization. If you have 100 developers and 50 projects, how do you manage their permissions? Manually adding 100 users to 50 different projects (5,000 operations) is not just tedious; it's an auditing and security nightmare.&lt;/p&gt;

&lt;p&gt;This is where the architectural differences between SCM platforms become critical.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The GitHub "Organization" Model:&lt;/strong&gt; GitHub's solution is the &lt;strong&gt;"Organization."&lt;/strong&gt; This is a good model that gathers all your projects under one "roof." You can then create "Teams" (e.g., "Frontend Developers," "Backend Developers") and grant those &lt;em&gt;teams&lt;/em&gt; access to individual repositories. This is a big improvement, but the structure is relatively flat. You still have to manually manage the connections between many teams and many repositories.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The GitLab "Group" Model (The Architectural Solution):&lt;/strong&gt; GitLab solves this "permissions pain point" with a more powerful, &lt;strong&gt;hierarchical namespace&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The Analogy:&lt;/strong&gt; A GitHub "Organization" is like a &lt;em&gt;single, large library building&lt;/em&gt;. You create "teams" (like "History Department") and give them keys to specific rooms (repositories).&lt;/p&gt;

&lt;p&gt;A GitLab &lt;strong&gt;"Group"&lt;/strong&gt; is like an entire &lt;strong&gt;"University Campus."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is the key distinction. A GitLab "Group" (e.g., "College of Engineering") is a container that can hold both:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Projects&lt;/strong&gt; (e.g., the "Robotics Lab Project")&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Sub-Groups&lt;/strong&gt; (e.g., the entire "Computer Science Department," which &lt;em&gt;itself&lt;/em&gt; has its own projects)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The architectural advantage here is &lt;strong&gt;permission inheritance&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When you hire a new developer, you don't give them 50 different keys. You simply add their user account to the "College of Engineering" &lt;strong&gt;Group&lt;/strong&gt; &lt;em&gt;one time&lt;/em&gt; and assign them the "Developer" role.&lt;/p&gt;

&lt;p&gt;By inheritance, they &lt;em&gt;automatically&lt;/em&gt; get "Developer" access to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The "Robotics Lab Project."&lt;/li&gt;
&lt;li&gt;The &lt;em&gt;entire&lt;/em&gt; "Computer Science Department" sub-group.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Every single project&lt;/em&gt; inside the "Computer Science Department."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This hierarchical "Group" model is a far superior architectural solution for managing a complex organization, and it's a core reason we have chosen GitLab as our "Central Library's" architecture. We will use this "Group" feature to organize all the projects for our new "city."&lt;/p&gt;
&lt;h1&gt;
  
  
  Chapter 3: Action Plan (Part 1) — The "Blueprint First" Strategy
&lt;/h1&gt;
&lt;h2&gt;
  
  
  3.1. Prerequisite: The &lt;code&gt;cicd.env&lt;/code&gt; File
&lt;/h2&gt;

&lt;p&gt;Before we run a single script, we must perform our one and only manual setup step. We need to provide the secrets that our setup script will "bake" into the GitLab configuration.&lt;/p&gt;

&lt;p&gt;We will create a file named &lt;strong&gt;&lt;code&gt;cicd.env&lt;/code&gt;&lt;/strong&gt; inside our project's &lt;code&gt;~/cicd_stack&lt;/code&gt; directory. This file is &lt;strong&gt;not&lt;/strong&gt; used by Docker. It is a prerequisite that is read &lt;em&gt;only&lt;/em&gt; by our &lt;code&gt;01-initial-setup.sh&lt;/code&gt; script. Our setup script is smart enough to add this filename to &lt;code&gt;.gitignore&lt;/code&gt; automatically, ensuring we never accidentally commit our private passwords.&lt;/p&gt;

&lt;p&gt;On your host machine, create this file. You can use &lt;code&gt;nano&lt;/code&gt; or any text editor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nano ~/cicd_stack/cicd.env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the following two lines. It is &lt;strong&gt;critical&lt;/strong&gt; that you replace the placeholder values with your real credentials.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="py"&gt;GITLAB_ROOT_PASSWORD&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"your-secure-password-here"&lt;/span&gt;
&lt;span class="py"&gt;GMAIL_APP_PASSWORD&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"your-16-char-password-here"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A Note on Passwords:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;GITLAB_ROOT_PASSWORD&lt;/code&gt;&lt;/strong&gt;: This will become the password for the &lt;code&gt;root&lt;/code&gt; administrator account. Make it strong and memorable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;GMAIL_APP_PASSWORD&lt;/code&gt;&lt;/strong&gt;: This is &lt;strong&gt;not&lt;/strong&gt; your regular Gmail password. It is a 16-character "App Password" you must generate from your Google Account security settings. (We will walk through the exact UI steps for this in Chapter 5, but it must be generated for this script to work).&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once this file is saved, you have completed the only manual prerequisite. Our setup script will handle everything else.&lt;/p&gt;

&lt;h2&gt;
  
  
  3.2. The "First Run" Conflict (Our Key Discovery)
&lt;/h2&gt;

&lt;p&gt;With our secrets file in place, we were ready to build our configuration. Our goal was to provide all configuration &lt;em&gt;before&lt;/em&gt; the container started. This led us to what &lt;em&gt;should&lt;/em&gt; have been a simple, two-part setup:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;For SSL/SMTP:&lt;/strong&gt; Write a custom &lt;code&gt;gitlab.rb&lt;/code&gt; file and bind-mount it to &lt;code&gt;/etc/gitlab&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;For the Password:&lt;/strong&gt; Securely pass the &lt;code&gt;GITLAB_ROOT_PASSWORD&lt;/code&gt; using the &lt;code&gt;--env-file&lt;/code&gt; flag in our &lt;code&gt;docker run&lt;/code&gt; command.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This logical approach led us to a critical failure and our most important discovery. When we ran the container with &lt;strong&gt;both&lt;/strong&gt; the bind-mounted &lt;code&gt;gitlab.rb&lt;/code&gt; &lt;em&gt;and&lt;/em&gt; the &lt;code&gt;--env-file&lt;/code&gt; flag, the container entered a "confused" state.&lt;/p&gt;

&lt;p&gt;This is the conflict:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The container started successfully.&lt;/li&gt;
&lt;li&gt;NGINX read our &lt;code&gt;gitlab.rb&lt;/code&gt; and loaded our SSL certificates.&lt;/li&gt;
&lt;li&gt;But the "first-run" password logic &lt;strong&gt;failed silently&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The password from our &lt;code&gt;--env-file&lt;/code&gt; was &lt;strong&gt;ignored&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The container &lt;strong&gt;did not&lt;/strong&gt; generate a random password, because it assumed our environment variable would work.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We were left with a perfectly-secured, SSL-enabled container that had no &lt;code&gt;root&lt;/code&gt; password. The login page would just fail indefinitely.&lt;/p&gt;

&lt;p&gt;We discovered that the presence of the &lt;strong&gt;&lt;code&gt;--env-file&lt;/code&gt;&lt;/strong&gt; flag, when used &lt;em&gt;in combination with&lt;/em&gt; a pre-populated &lt;code&gt;/etc/gitlab&lt;/code&gt; volume, breaks the container's "first-run" password logic.&lt;/p&gt;

&lt;p&gt;This discovery was the key. We couldn't trust the container to manage its environment and our config file at the same time. We had to choose one.&lt;/p&gt;

&lt;h2&gt;
  
  
  3.3. The Solution: "Baking" Credentials
&lt;/h2&gt;

&lt;p&gt;This discovery led us to our final, robust solution. Since the container cannot be trusted to handle both an environment file and a pre-configured &lt;code&gt;gitlab.rb&lt;/code&gt; file, we will remove the source of the conflict: &lt;strong&gt;we will not use the &lt;code&gt;--env-file&lt;/code&gt; flag at all.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Instead, we will create a master "blueprint" on our host &lt;em&gt;before&lt;/em&gt; the container ever runs. This blueprint (&lt;code&gt;gitlab.rb&lt;/code&gt;) will contain &lt;strong&gt;everything&lt;/strong&gt; the container needs to know: our SSL paths, our SMTP settings, and, most importantly, the administrator password.&lt;/p&gt;

&lt;p&gt;We will accomplish this using our &lt;strong&gt;&lt;code&gt;01-initial-setup.sh&lt;/code&gt;&lt;/strong&gt; script. This script will act as our "architect," performing a clever "bake-in" process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; It will first read our &lt;code&gt;cicd.env&lt;/code&gt; file on the host, loading the &lt;code&gt;GITLAB_ROOT_PASSWORD&lt;/code&gt; and &lt;code&gt;GMAIL_APP_PASSWORD&lt;/code&gt; into its own environment.&lt;/li&gt;
&lt;li&gt; It will then programmatically "bake" (i.e., hardcode) the &lt;em&gt;values&lt;/em&gt; of these variables directly into the text of the &lt;code&gt;gitlab.rb&lt;/code&gt; file it generates.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When the GitLab container starts, it will be completely unaware of our &lt;code&gt;cicd.env&lt;/code&gt; file. It will simply see a &lt;code&gt;gitlab.rb&lt;/code&gt; file with lines like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# (This is what the container will see)&lt;/span&gt;
&lt;span class="n"&gt;gitlab_rails&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'initial_root_password'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'MySuperSecurePassword123'&lt;/span&gt;
&lt;span class="n"&gt;gitlab_rails&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'smtp_password'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'ab12cd34ef56gh78'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This bypasses the container's broken "first-run" logic entirely. The container's setup script will read this hardcoded value from &lt;code&gt;gitlab.rb&lt;/code&gt; and use it to correctly initialize the database.&lt;/p&gt;

&lt;p&gt;While hardcoding secrets in a configuration file is generally not ideal, it is the only reliable and repeatable method to solve this specific "first-run" conflict caused by the GitLab Docker image. This "Blueprint First" strategy gives us a single, complete, and fully-configured blueprint to hand off to our deployment script.&lt;/p&gt;

&lt;h2&gt;
  
  
  3.4. The "Architect" Script (&lt;code&gt;01-initial-setup.sh&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;This is our "architect" script. Its sole purpose is to run on the host machine and prepare the &lt;em&gt;entire&lt;/em&gt; configuration "blueprint" (&lt;code&gt;gitlab.rb&lt;/code&gt;) and "site" (the required directories) &lt;em&gt;before&lt;/em&gt; the container is ever created. It performs all the preparation we've discussed: checking prerequisites, creating directories, solving our CA trust issue, and "baking" our secrets.&lt;/p&gt;

&lt;p&gt;Here is the complete script.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# 🚀 01-initial-setup.sh&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# This script prepares the host environment for the GitLab container.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# ❗ PREREQUISITE:&lt;/span&gt;
&lt;span class="c"&gt;# You MUST create the 'cicd.env' file in the '~/cicd_stack' directory&lt;/span&gt;
&lt;span class="c"&gt;# *before* running this script. It must contain:&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#   GITLAB_ROOT_PASSWORD="your-secure-password"&lt;/span&gt;
&lt;span class="c"&gt;#   GMAIL_APP_PASSWORD="your-16-char-password"&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;

&lt;span class="c"&gt;# --- Configuration ---&lt;/span&gt;
&lt;span class="nv"&gt;CICD_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/cicd_stack"&lt;/span&gt;
&lt;span class="nv"&gt;ENV_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CICD_DIR&lt;/span&gt;&lt;span class="s2"&gt;/cicd.env"&lt;/span&gt;
&lt;span class="nv"&gt;GITIGNORE_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CICD_DIR&lt;/span&gt;&lt;span class="s2"&gt;/.gitignore"&lt;/span&gt;
&lt;span class="nv"&gt;GITIGNORE_ENTRY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"cicd.env"&lt;/span&gt;

&lt;span class="c"&gt;# --- GitLab Config Paths ---&lt;/span&gt;
&lt;span class="nv"&gt;GITLAB_CONFIG_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CICD_DIR&lt;/span&gt;&lt;span class="s2"&gt;/gitlab/config"&lt;/span&gt;
&lt;span class="nv"&gt;GITLAB_TRUSTED_CERTS_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GITLAB_CONFIG_DIR&lt;/span&gt;&lt;span class="s2"&gt;/trusted-certs"&lt;/span&gt;
&lt;span class="nv"&gt;GITLAB_CONFIG_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GITLAB_CONFIG_DIR&lt;/span&gt;&lt;span class="s2"&gt;/gitlab.rb"&lt;/span&gt;

&lt;span class="c"&gt;# --- CA Paths ---&lt;/span&gt;
&lt;span class="nv"&gt;CA_CERT_SOURCE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/cicd_stack/ca/pki/certs/ca.pem"&lt;/span&gt;
&lt;span class="nv"&gt;CA_CERT_DEST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GITLAB_TRUSTED_CERTS_DIR&lt;/span&gt;&lt;span class="s2"&gt;/ca.pem"&lt;/span&gt;


&lt;span class="c"&gt;# --- Script ---&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🚀 Starting setup..."&lt;/span&gt;

&lt;span class="c"&gt;# 1. 🛑 PREREQUISITE CHECK: Stop if cicd.env is missing&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"---------------------------------------------------------------"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"⛔ ERROR: '&lt;/span&gt;&lt;span class="nv"&gt;$ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;' not found."&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Please create this file with your passwords before running."&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"It must contain:"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"  GITLAB_ROOT_PASSWORD=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;...&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"  GMAIL_APP_PASSWORD=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;...&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"---------------------------------------------------------------"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# 2. Ensure the base directory exists (owned by user)&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CICD_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# 3. Add cicd.env to .gitignore (owned by user)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GITIGNORE_ENTRY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GITIGNORE_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;/dev/null&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Adding &lt;/span&gt;&lt;span class="nv"&gt;$GITIGNORE_ENTRY&lt;/span&gt;&lt;span class="s2"&gt; to &lt;/span&gt;&lt;span class="nv"&gt;$GITIGNORE_FILE&lt;/span&gt;&lt;span class="s2"&gt;..."&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GITIGNORE_ENTRY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GITIGNORE_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GITIGNORE_ENTRY&lt;/span&gt;&lt;span class="s2"&gt; is already in &lt;/span&gt;&lt;span class="nv"&gt;$GITIGNORE_FILE&lt;/span&gt;&lt;span class="s2"&gt;. Skipping."&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# 4. Ensure the gitlab config directories exist (requires sudo)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Ensuring GitLab config directories exist (may ask for password)..."&lt;/span&gt;
&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GITLAB_CONFIG_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GITLAB_TRUSTED_CERTS_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# 5. Copy the CA certificate into the trusted-certs directory (requires sudo)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CA_CERT_SOURCE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Copying CA certificate to &lt;/span&gt;&lt;span class="nv"&gt;$CA_CERT_DEST&lt;/span&gt;&lt;span class="s2"&gt; (may ask for password)..."&lt;/span&gt;
    &lt;span class="nb"&gt;sudo cp&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CA_CERT_SOURCE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CA_CERT_DEST&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"⚠️ WARNING: CA certificate not found at &lt;/span&gt;&lt;span class="nv"&gt;$CA_CERT_SOURCE&lt;/span&gt;&lt;span class="s2"&gt;."&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Please ensure your CA is generated before running GitLab."&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# 6. Create the gitlab.rb configuration file (requires sudo)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Creating custom gitlab.rb configuration (may ask for password)..."&lt;/span&gt;

&lt;span class="c"&gt;# Load the .env file variables into this script's environment&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="c"&gt;# Automatically export all variables that are defined&lt;/span&gt;
&lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; +a &lt;span class="c"&gt;# Stop auto-exporting&lt;/span&gt;

&lt;span class="c"&gt;# This sudo block injects the *values* from the .env file&lt;/span&gt;
&lt;span class="c"&gt;# directly into the gitlab.rb file.&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;bash &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"cat &amp;lt;&amp;lt; EOF &amp;gt; &lt;/span&gt;&lt;span class="nv"&gt;$GITLAB_CONFIG_FILE&lt;/span&gt;&lt;span class="s2"&gt;
# --- Main GitLab URL ---
# This is the address all containers on 'cicd-net' will use.
external_url 'https://gitlab.cicd.local:10300'

# --- Custom SSL Configuration ---
nginx['enable'] = true
nginx['ssl_certificate'] = '/etc/gitlab/ssl/gitlab.cicd.local.crt.pem'
nginx['ssl_certificate_key'] = '/etc/gitlab/ssl/gitlab.cicd.local.key.pem'

# --- Disable Let's Encrypt ---
letsencrypt['enable'] = false

# --- SMTP Email (Gmail) Configuration ---
# NOTE: Update 'YOUR_GMAIL_EMAIL_HERE' with your actual Gmail address
# The password is hardcoded from the .env file during setup.
gitlab_rails['smtp_enable'] = true
gitlab_rails['smtp_address'] = 'smtp.gmail.com'
gitlab_rails['smtp_port'] = 587
gitlab_rails['smtp_user_name'] = 'YOUR_GMAIL_EMAIL_HERE'
gitlab_rails['smtp_password'] = '&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GMAIL_APP_PASSWORD&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;'
gitlab_rails['smtp_domain'] = 'smtp.gmail.com'
gitlab_rails['smtp_authentication'] = 'login'
gitlab_rails['smtp_enable_starttls_auto'] = true
gitlab_rails['smtp_tls'] = false

# --- Email From/Reply-to Settings ---
# NOTE: Update 'YOUR_GMAIL_EMAIL_HERE' and 'YOUR_DOMAIN_HERE'
gitlab_rails['gitlab_email_from'] = 'YOUR_GMAIL_EMAIL_HERE'
gitlab_rails['gitlab_email_reply_to'] = 'noreply@YOUR_DOMAIN_HERE'

# --- Set Initial Root Password ---
# The password is hardcoded from the .env file during setup.
gitlab_rails['initial_root_password'] = '&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GITLAB_ROOT_PASSWORD&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;'

EOF"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✅ Setup complete. You are now ready to run 02-create-docker.sh"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  3.5. Deconstruction of &lt;code&gt;01-initial-setup.sh&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;This script is dense, but every step is a deliberate solution to a problem we uncovered. Let's deconstruct it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Step 1: Prerequisite Check&lt;/strong&gt;: The script begins with a critical safety check. It ensures the &lt;code&gt;cicd.env&lt;/code&gt; file we created in 3.1 actually exists. If it doesn't, the script immediately exits with a helpful error message. This prevents us from ever running a misconfigured setup.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Steps 2 &amp;amp; 3: Housekeeping&lt;/strong&gt;: These steps create the base &lt;code&gt;~/cicd_stack&lt;/code&gt; directory and add our secret &lt;code&gt;cicd.env&lt;/code&gt; file to &lt;code&gt;.gitignore&lt;/code&gt; to prevent us from ever accidentally committing it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Steps 4 &amp;amp; 5: Directory Scaffolding &amp;amp; CA Trust&lt;/strong&gt;: This is where we see the first piece of our proactive strategy. The script creates the &lt;code&gt;gitlab/config&lt;/code&gt; and, crucially, the &lt;strong&gt;&lt;code&gt;gitlab/config/trusted-certs&lt;/code&gt;&lt;/strong&gt; directories. It then immediately copies our &lt;code&gt;ca.pem&lt;/code&gt; (from Article 2) &lt;em&gt;into&lt;/em&gt; this &lt;code&gt;trusted-certs&lt;/code&gt; folder.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;This single step solves the "Outbound Trust" problem before it ever begins.&lt;/strong&gt; When GitLab starts for the first time, its &lt;code&gt;reconfigure&lt;/code&gt; script will see our CA certificate already in place and will automatically build its trust store, adding our CA. It will then be able to send secure webhooks to &lt;code&gt;https://jenkins.cicd.local&lt;/code&gt; or talk to other internal, SSL-secured services without a single SSL error. No second &lt;code&gt;reconfigure&lt;/code&gt; is ever needed.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Step 6: The "Bake-in"&lt;/strong&gt;: This is the most important part of the script and the core of our solution.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;set -a / source "$ENV_FILE" / set +a&lt;/code&gt;&lt;/strong&gt;: This triplet of commands loads our secrets. &lt;code&gt;source&lt;/code&gt; "runs" the &lt;code&gt;cicd.env&lt;/code&gt; file, which sets the &lt;code&gt;GITLAB_ROOT_PASSWORD&lt;/code&gt; and &lt;code&gt;GMAIL_APP_PASSWORD&lt;/code&gt; variables within the script's environment.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;sudo bash -c "cat &amp;lt;&amp;lt; EOF &amp;gt; ..."&lt;/code&gt;&lt;/strong&gt;: This is the "bake-in" operation. We use &lt;code&gt;sudo&lt;/code&gt; to gain root privileges, which are necessary to write into the &lt;code&gt;gitlab/config&lt;/code&gt; directory (as &lt;code&gt;sudo&lt;/code&gt; created it). The &lt;code&gt;cat &amp;lt;&amp;lt; EOF &amp;gt; $GITLAB_CONFIG_FILE&lt;/code&gt; command writes a "here document" (all the text until the final &lt;code&gt;EOF&lt;/code&gt;) into our &lt;code&gt;gitlab.rb&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Variable Expansion&lt;/strong&gt;: Because we &lt;code&gt;source&lt;/code&gt;'d the variables first, the shell &lt;em&gt;expands&lt;/em&gt; them &lt;em&gt;before&lt;/em&gt; &lt;code&gt;sudo&lt;/code&gt; ever runs. This means the line &lt;code&gt;gitlab_rails['initial_root_password'] = '${GITLAB_ROOT_PASSWORD}'&lt;/code&gt; becomes &lt;code&gt;gitlab_rails['initial_root_password'] = 'MySuperSecurePassword123'&lt;/code&gt; &lt;em&gt;inside&lt;/em&gt; the final file.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  3.6. Deconstruction of the "Baked" &lt;code&gt;gitlab.rb&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;gitlab.rb&lt;/code&gt; file generated by our script is the master blueprint for the entire container. Let's analyze each block:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;external_url 'https://gitlab.cicd.local:10300'&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
This is arguably the most critical setting. It tells GitLab what its "public" URL is. Because all our CI/CD services (like Jenkins, SonarQube, and our dev-container) live on the same &lt;code&gt;cicd-net&lt;/code&gt;, they will all be able to find and access this container using its internal DNS name, &lt;code&gt;gitlab.cicd.local&lt;/code&gt;. We match the port to the host port to prevent browser issues stemming from the split-horizon DNS problem.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;nginx['...']&lt;/code&gt; and &lt;code&gt;letsencrypt['...']&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
This block tells GitLab's built-in Nginx web server to &lt;em&gt;disable&lt;/em&gt; its default Let's Encrypt integration (&lt;code&gt;letsencrypt['enable'] = false&lt;/code&gt;). Instead, we explicitly point it to the "passports" (our custom SSL certificate and key from Article 2) that we will be mounting into the &lt;code&gt;/etc/gitlab/ssl/&lt;/code&gt; directory.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;gitlab_rails['smtp_...']&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
This is our full SMTP configuration for sending notification emails via Gmail. It sets the server, port, and authentication. Most importantly, the line &lt;code&gt;gitlab_rails['smtp_password'] = '${GMAIL_APP_PASSWORD}'&lt;/code&gt; is where the 16-character App Password (which we "baked-in") is placed, allowing GitLab to authenticate with Google. You will still need to edit this file one time to replace &lt;code&gt;YOUR_GMAIL_EMAIL_HERE&lt;/code&gt; with your actual email address.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;gitlab_rails['initial_root_password'] = '${GITLAB_ROOT_PASSWORD}'&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
This is the &lt;strong&gt;solution to our entire login problem&lt;/strong&gt;. By "baking" the password from our &lt;code&gt;cicd.env&lt;/code&gt; file directly into this line, we force GitLab's "first-run" database script to use &lt;em&gt;our&lt;/em&gt; password. It bypasses all the broken logic, never generates a random password, and ensures that when the container is ready, we can log in with the exact credentials we intended.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Chapter 4: Action Plan (Part 2) — Deployment &amp;amp; First Login
&lt;/h1&gt;

&lt;p&gt;With our "architect" script (&lt;code&gt;01-initial-setup.sh&lt;/code&gt;) having perfectly prepared the host environment, our "blueprint" (&lt;code&gt;gitlab.rb&lt;/code&gt;) is now complete, with all our configurations and secrets "baked" in.&lt;/p&gt;

&lt;p&gt;The hard part is over. All that's left is to bring in the "construction crew" and build the skyscraper. This second script is beautifully simple, as all the complex logic now lives in the configuration file we just built.&lt;/p&gt;

&lt;h2&gt;
  
  
  4.1. The "Construction" Script (&lt;code&gt;02-create-docker.sh&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;This script's only job is to run the &lt;code&gt;docker run&lt;/code&gt; command. It takes our prepared config directory, our SSL certificates, and our persistent volumes and assembles them into a single, running container.&lt;/p&gt;

&lt;p&gt;Here is the complete &lt;code&gt;02-create-docker.sh&lt;/code&gt; script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="c"&gt;# This script launches the GitLab container with all&lt;/span&gt;
&lt;span class="c"&gt;# the networking, volumes, and security settings.&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🚀 Starting GitLab container..."&lt;/span&gt;

docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; gitlab &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--restart&lt;/span&gt; always &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--hostname&lt;/span&gt; gitlab.cicd.local &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network&lt;/span&gt; cicd-net &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--publish&lt;/span&gt; 127.0.0.1:10300:443 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--publish&lt;/span&gt; 127.0.0.1:10301:22 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; gitlab-data:/var/opt/gitlab &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; gitlab-logs:/var/log/gitlab &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/cicd_stack/gitlab/config"&lt;/span&gt;:/etc/gitlab &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/cicd_stack/ca/pki/services/gitlab.cicd.local"&lt;/span&gt;:/etc/gitlab/ssl:ro &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--shm-size&lt;/span&gt; 256m &lt;span class="se"&gt;\&lt;/span&gt;
  gitlab/gitlab-ce:latest

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✅ GitLab container is starting."&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Monitor its progress with: docker logs -f gitlab"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  4.2. Deconstruction of the &lt;code&gt;docker run&lt;/code&gt; Command
&lt;/h2&gt;

&lt;p&gt;Every single flag in this command is a deliberate architectural decision, connecting back to the foundations we laid in Articles 1 and 2.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;--network cicd-net&lt;/code&gt; &amp;amp; &lt;code&gt;--hostname gitlab.cicd.local&lt;/code&gt;&lt;/strong&gt;: This is the payoff from Article 1. We connect our "skyscraper" to our private "city road network" (&lt;code&gt;cicd-net&lt;/code&gt;) and give it an internal DNS name (&lt;code&gt;gitlab&lt;/code&gt;). This is what will allow our dev-container (and later, Jenkins) to find it by simply using the address &lt;code&gt;https://gitlab.cicd.local&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;--publish 127.0.0.1:10300:443&lt;/code&gt; &amp;amp; &lt;code&gt;--publish 127.0.0.1:10301:22&lt;/code&gt;&lt;/strong&gt;: This is our port mapping scheme.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It exposes the container's internal HTTPS (443) and SSH (22) ports to &lt;code&gt;10300&lt;/code&gt; and &lt;code&gt;10301&lt;/code&gt; on our host.&lt;/li&gt;
&lt;li&gt;Critically, it binds &lt;strong&gt;only to &lt;code&gt;127.0.0.1&lt;/code&gt; (localhost)&lt;/strong&gt;. This is a key security choice. It means our GitLab instance is &lt;em&gt;not&lt;/em&gt; exposed to our local LAN (e.g., your office Wi-Fi). It is only accessible from your host machine.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;--volume gitlab-data:/var/opt/gitlab&lt;/code&gt; &amp;amp; &lt;code&gt;--volume gitlab-logs:/var/log/gitlab&lt;/code&gt;&lt;/strong&gt;: This is our "storage locker" persistence strategy from Article 1. We are mounting our pre-made named volumes to store all of GitLab's opaque, internal data (like its database, repositories, and logs).&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;--volume "${HOME}/cicd_stack/gitlab/config":/etc/gitlab&lt;/code&gt;&lt;/strong&gt;: This is the &lt;strong&gt;grand payoff&lt;/strong&gt; of our entire "Blueprint First" strategy. We are mounting our &lt;em&gt;fully-configured&lt;/em&gt; host directory (which contains our baked &lt;code&gt;gitlab.rb&lt;/code&gt; and &lt;code&gt;trusted-certs&lt;/code&gt; folder) directly into the container.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;--volume "${HOME}/cicd_stack/ca/pki/services/gitlab.cicd.local":/etc/gitlab/ssl:ro&lt;/code&gt;&lt;/strong&gt;: This is the payoff from Article 2. We mount our &lt;code&gt;gitlab&lt;/code&gt; "passport" (its SSL certificate and key) into the container in &lt;code&gt;ro&lt;/code&gt; (read-only) mode for security.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;--shm-size 256m&lt;/code&gt;&lt;/strong&gt;: This is a performance optimization. GitLab's internal services use shared memory, and the Docker default is too small (64m), which can cause random crashes. We explicitly give it more memory to ensure stability.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Missing Flag: &lt;code&gt;--env-file&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The most important part of this command is what's &lt;em&gt;missing&lt;/em&gt;. We are &lt;strong&gt;intentionally not using &lt;code&gt;--env-file&lt;/code&gt;&lt;/strong&gt;. By "baking" our secrets directly into &lt;code&gt;gitlab.rb&lt;/code&gt;, we have eliminated the source of our "first-run" conflict, leading to a much cleaner and more reliable deployment.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  4.3. The First Run: "Reconfiguring..."
&lt;/h2&gt;

&lt;p&gt;Now, it's time to launch.&lt;/p&gt;

&lt;p&gt;The workflow is simple:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Ensure you have created and edited your &lt;code&gt;~/cicd_stack/cicd.env&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt; Run your setup script: &lt;code&gt;./01-initial-setup.sh&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt; Run your deployment script: &lt;code&gt;./02-create-docker.sh&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As soon as you run the second script, you must be patient. GitLab is a very large application. On first boot, it will run its internal &lt;code&gt;reconfigure&lt;/code&gt; script to apply all the settings from our &lt;code&gt;gitlab.rb&lt;/code&gt; file, build its database, and start all its services.&lt;/p&gt;

&lt;p&gt;This process can take &lt;strong&gt;3 to 5 minutes&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You can (and should) watch this process in real-time by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker logs &lt;span class="nt"&gt;-f&lt;/span&gt; gitlab
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will see a massive flood of text as it configures all its components. You will know it is finished when the log messages slow down and you see "Chef Client finished" messages, followed by the services (Puma, Sidekiq, etc.) starting up. Do not attempt to log in until this process is complete.&lt;/p&gt;

&lt;h2&gt;
  
  
  4.4. Verification: The First Login
&lt;/h2&gt;

&lt;p&gt;Once the logs have settled, open your web browser on your host machine and navigate to the &lt;code&gt;localhost&lt;/code&gt; port we published:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;https://127.0.0.1:10300&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Thanks to the foundational work we did in Article 2—where we imported our Local CA's root certificate into our host machine's system trust store—your browser will greet you with a &lt;strong&gt;secure lock icon&lt;/strong&gt;. There will be no security warnings. Your browser recognizes and trusts the &lt;code&gt;gitlab&lt;/code&gt; certificate because it was signed by our now-trusted Certificate Authority.&lt;/p&gt;

&lt;p&gt;You will see the GitLab login page.&lt;/p&gt;

&lt;p&gt;Now, for the moment of truth. Log in using the credentials &lt;strong&gt;you&lt;/strong&gt; defined in your &lt;code&gt;cicd.env&lt;/code&gt; file:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Username:&lt;/strong&gt; &lt;code&gt;root&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Password:&lt;/strong&gt; (The &lt;code&gt;GITLAB_ROOT_PASSWORD&lt;/code&gt; value you set)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because we "baked" this password directly into the &lt;code&gt;gitlab.rb&lt;/code&gt; file, it will work on the very first try.&lt;/p&gt;

&lt;p&gt;It is advisable to change the root password by clicking on the root user's avatar, clicking Preferences and navigating to the Password tab.&lt;/p&gt;

&lt;h3&gt;
  
  
  Verification (Curls)
&lt;/h3&gt;

&lt;p&gt;Finally, let's run our "proof of success" checks from our terminals.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;From your Host terminal:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  curl https://127.0.0.1:10300
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;From your dev-container terminal:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  &lt;span class="c"&gt;# (Inside the dev-container)&lt;/span&gt;
  curl https://gitlab.cicd.local:10300
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In both cases, you should see a &lt;code&gt;302 Found&lt;/code&gt; redirect and the HTML for the login page. The commands will work without any &lt;code&gt;-k&lt;/code&gt; or &lt;code&gt;--insecure&lt;/code&gt; flags, proving that both your host and your dev-container trust our new GitLab instance. This confirms that your host port mapping, internal DNS, and end-to-end SSL are all working perfectly.&lt;/p&gt;

&lt;h1&gt;
  
  
  Chapter 5: UI Configuration (Securing the Workflow)
&lt;/h1&gt;

&lt;p&gt;With our GitLab container running, secured, and accessible, the "construction" phase is complete. We now put on our "administrator" hat. The next steps involve configuring the GitLab application itself, creating the structures for our teams, and securing our development workflow.&lt;/p&gt;

&lt;p&gt;We will also complete the setup for one of our "baked-in" configurations: the &lt;code&gt;GMAIL_APP_PASSWORD&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  5.1. UI Guide: Creating a Google "App Password"
&lt;/h2&gt;

&lt;p&gt;In our &lt;code&gt;01-initial-setup.sh&lt;/code&gt; script, we "baked" a &lt;code&gt;GMAIL_APP_PASSWORD&lt;/code&gt; into our &lt;code&gt;gitlab.rb&lt;/code&gt; file. This is &lt;strong&gt;not&lt;/strong&gt; your normal Gmail password. Google's modern security ("Less Secure Apps" deprecation) requires us to generate a special, 16-character password for third-party applications like GitLab.&lt;/p&gt;

&lt;p&gt;If you haven't generated this password yet, here is the new, more direct process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Go to your Google Account settings: &lt;strong&gt;&lt;a href="https://myaccount.google.com/" rel="noopener noreferrer"&gt;myaccount.google.com&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt; Navigate to the &lt;strong&gt;Security&lt;/strong&gt; tab.&lt;/li&gt;
&lt;li&gt; Ensure &lt;strong&gt;2-Step Verification&lt;/strong&gt; is turned &lt;strong&gt;On&lt;/strong&gt;. You cannot create App Passwords without it.&lt;/li&gt;
&lt;li&gt; Use the search bar at the top of your account page and type in &lt;strong&gt;"App Passwords"&lt;/strong&gt;. Click the result.&lt;/li&gt;
&lt;li&gt; You will be prompted to sign in again for security.&lt;/li&gt;
&lt;li&gt; On the App Passwords screen, you will be prompted to give your new password a name.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;App name:&lt;/strong&gt; Give it a descriptive name, like &lt;strong&gt;"GitLab-CICD"&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Generate&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; Google will present you with a 16-character password in a yellow box (e.g., &lt;code&gt;xxxx yyyy zzzz wwww&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt; This is the password you must place in your &lt;code&gt;~/cicd_stack/cicd.env&lt;/code&gt; file for the &lt;code&gt;GMAIL_APP_PASSWORD&lt;/code&gt; variable.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you already had this set up, your email notifications for password resets and new accounts will now work. If you just generated this password for the first time, you must:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Update your &lt;code&gt;~/cicd_stack/cicd.env&lt;/code&gt; file with the new password.&lt;/li&gt;
&lt;li&gt; Re-run &lt;code&gt;./01-initial-setup.sh&lt;/code&gt; to "re-bake" the &lt;code&gt;gitlab.rb&lt;/code&gt; file with the new password.&lt;/li&gt;
&lt;li&gt; Restart the container to apply the new configuration: &lt;code&gt;docker stop gitlab &amp;amp;&amp;amp; docker start gitlab&lt;/code&gt;. I had encountered a problem with just using &lt;code&gt;docker restart gitlab&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  5.1.1. (Optional) Verifying Your SMTP Setup
&lt;/h3&gt;

&lt;p&gt;Now that you have your Gmail App Password configured, you can directly test GitLab's email functionality from within the container.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;First, open a "Rails console" session inside your running GitLab container. This command will drop you into an interactive Ruby prompt:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; gitlab gitlab-rails console
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;&lt;em&gt;(Note: It is normal for this to take 30-60 seconds to load.)&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Once you see the &lt;code&gt;irb(main):001:0&amp;gt;&lt;/code&gt; prompt, type the following command, replacing the email address with your own. The &lt;code&gt;.deliver_now&lt;/code&gt; is crucial as it forces the email to be sent immediately:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Notify&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'your_email@gmail.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'GitLab Test Email'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'This is a test message from your GitLab instance.'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;deliver_now&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;After a few seconds, you should see a large object output (the email object) and then a &lt;code&gt;=&amp;gt;&lt;/code&gt; line.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Type &lt;code&gt;exit&lt;/code&gt; to leave the console.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Within a minute, you should receive the test email in your Gmail inbox. If you do, your SMTP is configured perfectly. If not, double-check your &lt;code&gt;GMAIL_APP_PASSWORD&lt;/code&gt; in &lt;code&gt;cicd.env&lt;/code&gt; and your &lt;code&gt;smtp_user_name&lt;/code&gt; in &lt;code&gt;gitlab.rb&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  5.2. UI Guide: Creating Our Groups
&lt;/h2&gt;

&lt;p&gt;As we discussed in &lt;strong&gt;Chapter 1&lt;/strong&gt;, GitLab's key architectural advantage is its hierarchical &lt;strong&gt;"Group"&lt;/strong&gt; system. A "Group" is like a "University Campus" or a top-level organization. It can contain both projects and even &lt;em&gt;sub-groups&lt;/em&gt; (like "departments").&lt;/p&gt;

&lt;p&gt;We will create two top-level "Groups" to properly organize all our projects:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;CICD-Stack&lt;/code&gt;&lt;/strong&gt;: This will house test projects specific to our CI/CD pipeline, like the &lt;code&gt;hello-world&lt;/code&gt; project we'll create later.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;Articles&lt;/code&gt;&lt;/strong&gt;: This will be the new home for all the projects we've built in previous articles, like &lt;code&gt;0004_std_lib_http_client&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's create both now.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; From the GitLab dashboard, click the &lt;strong&gt;plus icon (&lt;code&gt;+&lt;/code&gt;)&lt;/strong&gt; in the top-left area of the navigation sidebar (it's to the left of your user avatar).&lt;/li&gt;
&lt;li&gt; A dropdown menu will appear. Select &lt;strong&gt;"New group"&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; On the next page, you'll be asked what you want to create. Click the large &lt;strong&gt;"Create group"&lt;/strong&gt; tile or button.&lt;/li&gt;
&lt;li&gt; This brings you to the "Create group" form:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Group name:&lt;/strong&gt; Type &lt;code&gt;CICD-Stack&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Group URL:&lt;/strong&gt; This will auto-populate based on the name.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Visibility level:&lt;/strong&gt; Select &lt;strong&gt;"Private"&lt;/strong&gt;. This ensures only logged-in members of your instance can see this group and its projects.&lt;/li&gt;
&lt;li&gt;(Optional) You can skip the "personalize" questions for now.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; Click the blue &lt;strong&gt;"Create group"&lt;/strong&gt; button at the bottom-left of the page.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You will be taken to the new group's page. Now, let's repeat this exact process for our &lt;code&gt;Articles&lt;/code&gt; group.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Click the &lt;strong&gt;plus icon (&lt;code&gt;+&lt;/code&gt;)&lt;/strong&gt; in the top-left sidebar again.&lt;/li&gt;
&lt;li&gt; Select &lt;strong&gt;"New group"&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; Click the &lt;strong&gt;"Create group"&lt;/strong&gt; tile.&lt;/li&gt;
&lt;li&gt; On the "Create group" form:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Group name:&lt;/strong&gt; Type &lt;code&gt;Articles&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Visibility level:&lt;/strong&gt; Select &lt;strong&gt;"Private"&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;"Create group"&lt;/strong&gt; button.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We now have our two top-level namespaces. This structure keeps our work organized and allows us to set permissions at the group level, which all projects inside will automatically inherit.&lt;/p&gt;

&lt;h2&gt;
  
  
  5.3. UI Guide: Fixing the Root Admin's Email
&lt;/h2&gt;

&lt;p&gt;When GitLab first starts, it assigns a "dummy" email address to the &lt;code&gt;root&lt;/code&gt; user (e.g., &lt;code&gt;gitlab_admin_0537fd@example.com&lt;/code&gt;). If you've just tested your SMTP settings as we did in 5.1.1, you may have seen a bounce-back email in your Gmail inbox, because this default email address is not real.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Example Bounce-back Error:&lt;/strong&gt;&lt;/p&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Address not found
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Your message wasn't delivered to gitlab_admin_&lt;a href="mailto:0537fd@example.com"&gt;0537fd@example.com&lt;/a&gt; because the domain example.com couldn't be found. Check for typos or unnecessary spaces and try again.&lt;br&gt;
...&lt;br&gt;
The domain example.com doesn't receive email...&lt;/p&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;

&lt;p&gt;We must fix this by assigning your &lt;em&gt;real&lt;/em&gt; email address to the &lt;code&gt;root&lt;/code&gt; account. This involves a critical "gotcha" where we must manually fix a confirmation link.&lt;/p&gt;

&lt;p&gt;Here is the full process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Add Your New Email:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Click your &lt;code&gt;root&lt;/code&gt; user avatar (top-left corner).&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;"Preferences"&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;In the left-hand navigation menu, click &lt;strong&gt;"Emails"&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;In the "Add email address" box, type your real email address (e.g., &lt;code&gt;your.name@gmail.com&lt;/code&gt;) and click the &lt;strong&gt;"Add email address"&lt;/strong&gt; button.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Find the Confirmation Email:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;GitLab will send a confirmation link to your inbox. Open the email (Subject: "Administrator, confirm your email address now!").&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Fix the Confirmation Link (The "Gotcha"):&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Do not click the link directly.&lt;/strong&gt; The link in the email will be broken. It will point to the &lt;em&gt;internal&lt;/em&gt; container address (e.g., &lt;code&gt;https://gitlab/...&lt;/code&gt;), which your host browser cannot resolve.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You must manually copy the link and replace the &lt;code&gt;https://gitlab.cicd.local:10300&lt;/code&gt; portion with our host-accessible address: &lt;code&gt;https://127.0.0.1:10300&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Broken Link (from email):&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;https://gitlab.cicd.local:10300/-/profile/emails/confirmation?confirmation_token=zFoxv_5xPMRiEjfgzR-E&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Corrected Link (to paste in browser):&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;https://127.0.0.1:10300/-/profile/emails/confirmation?confirmation_token=zFoxv_5xPMRiEjfgzR-E&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Paste the &lt;strong&gt;corrected link&lt;/strong&gt; into your browser and hit Enter. Your email address will now be confirmed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Change Your Primary Email:&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Now, you must make this new, confirmed email your primary address.&lt;/li&gt;
&lt;li&gt;Navigate back to your profile: &lt;strong&gt;Avatar &amp;gt; Preferences&lt;/strong&gt;. You will land on the &lt;strong&gt;"Profile"&lt;/strong&gt; page.&lt;/li&gt;
&lt;li&gt;Change the &lt;strong&gt;"Email"&lt;/strong&gt; field from the fake &lt;code&gt;example.com&lt;/code&gt; address to your new, confirmed email.&lt;/li&gt;
&lt;li&gt;Change the &lt;strong&gt;"Commit email"&lt;/strong&gt; field to your new, confirmed email as well.&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;"Update profile settings"&lt;/strong&gt; button at the bottom.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Clean Up the Old Email:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Finally, let's remove the old, fake email.&lt;/li&gt;
&lt;li&gt;Go back to the &lt;strong&gt;"Emails"&lt;/strong&gt; settings page (left-hand navigation).&lt;/li&gt;
&lt;li&gt;You will now see your new email listed as "Primary." Click the &lt;strong&gt;"Delete"&lt;/strong&gt; (trash can) icon next to the old &lt;code&gt;gitlab_admin_..._@example.com&lt;/code&gt; address to remove it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your &lt;code&gt;root&lt;/code&gt; account is now fully configured with a valid, working email address for all notifications.&lt;/p&gt;

&lt;h2&gt;
  
  
  5.4. UI Guide: Creating Access Tokens
&lt;/h2&gt;

&lt;p&gt;Our GitLab instance is now fully configured, but to automate it (our ultimate goal), we need to interact with its API. To do this securely, we will create &lt;strong&gt;Access Tokens&lt;/strong&gt;. These are the "keys" that our scripts and external services (like Jenkins) will use to authenticate with GitLab.&lt;/p&gt;

&lt;p&gt;We will create two different types of tokens to understand the options available. Note: You might want to do section 5.4.3 first.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Personal Access Token (PAT):&lt;/strong&gt; This token is tied directly to a &lt;em&gt;user account&lt;/em&gt; (in this case, our &lt;code&gt;root&lt;/code&gt; admin). It's a "master key" that grants permissions &lt;em&gt;as that user&lt;/em&gt;. It can do anything the &lt;code&gt;root&lt;/code&gt; user can do.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Group Access Token (GAT):&lt;/strong&gt; This is a more modern, secure "bot" token. It is tied to a &lt;em&gt;Group&lt;/em&gt; (e.g., our &lt;code&gt;CICD-Stack&lt;/code&gt; group) instead of a human user. This is the best practice for CI/CD, as the token's permissions are limited &lt;em&gt;only&lt;/em&gt; to that group, and it's not tied to a person who might leave the company.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For our verification scripts, we will create and use a &lt;strong&gt;Personal Access Token&lt;/strong&gt; for our &lt;code&gt;root&lt;/code&gt; user, as it's the most powerful and straightforward to use for our "master plan" of creating projects in multiple groups.&lt;/p&gt;

&lt;h3&gt;
  
  
  5.4.1. Creating a Personal Access Token (PAT)
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt; In the top-left corner of the sidebar, click your &lt;code&gt;root&lt;/code&gt; user avatar.&lt;/li&gt;
&lt;li&gt; From the dropdown menu, select &lt;strong&gt;"Preferences"&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; In the left-hand navigation menu of the Preferences page, click &lt;strong&gt;"Personal Access Tokens"&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; You will see the "Add a personal access token" form:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Token name:&lt;/strong&gt; Give it a descriptive name, like &lt;code&gt;cicd-admin-token&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expiration date:&lt;/strong&gt; For now, you can leave it blank (it will not expire). For a real production system, you would set a strict expiration date and rotate the key.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Select scopes:&lt;/strong&gt; Check the box for &lt;strong&gt;&lt;code&gt;api&lt;/code&gt;&lt;/strong&gt;. This is the master scope that grants the token full read/write access to the entire API, which we need to create groups and projects.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; Click the &lt;strong&gt;"Create personal access token"&lt;/strong&gt; button.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;GitLab will immediately display your new token (e.g., &lt;code&gt;glpat-xxxxxxxxxxxx&lt;/code&gt;). &lt;strong&gt;This is the only time you will ever see this token.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Copy this token immediately and save it in our secrets file, &lt;code&gt;~/cicd_stack/cicd.env&lt;/code&gt;. Add it as a new line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="py"&gt;GITLAB_API_TOKEN&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"glpat-xxxxxxxxxxxx"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our Python scripts in the next chapter will read this variable to authenticate.&lt;/p&gt;

&lt;h3&gt;
  
  
  5.4.2. Creating a Group Access Token (GAT)
&lt;/h3&gt;

&lt;p&gt;Now, let's create a Group Access Token for our &lt;code&gt;CICD-Stack&lt;/code&gt; group. This is the more modern, "best-practice" way to create a token for automation &lt;em&gt;within&lt;/em&gt; a specific group.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; In the top-left sidebar, click the &lt;strong&gt;"Groups"&lt;/strong&gt; icon and select &lt;strong&gt;"Your groups"&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; Click on the &lt;strong&gt;&lt;code&gt;CICD-Stack&lt;/code&gt;&lt;/strong&gt; group.&lt;/li&gt;
&lt;li&gt; In the group's left-hand sidebar, hover over &lt;strong&gt;"Settings"&lt;/strong&gt; and then click &lt;strong&gt;"Access Tokens"&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; Click the &lt;strong&gt;"Add new token"&lt;/strong&gt; button.&lt;/li&gt;
&lt;li&gt; Fill out the form:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Token name:&lt;/strong&gt; Give it a descriptive name, like &lt;code&gt;cicd-stack-bot-token&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Role:&lt;/strong&gt; Select &lt;strong&gt;"Owner"&lt;/strong&gt;. This is the highest permission &lt;em&gt;within the group&lt;/em&gt; and will allow this token to create new projects &lt;em&gt;inside&lt;/em&gt; the &lt;code&gt;CICD-Stack&lt;/code&gt; group.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Select scopes:&lt;/strong&gt; Check the &lt;strong&gt;&lt;code&gt;api&lt;/code&gt;&lt;/strong&gt; scope.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; Click the &lt;strong&gt;"Create group access token"&lt;/strong&gt; button.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Just like the PAT, this token will be displayed only once. You don't need to save it for our next steps (as we'll be using the PAT), but it's crucial to understand this is the more secure, preferred method for production CI/CD automation.&lt;/p&gt;

&lt;h3&gt;
  
  
  5.4.3. The "Split-Horizon" Fix: Resolving CORS Errors
&lt;/h3&gt;

&lt;p&gt;As you attempted to create an Access Token, you likely ran into a critical error. The page failed to load, and your browser's developer console showed a &lt;strong&gt;&lt;code&gt;Cross-Origin Request Blocked&lt;/code&gt; (CORS)&lt;/strong&gt; error.&lt;/p&gt;

&lt;p&gt;This is a classic "split-horizon" DNS problem, and it's the final major "gotcha" in our setup.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Problem
&lt;/h4&gt;

&lt;p&gt;The error occurred because our browser and our application had two different ideas of "home":&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;The Browser:&lt;/strong&gt; We were accessing the UI from &lt;code&gt;https://127.0.0.1:10300&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The Application:&lt;/strong&gt; GitLab's &lt;code&gt;external_url&lt;/code&gt; was set to &lt;code&gt;https://gitlab.cicd.local:10300&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When the UI (loaded from &lt;code&gt;127.0.0.1&lt;/code&gt;) tried to make an API call, its JavaScript (which was configured by &lt;code&gt;external_url&lt;/code&gt;) tried to contact &lt;code&gt;https://gitlab.cicd.local:10300&lt;/code&gt;. The browser saw that &lt;code&gt;127.0.0.1:10300&lt;/code&gt; and &lt;code&gt;gitlab.cicd.local&lt;/code&gt; were two different "origins" and blocked the request for security.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Solution
&lt;/h4&gt;

&lt;p&gt;To fix this, we must make the URL we type in the browser &lt;strong&gt;exactly match&lt;/strong&gt; the &lt;code&gt;external_url&lt;/code&gt; in the GitLab configuration. This requires a three-part fix.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1. Teach Your Host What &lt;code&gt;gitlab.cicd.local&lt;/code&gt; Means&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;First, we must edit the &lt;code&gt;hosts&lt;/code&gt; file on our &lt;strong&gt;host machine&lt;/strong&gt; (not the dev-container) to resolve &lt;code&gt;gitlab.cicd.local&lt;/code&gt; to our &lt;code&gt;localhost&lt;/code&gt; IP.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open your hosts file with &lt;code&gt;sudo&lt;/code&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  &lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/hosts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Add this line to the bottom of the file:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  127.0.0.1   gitlab.cicd.local
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Save and close the file. Your host machine now knows that &lt;code&gt;gitlab.cicd.local&lt;/code&gt; points to &lt;code&gt;127.0.0.1&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 2. Update &lt;code&gt;01-initial-setup.sh&lt;/code&gt; (The &lt;code&gt;external_url&lt;/code&gt;)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Next, we must tell GitLab to include our custom port in its &lt;code&gt;external_url&lt;/code&gt;. This tells the application to generate all its internal links with the correct port, solving the CORS mismatch.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open &lt;code&gt;01-initial-setup.sh&lt;/code&gt; and find this line in the &lt;code&gt;gitlab.rb&lt;/code&gt; section:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="c1"&gt;# --- Main GitLab URL ---&lt;/span&gt;
  &lt;span class="n"&gt;external_url&lt;/span&gt; &lt;span class="s1"&gt;'https://gitlab.cicd.local'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Change it to include our port:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="c1"&gt;# --- Main GitLab URL ---&lt;/span&gt;
  &lt;span class="c1"&gt;# This sets the public URL AND configures the internal NGINX&lt;/span&gt;
  &lt;span class="c1"&gt;# to listen on this port (e.g., 10300).&lt;/span&gt;
  &lt;span class="n"&gt;external_url&lt;/span&gt; &lt;span class="s1"&gt;'https://gitlab.cicd.local:10300'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The "Gotcha":&lt;/strong&gt; We discovered that changing this &lt;code&gt;external_url&lt;/code&gt; &lt;em&gt;also&lt;/em&gt; changes the internal port that GitLab's NGINX service listens on. It will no longer listen on &lt;code&gt;443&lt;/code&gt;; it will now listen on &lt;code&gt;10300&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 3. Update &lt;code&gt;02-create-docker.sh&lt;/code&gt; (The Port Mapping)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Finally, because the container's internal port is now &lt;code&gt;10300&lt;/code&gt;, we must update our &lt;code&gt;docker run&lt;/code&gt; command to match.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open &lt;code&gt;02-create-docker.sh&lt;/code&gt; and find this line:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  &lt;span class="nt"&gt;--publish&lt;/span&gt; 127.0.0.1:10300:443 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Change it to map &lt;code&gt;10300&lt;/code&gt; on the host to &lt;code&gt;10300&lt;/code&gt; in the container:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  &lt;span class="nt"&gt;--publish&lt;/span&gt; 127.0.0.1:10300:10300 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 4. Relaunch and Access by Name&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To apply these changes, you &lt;strong&gt;do not need to delete your data&lt;/strong&gt;. Simply re-run the scripts.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Stop and remove the old container.&lt;/strong&gt; This is required to apply the new &lt;code&gt;--publish&lt;/code&gt; flag.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker stop gitlab &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; docker &lt;span class="nb"&gt;rm &lt;/span&gt;gitlab
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Re-run the setup script.&lt;/strong&gt; This will update the &lt;code&gt;gitlab.rb&lt;/code&gt; file on your host.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./01-initial-setup.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Re-run the deployment script.&lt;/strong&gt; This will create a new container with the correct port mapping, which will then read your updated &lt;code&gt;gitlab.rb&lt;/code&gt; file and reconfigure itself.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./02-create-docker.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After waiting a few minutes for the container to start, clear your browser cache (or use a private window). From now on, to access the UI, you &lt;strong&gt;must&lt;/strong&gt; use the new, correct URL in your browser:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://gitlab.cicd.local:10300" rel="noopener noreferrer"&gt;https://gitlab.cicd.local:10300&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When you do, the "origin" will match (&lt;code&gt;https, gitlab, 10300&lt;/code&gt;), the CORS errors will be gone, and all UI features will now work perfectly. You can now proceed with creating your Access Tokens.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;NOTE&lt;br&gt;
The scripts will be fixed by the time you read this, so all you really need to do is edit your hosts file.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  5.5. The "Workflow" Pain Point (Enforcing MRs)
&lt;/h2&gt;

&lt;p&gt;Our GitLab instance is now fully configured, but it has a major &lt;strong&gt;workflow problem&lt;/strong&gt;. By default, any developer with access to a project can commit and push &lt;em&gt;directly&lt;/em&gt; to the &lt;code&gt;main&lt;/code&gt; branch.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The Analogy:&lt;/strong&gt; This is like letting any engineer walk into the "Central Library" and scribble directly on the "master blueprint" &lt;em&gt;without&lt;/em&gt; a review.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This "pain point" defeats the entire purpose of our CI/CD pipeline. We &lt;em&gt;want&lt;/em&gt; every change to be a "proposal" (a Merge Request) that can be reviewed, tested, and scanned &lt;em&gt;before&lt;/em&gt; it's merged into our "single source of truth."&lt;/p&gt;

&lt;p&gt;The solution is to apply a &lt;strong&gt;Branch Rule&lt;/strong&gt;. This feature allows us to lock the &lt;code&gt;main&lt;/code&gt; branch and enforce our Merge Request workflow.&lt;/p&gt;

&lt;p&gt;Based on the documentation, there is a key distinction between the Free and Premium tiers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitLab Free (Our Version):&lt;/strong&gt; We can control &lt;em&gt;who&lt;/em&gt; is allowed to push or merge to a branch. This is the core functionality we need.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitLab Premium/Ultimate:&lt;/strong&gt; This adds &lt;em&gt;required approvals&lt;/em&gt; (e.g., "requires 2 approvals") and status checks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We will now use the Free tier's "Branch rule" capability to set our core push/merge permissions.&lt;/p&gt;




&lt;h2&gt;
  
  
  5.6. Action (UI): Configuring a Branch Rule
&lt;/h2&gt;

&lt;p&gt;We will now enforce our workflow. We'll create a "hello-world" project and apply the "Branch rule," but this time, we'll ensure the &lt;code&gt;main&lt;/code&gt; branch exists &lt;em&gt;first&lt;/em&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; First, let's create the project. Navigate to the &lt;strong&gt;&lt;code&gt;CICD-Stack&lt;/code&gt;&lt;/strong&gt; Group you created.&lt;/li&gt;
&lt;li&gt; Click the &lt;strong&gt;"New project"&lt;/strong&gt; button (top-right).&lt;/li&gt;
&lt;li&gt; Select &lt;strong&gt;"Create blank project"&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Project name:&lt;/strong&gt; &lt;code&gt;hello-world&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Visibility Level:&lt;/strong&gt; &lt;code&gt;Private&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;✅ Check the box for "Initialize repository with a README."&lt;/strong&gt;
This is the crucial step. It creates the project &lt;em&gt;with&lt;/em&gt; a &lt;code&gt;main&lt;/code&gt; branch and a single, initial commit.&lt;/li&gt;
&lt;li&gt; Click &lt;strong&gt;"Create project"&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You will be taken to the new project's main page, and you will see the &lt;code&gt;README.md&lt;/code&gt; file is already there. The &lt;code&gt;main&lt;/code&gt; branch now officially exists.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Now&lt;/em&gt; we can protect it.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; In the project's left-hand navigation sidebar, go to &lt;strong&gt;Settings &amp;gt; Repository&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; Find the &lt;strong&gt;"Branch rules"&lt;/strong&gt; section and click the &lt;strong&gt;"Expand"&lt;/strong&gt; button.&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;"Add branch rule"&lt;/strong&gt; button.&lt;/li&gt;
&lt;li&gt;You will be prompted to choose a target. Select &lt;strong&gt;"Branch name or pattern"&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;From the dropdown, select &lt;code&gt;main&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;"Create branch rule"&lt;/strong&gt; button.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This will create the rule and take you to the &lt;strong&gt;Branch rule details&lt;/strong&gt; page.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;On this details page, find the &lt;strong&gt;"Protected branch"&lt;/strong&gt; section.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Allowed to merge:&lt;/strong&gt; In the dropdown, select &lt;strong&gt;"Maintainers"&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Allowed to push and merge:&lt;/strong&gt; In the dropdown, select &lt;strong&gt;"No one"&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can now save these changes. The &lt;code&gt;main&lt;/code&gt; branch is now "locked."&lt;/p&gt;

&lt;p&gt;From this point forward, no one can push directly to &lt;code&gt;main&lt;/code&gt;. The only way to update it is to push a new feature branch and open a Merge Request, which is exactly the professional workflow we want.&lt;/p&gt;

&lt;h2&gt;
  
  
  6.1. Verification (Part 1): The "API-First" Test (Project Creation)
&lt;/h2&gt;

&lt;p&gt;Our first test will be to use the &lt;strong&gt;Personal Access Token (PAT)&lt;/strong&gt; we created in Chapter 5. This test will prove that our API is accessible, our token is working, and our Python environment on our &lt;strong&gt;host machine&lt;/strong&gt; can successfully talk to our container.&lt;/p&gt;

&lt;p&gt;This test is the &lt;strong&gt;ultimate payoff for Article 2&lt;/strong&gt;. We will not be manually loading any CA files. We will use Python's standard &lt;code&gt;ssl.create_default_context()&lt;/code&gt;, which now automatically uses our host's system trust store (where our Local CA was installed).&lt;/p&gt;

&lt;p&gt;Create the following Python script on your host machine. You can save it as &lt;code&gt;verify_gitlab.py&lt;/code&gt; inside your &lt;code&gt;~/cicd_stack&lt;/code&gt; directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ssl&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;urllib.request&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;

&lt;span class="c1"&gt;# --- Configuration ---
&lt;/span&gt;&lt;span class="n"&gt;ENV_FILE_PATH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;home&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cicd_stack&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cicd.env&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;GITLAB_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://gitlab.cicd.local:10300&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="c1"&gt;# Our host-accessible URL
&lt;/span&gt;&lt;span class="n"&gt;GROUP_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Articles&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="c1"&gt;# The group we created in 5.2
&lt;/span&gt;
&lt;span class="c1"&gt;# --- Standard Library .env parser ---
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;load_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env_path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Reads a .env file and loads its variables into os.environ.
    No third-party packages needed.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Loading environment from: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;env_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;env_path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;⛔ ERROR: Environment file not found at &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;env_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;

    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'"&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# Remove quotes
&lt;/span&gt;                &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;

&lt;span class="c1"&gt;# --- Find the Group ID ---
# We must find the numeric ID for our "Articles" group
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_group_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;group_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Searching for Group ID for: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;group_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PRIVATE-TOKEN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;GITLAB_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/api/v4/groups?search=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;group_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;groups&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;groups&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error: Group &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;group_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; not found.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

            &lt;span class="c1"&gt;# Find the exact match
&lt;/span&gt;            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;groups&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;group_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Found Group ID: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error: No exact match for &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;group_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; found.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;An error occurred: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

&lt;span class="c1"&gt;# --- Create the Project ---
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_project&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;group_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;project_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Creating project &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;project_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; in Group ID &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;group_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PRIVATE-TOKEN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Content-Type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;GITLAB_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/api/v4/projects&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="c1"&gt;# We will create the project from our 0004 article
&lt;/span&gt;    &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;project_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;namespace_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;group_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;visibility&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;private&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;initialize_with_readme&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
    &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;project&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;✅ Success! Project created.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  ID: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  URL: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;web_url&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;An error occurred creating project: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;hasattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;read&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  Response: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# --- Main ---
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;load_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ENV_FILE_PATH&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;GITLAB_TOKEN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;GITLAB_API_TOKEN&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;GITLAB_TOKEN&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error: GITLAB_API_TOKEN not found in cicd.env&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# This is the payoff from Article 2!
&lt;/span&gt;    &lt;span class="c1"&gt;# We just create the default context. Python will
&lt;/span&gt;    &lt;span class="c1"&gt;# automatically use the system trust store, which
&lt;/span&gt;    &lt;span class="c1"&gt;# now contains our Local CA.
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Creating default SSL context...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_default_context&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;group_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_group_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GROUP_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GITLAB_TOKEN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;group_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;create_project&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;group_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0004_std_lib_http_client&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GITLAB_TOKEN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Running the Verification
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Run the Script (No Dependencies Required):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 verify_gitlab.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The Payoff:&lt;/strong&gt;&lt;br&gt;
You should see the following output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Loading environment from: /home/your_user/cicd_stack/cicd.env
Creating default SSL context...
Searching for Group ID for: Articles...
Found Group ID: 2
Creating project '0004_std_lib_http_client' in Group ID 2...
✅ Success! Project created.
  ID: 3
  URL: https://gitlab.cicd.local:10300/Articles/0004_std_lib_http_client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This simple, dependency-free script verifies a massive amount of our architecture:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Our &lt;code&gt;/etc/hosts&lt;/code&gt; file is working (it found &lt;code&gt;gitlab&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt; Our Docker port mapping is correct (it reached &lt;code&gt;10300&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt; Our &lt;strong&gt;host's system trust store is working perfectly&lt;/strong&gt; (the SSL connection succeeded with &lt;code&gt;ssl.create_default_context()&lt;/code&gt; and no other arguments).&lt;/li&gt;
&lt;li&gt; Our &lt;code&gt;gitlab.rb&lt;/code&gt; SSL config is working.&lt;/li&gt;
&lt;li&gt; Our Personal Access Token is valid and has &lt;code&gt;api&lt;/code&gt; scope.&lt;/li&gt;
&lt;li&gt; Our &lt;code&gt;Articles&lt;/code&gt; group is set up and accessible.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Go to the GitLab UI (&lt;code&gt;https://gitlab.cicd.local:10300&lt;/code&gt;). You will now see your new &lt;strong&gt;&lt;code&gt;0004_std_lib_http_client&lt;/code&gt;&lt;/strong&gt; project, complete without a &lt;code&gt;README.md&lt;/code&gt;, inside the &lt;code&gt;Articles&lt;/code&gt; group.&lt;/p&gt;

&lt;h2&gt;
  
  
  6.2. Verification (Part 2): The "Internal Push" &amp;amp; Webhook Test
&lt;/h2&gt;

&lt;p&gt;This is the full-stack test of our &lt;code&gt;cicd-net&lt;/code&gt; architecture. We will simulate our entire CI/CD pipeline. Our &lt;code&gt;dev-container&lt;/code&gt; will act as a developer pushing a change &lt;em&gt;and&lt;/em&gt; as the "Jenkins" server receiving the webhook.&lt;/p&gt;

&lt;p&gt;This test will prove:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Internal DNS:&lt;/strong&gt; The dev-container can resolve &lt;code&gt;gitlab&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitLab's CA Trust:&lt;/strong&gt; GitLab (as a client) trusts our internal services.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Webhook Mechanism:&lt;/strong&gt; GitLab correctly fires a webhook on a push.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Action 1 (Admin UI): The "SSRF" Gotcha - Allowing Local Webhooks
&lt;/h3&gt;

&lt;p&gt;Before we can set up the webhook, we must first get past the &lt;code&gt;Invalid url given&lt;/code&gt; error. We need to tell GitLab that it's allowed to send requests to our internal &lt;code&gt;dev-container&lt;/code&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; As your &lt;code&gt;root&lt;/code&gt; user, go to the &lt;strong&gt;Main menu&lt;/strong&gt; (top-left waffle icon) and click &lt;strong&gt;"Admin"&lt;/strong&gt; (the wrench icon).&lt;/li&gt;
&lt;li&gt; In the Admin Area's left-hand sidebar, navigate to &lt;strong&gt;Settings &amp;gt; Network&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; Find the &lt;strong&gt;"Outbound requests"&lt;/strong&gt; section and click the &lt;strong&gt;"Expand"&lt;/strong&gt; button.&lt;/li&gt;
&lt;li&gt; Find the checkbox labeled &lt;strong&gt;"Allow requests to the local network from web hooks and services"&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Check&lt;/strong&gt; this box.&lt;/li&gt;
&lt;li&gt; (Optional but Recommended) For a more secure setup, you can leave the box &lt;em&gt;unchecked&lt;/em&gt; and instead add &lt;code&gt;dev-container&lt;/code&gt; (and later, &lt;code&gt;jenkins&lt;/code&gt;) to the allowlist in the "Local IP addresses and domain names that hooks and integrations can access" text box. For our purposes, checking the main "Allow" box is the simplest solution.&lt;/li&gt;
&lt;li&gt; Click the &lt;strong&gt;"Save changes"&lt;/strong&gt; button at the bottom of the section.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now that we've told GitLab to trust our local network, the &lt;code&gt;Invalid url given&lt;/code&gt; error will be gone.&lt;/p&gt;

&lt;h3&gt;
  
  
  Action 2 (UI): Set Up the Webhook
&lt;/h3&gt;

&lt;p&gt;Now, let's try this step again.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; In the GitLab UI, navigate to your &lt;strong&gt;&lt;code&gt;hello-world&lt;/code&gt;&lt;/strong&gt; project (inside the &lt;code&gt;CICD-Stack&lt;/code&gt; group).&lt;/li&gt;
&lt;li&gt; In the project's left-hand sidebar, go to &lt;strong&gt;Settings &amp;gt; Webhooks&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; Fill out the webhook form:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;URL:&lt;/strong&gt; &lt;code&gt;https://dev-container:10400&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secret token:&lt;/strong&gt; Create a simple secret, for example, &lt;code&gt;my-super-secret-token&lt;/code&gt;. We will use this to verify the request.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trigger:&lt;/strong&gt; Uncheck everything &lt;em&gt;except&lt;/em&gt; &lt;strong&gt;"Push events"&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSL verification:&lt;/strong&gt; Make sure &lt;strong&gt;"Enable SSL verification"&lt;/strong&gt; is &lt;strong&gt;CHECKED&lt;/strong&gt;. This is the whole point of the test.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; Click &lt;strong&gt;"Add webhook"&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This time, it will work. GitLab will send a test event, which will still fail (with a "Hook execution failed" error) because our &lt;code&gt;webhook_receiver.py&lt;/code&gt; isn't running yet. This is expected.&lt;/p&gt;

&lt;h3&gt;
  
  
  Action 3 (Host): Generate and Stage the &lt;code&gt;dev-container&lt;/code&gt; Certificate
&lt;/h3&gt;

&lt;p&gt;Before we can run our server, we must give it a valid "passport." We'll use our Article 2 CA scripts from our &lt;strong&gt;host machine&lt;/strong&gt; to generate a new certificate for the hostname &lt;code&gt;dev-container&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then, we'll copy those new certs into the &lt;code&gt;~/Documents/FromFirstPrinciples/data&lt;/code&gt; directory, which is mounted inside our dev container as &lt;code&gt;~/data&lt;/code&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Open a terminal on your &lt;strong&gt;host machine&lt;/strong&gt; (not the dev-container).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Navigate to your Article 2 script directory:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# (On your HOST machine)&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/Documents/FromFirstPrinciples/articles/0006_cicd_part02_certificate_authority
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the &lt;code&gt;02-issue-service-cert.sh&lt;/code&gt; script from here, passing &lt;code&gt;dev-container&lt;/code&gt; as the name. This script is smart enough to operate on the &lt;code&gt;~/cicd_stack/ca&lt;/code&gt; directory.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./02-issue-service-cert.sh dev-container
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;This will create the new certs in &lt;code&gt;~/cicd_stack/ca/pki/services/dev-container/&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Copy the new certs&lt;/strong&gt; to the dev container's shared data volume (which, as you noted, already exists):&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# (On your HOST machine)&lt;/span&gt;
&lt;span class="nv"&gt;DATA_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;~/Documents/FromFirstPrinciples/data
&lt;span class="nv"&gt;CERT_SOURCE_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;~/cicd_stack/ca/pki/services/dev-container

&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nv"&gt;$CERT_SOURCE_DIR&lt;/span&gt;/dev-container.crt.pem &lt;span class="nv"&gt;$DATA_DIR&lt;/span&gt;/
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nv"&gt;$CERT_SOURCE_DIR&lt;/span&gt;/dev-container.key.pem &lt;span class="nv"&gt;$DATA_DIR&lt;/span&gt;/
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Action 4 (Dev Container): The "Jenkins" Simulator
&lt;/h3&gt;

&lt;p&gt;Now, let's go back to your &lt;strong&gt;dev container&lt;/strong&gt;. We will create the simple Python server that will act as our "Jenkins" instance, placing it in the correct article directory. It will listen on port &lt;code&gt;10400&lt;/code&gt; and use the &lt;code&gt;dev-container&lt;/code&gt; certificate we just staged in the &lt;code&gt;~/data&lt;/code&gt; directory.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Create the server script:&lt;/strong&gt;&lt;br&gt;
Navigate to your article directory and create the new Python file.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# (Inside the dev container)&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/articles/0007_cicd_part03_gitlab
nano webhook_receiver.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Paste in the following code.&lt;/strong&gt; Note the updated certificate paths pointing to &lt;code&gt;~/data&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;http.server&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ssl&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="c1"&gt;# --- CONFIGURATION ---
&lt;/span&gt;&lt;span class="n"&gt;LISTEN_HOST&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;0.0.0.0&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="n"&gt;LISTEN_PORT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10400&lt;/span&gt;
&lt;span class="n"&gt;SECRET_TOKEN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;my-super-secret-token&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="c1"&gt;# Must match your GitLab webhook
&lt;/span&gt;
&lt;span class="c1"&gt;# Paths to our *new* dev-container certs in the ~/data mount
&lt;/span&gt;&lt;span class="n"&gt;CERT_DIR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expanduser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;~/data&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;CERT_FILE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CERT_DIR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dev-container.crt.pem&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;KEY_FILE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CERT_DIR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dev-container.key.pem&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WebhookHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BaseHTTPRequestHandler&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;do_POST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# 1. Verify the Secret Token
&lt;/span&gt;        &lt;span class="n"&gt;gitlab_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;X-Gitlab-Token&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;gitlab_token&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;SECRET_TOKEN&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;⛔ ERROR: Invalid X-Gitlab-Token.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end_headers&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;

        &lt;span class="c1"&gt;# 2. Read the JSON payload
&lt;/span&gt;        &lt;span class="n"&gt;content_length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Content-Length&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rfile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content_length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# 3. Print the relevant info
&lt;/span&gt;        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- ✅ WEBHOOK RECEIVED! ---&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Project: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;project&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;path_with_namespace&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Pusher:  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;user_name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Ref:     &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ref&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="c1"&gt;# Print all commits in this push
&lt;/span&gt;            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;commit&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;commits&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]):&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  - Commit: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;    Author: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;author&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;    Msg:    &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error parsing JSON: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# 4. Send a 200 OK response
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end_headers&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wfile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Webhook Received&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Starting HTTPS server on https://&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;LISTEN_HOST&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;LISTEN_PORT&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Create an SSL context using our "dev-container" certs
&lt;/span&gt;    &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_default_context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Purpose&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CLIENT_AUTH&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load_cert_chain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;certfile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;CERT_FILE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;keyfile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;KEY_FILE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;httpd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;HTTPServer&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;LISTEN_HOST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LISTEN_PORT&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;WebhookHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;httpd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wrap_socket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;httpd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;server_side&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;httpd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;serve_forever&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;KeyboardInterrupt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Shutting down server...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;httpd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;server_close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Run the server:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# (Inside the dev-container, from the 0007... directory)&lt;/span&gt;
python3 webhook_receiver.py
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;You should see: &lt;code&gt;Starting HTTPS server on https://0.0.0.0:10400...&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Our "Jenkins" simulator is now running with a valid, matching certificate, waiting for a signal.&lt;/p&gt;

&lt;h3&gt;
  
  
  Action 5 (Dev Container): The Git Push
&lt;/h3&gt;

&lt;p&gt;Finally, let's act as a developer. We'll open a &lt;strong&gt;second &lt;code&gt;dev-container&lt;/code&gt; terminal&lt;/strong&gt; (leave your &lt;code&gt;webhook_receiver.py&lt;/code&gt; server running in the first one).&lt;/p&gt;

&lt;p&gt;We will clone the &lt;code&gt;hello-world&lt;/code&gt; project, attempt to push to &lt;code&gt;main&lt;/code&gt; (which will fail), and then successfully push a feature branch (which will trigger our webhook).&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Configure Git
&lt;/h3&gt;

&lt;p&gt;Before your first push, you must tell Git who you are.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note on Automation:&lt;/strong&gt; Our dev container is set up to automatically source a Git configuration from &lt;code&gt;~/data/.gitconfig&lt;/code&gt; at startup. For a fully automated setup, you could place your &lt;code&gt;.gitconfig&lt;/code&gt; file in the &lt;code&gt;~/Documents/FromFirstPrinciples/data&lt;/code&gt; directory on your host, and your container would pick it up on every boot.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For now, we'll run the manual, one-time commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# (Inside the dev-container)&lt;/span&gt;
git config &lt;span class="nt"&gt;--global&lt;/span&gt; user.name &lt;span class="s2"&gt;"Your Name"&lt;/span&gt;
git config &lt;span class="nt"&gt;--global&lt;/span&gt; user.email &lt;span class="s2"&gt;"your.email@gmail.com"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Use the email address you registered with GitLab in section 5.3).&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Clone the Repository
&lt;/h3&gt;

&lt;p&gt;Navigate to your &lt;code&gt;repos&lt;/code&gt; directory, where we'll clone the project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# (Inside the dev-container)&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/repos
git clone https://gitlab.cicd.local:10300/CICD-Stack/hello-world.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will now be prompted for credentials. This is the crucial step for Git-over-HTTPS:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Username:&lt;/strong&gt; &lt;code&gt;root&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Password:&lt;/strong&gt; &lt;strong&gt;Do NOT use your root password.&lt;/strong&gt; Instead, copy and paste the &lt;strong&gt;Personal Access Token (PAT)&lt;/strong&gt; (&lt;code&gt;glpat-...&lt;/code&gt;) that you saved in your &lt;code&gt;cicd.env&lt;/code&gt; file. This is the standard, secure way to authenticate Git clients.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After you authenticate, Git will clone the repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cloning into 'hello-world'...
Username for 'https://gitlab.cicd.local:10300': root
Password for 'https://root@gitlab.cicd.local:10300': 
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
Receiving objects: 100% (3/3), done.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, &lt;code&gt;cd&lt;/code&gt; into the new project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;hello-world
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Create Our First Commit (The Failed Push)
&lt;/h3&gt;

&lt;p&gt;Recall that we &lt;em&gt;protected&lt;/em&gt; the &lt;code&gt;main&lt;/code&gt; branch. This means our push to &lt;code&gt;main&lt;/code&gt; &lt;strong&gt;must fail&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# (Inside the dev-container)&lt;/span&gt;
&lt;span class="c"&gt;# We will just edit the README that was created when we made the project.&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Hello World!"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; README.md
git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Test commit to main"&lt;/span&gt;

&lt;span class="c"&gt;# This push will be REJECTED by our branch rule&lt;/span&gt;
git push &lt;span class="nt"&gt;-u&lt;/span&gt; origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Payoff (Part 1):&lt;/strong&gt; You will see this error, proving our rule is working:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;remote: GitLab: You are not allowed to push code to protected branches on this project.
! [remote rejected] main -&amp;gt; main (pre-receive hook declined)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Push Correctly (The Successful Push)
&lt;/h3&gt;

&lt;p&gt;Now, let's do it the &lt;em&gt;right&lt;/em&gt; way by creating a feature branch.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# (Inside the dev-container)&lt;/span&gt;
git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; feature/initial-commit

&lt;span class="c"&gt;# We already have the commit, so we just push the new branch&lt;/span&gt;
git push &lt;span class="nt"&gt;-u&lt;/span&gt; origin feature/initial-commit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This push will &lt;strong&gt;succeed&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Payoff (Part 2)
&lt;/h3&gt;

&lt;p&gt;Now, look at your &lt;em&gt;first&lt;/em&gt; dev-container terminal (the one running &lt;code&gt;webhook_receiver.py&lt;/code&gt;). You will see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;--- ✅ WEBHOOK RECEIVED! ---
Project: CICD-Stack/hello-world
Pusher:  Your Name
Ref:     refs/heads/feature/initial-commit
  - Commit: &amp;lt;some_hash&amp;gt;
    Author: Your Name
    Msg:    Test commit to main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This single test confirms our entire internal architecture is working perfectly:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Internal DNS:&lt;/strong&gt; &lt;code&gt;git clone https://gitlab.cicd.local:10300&lt;/code&gt; worked.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Authentication:&lt;/strong&gt; Our Personal Access Token worked for Git authentication.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Branch Rules:&lt;/strong&gt; Our push to &lt;code&gt;main&lt;/code&gt; was correctly &lt;strong&gt;rejected&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Webhook Trigger:&lt;/strong&gt; Our push to &lt;code&gt;feature/initial-commit&lt;/code&gt; was successful and &lt;strong&gt;fired the webhook&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;GitLab CA Trust:&lt;/strong&gt; GitLab (as a client) saw our &lt;code&gt;https://dev-container:10400&lt;/code&gt; URL, validated its certificate (which has the correct hostname &lt;code&gt;dev-container&lt;/code&gt;) against the &lt;code&gt;ca.pem&lt;/code&gt; we mounted in &lt;code&gt;trusted-certs&lt;/code&gt;, and successfully made the HTTPS request.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We have now verified our setup from end to end.&lt;/p&gt;

&lt;h2&gt;
  
  
  6.3. Verification (Part 3): The "External Push" (Practical Application)
&lt;/h2&gt;

&lt;p&gt;We have successfully verified our &lt;em&gt;internal&lt;/em&gt; network (dev-container to GitLab). This final test is the payoff for our &lt;strong&gt;host machine&lt;/strong&gt; setup from Article 2. We will prove that our &lt;em&gt;external&lt;/em&gt; (host-to-container) workflow is working just as smoothly.&lt;/p&gt;

&lt;p&gt;We will &lt;code&gt;cd&lt;/code&gt; into our &lt;em&gt;existing&lt;/em&gt; &lt;code&gt;0004_std_lib_http_client&lt;/code&gt; repository on our host machine, add our new GitLab instance as a "remote," and push our code.&lt;/p&gt;

&lt;p&gt;This push will &lt;strong&gt;only succeed&lt;/strong&gt; because:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Our host's &lt;code&gt;/etc/hosts&lt;/code&gt; file can resolve &lt;code&gt;gitlab.cicd.local&lt;/code&gt; to &lt;code&gt;127.0.0.1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; Our &lt;code&gt;git&lt;/code&gt; CLI (like our Python script) uses the &lt;strong&gt;system trust store&lt;/strong&gt;, which we fixed in Article 2 to trust our Local CA.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Action 1 (Host): Add the New Remote
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;On your &lt;strong&gt;host machine's terminal&lt;/strong&gt;, navigate to the existing project directory from our previous article:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# (On your HOST machine)&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/Documents/FromFirstPrinciples/articles/0004_std_lib_http_client
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add our new GitLab instance as a remote. We'll call it &lt;code&gt;gitlab&lt;/code&gt;. We use the &lt;code&gt;https://gitlab.cicd.local:10300&lt;/code&gt; URL that our host can now resolve.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git remote add gitlab https://gitlab.cicd.local:10300/Articles/0004_std_lib_http_client.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;(Optional) Verify the new remote was added:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git remote &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;You should see &lt;code&gt;gitlab&lt;/code&gt; listed along with &lt;code&gt;origin&lt;/code&gt; (which likely points to GitHub).&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Action 2 (Host): Push to GitLab
&lt;/h3&gt;

&lt;p&gt;Now, let's push our &lt;code&gt;main&lt;/code&gt; branch to the new &lt;code&gt;gitlab&lt;/code&gt; remote.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# (On your HOST machine)&lt;/span&gt;
git push &lt;span class="nt"&gt;-u&lt;/span&gt; gitlab main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just as you did in the dev container, you will be prompted for credentials:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Username:&lt;/strong&gt; &lt;code&gt;root&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Password:&lt;/strong&gt; (Paste your &lt;strong&gt;Personal Access Token&lt;/strong&gt;, &lt;code&gt;glpat-...&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The Payoff:&lt;/strong&gt;&lt;br&gt;
The push will succeed without any SSL errors. &lt;code&gt;git&lt;/code&gt; will not complain about an "untrusted certificate" because it's using your host's trust store.&lt;/p&gt;

&lt;p&gt;You have now:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Created a project (&lt;code&gt;0004_std_lib_http_client&lt;/code&gt;) via the API.&lt;/li&gt;
&lt;li&gt; Pushed your existing local code to it from your host machine.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Go to the GitLab UI and look at your &lt;code&gt;0004_std_lib_http_client&lt;/code&gt; project. It will no longer be empty. It will now contain all the code from your local repository, and its &lt;code&gt;main&lt;/code&gt; branch is now populated.&lt;/p&gt;

&lt;h1&gt;
  
  
  Chapter 7: Conclusion
&lt;/h1&gt;

&lt;h2&gt;
  
  
  7.1. What We've Built: The "Central Library" is Open
&lt;/h2&gt;

&lt;p&gt;Let's take a moment to appreciate what we have just accomplished. We have successfully deployed a fully-featured, secure, and production-ready &lt;strong&gt;GitLab&lt;/strong&gt; instance entirely from first principles.&lt;/p&gt;

&lt;p&gt;This isn't just a container; it's the &lt;strong&gt;"Central Library"&lt;/strong&gt; of our new CI/CD city, and it is built on a rock-solid, architecturally-sound foundation.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It lives &lt;strong&gt;inside our private &lt;code&gt;cicd-net&lt;/code&gt;&lt;/strong&gt;, able to communicate with our other services using internal DNS.&lt;/li&gt;
&lt;li&gt;It is &lt;strong&gt;fully secured with SSL&lt;/strong&gt;, serving traffic over HTTPS using a certificate from our own Local Certificate Authority.&lt;/li&gt;
&lt;li&gt;It &lt;strong&gt;trusts our Local CA&lt;/strong&gt;, allowing it to send secure webhooks to &lt;em&gt;other&lt;/em&gt; internal services like our &lt;code&gt;dev-container&lt;/code&gt; (and later, Jenkins).&lt;/li&gt;
&lt;li&gt;It is configured for &lt;strong&gt;professional team workflow&lt;/strong&gt; using "Branch Rules" to protect &lt;code&gt;main&lt;/code&gt; and enforce Merge Requests.&lt;/li&gt;
&lt;li&gt;It is &lt;strong&gt;fully automated&lt;/strong&gt;, with a "baked-in" &lt;code&gt;gitlab.rb&lt;/code&gt; file that handles everything from the root password to our SMTP settings, solving the complex "first-run" conflict.&lt;/li&gt;
&lt;li&gt;It is &lt;strong&gt;fully verifiable&lt;/strong&gt;, proven to work from the API (Python script), the internal network (dev-container Git push), and the external host (host Git push).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We have solved every "pain point" we set out to address: network isolation, data sovereignty, and the "paid feature" trap. We have built our library &lt;em&gt;inside&lt;/em&gt; our city walls.&lt;/p&gt;

&lt;h2&gt;
  
  
  7.2. The Next "Pain Point"
&lt;/h2&gt;

&lt;p&gt;We have successfully built our "Central Library" (GitLab) and proven that our code (like &lt;code&gt;hello-world&lt;/code&gt; and &lt;code&gt;0004_http_client&lt;/code&gt;) is now safely stored inside, "on the shelf."&lt;/p&gt;

&lt;p&gt;This introduces our next, obvious "pain point."&lt;/p&gt;

&lt;p&gt;Our code is just &lt;strong&gt;sitting there&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Our &lt;code&gt;dev-container&lt;/code&gt; test was a simulation, but it revealed the truth: a webhook is firing, but it's just hitting a test server. We have no "factory" to &lt;em&gt;act&lt;/em&gt; on this signal. We have no automated process to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Compile our code.&lt;/li&gt;
&lt;li&gt;Run our unit tests.&lt;/li&gt;
&lt;li&gt;Scan our code for quality or security vulnerabilities.&lt;/li&gt;
&lt;li&gt;Build our code into a Docker image.&lt;/li&gt;
&lt;li&gt;Publish our finished "product."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Our "Central Library" is open, but the "Factory" next door is an empty lot.&lt;/p&gt;




&lt;h2&gt;
  
  
  7.3. Next Steps
&lt;/h2&gt;

&lt;p&gt;We must now build that factory. Our webhook signal needs a real target, an "Automation Foreman" that can receive the "new code" signal from GitLab and start a complex assembly line.&lt;/p&gt;

&lt;p&gt;In the next article, we will do exactly that. We will build the second "skyscraper" in our CI/CD city: &lt;strong&gt;Jenkins&lt;/strong&gt;. We will deploy the Jenkins controller, connect it to our &lt;code&gt;cicd-net&lt;/code&gt;, and configure it to receive the webhooks from GitLab, officially kicking off our automated pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Addendum: Best Practices - Creating a Daily User Account
&lt;/h2&gt;

&lt;p&gt;We have successfully set up and verified our entire GitLab instance using the &lt;code&gt;root&lt;/code&gt; administrator account. While this was necessary for the initial setup, using the &lt;code&gt;root&lt;/code&gt; account for daily work or CI/CD integrations is a &lt;strong&gt;significant security risk&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why You Shouldn't Use &lt;code&gt;root&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;root&lt;/code&gt; user is an "all-powerful" administrator. It can instantly delete all projects, change critical instance settings, or remove other users. If this account's credentials (or its Personal Access Token) were ever leaked, your entire "Central Library" would be compromised.&lt;/p&gt;

&lt;p&gt;We will now follow the &lt;strong&gt;Principle of Least Privilege&lt;/strong&gt; by creating a regular user account for our daily work.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Create Your Unprivileged User
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt; As the &lt;code&gt;root&lt;/code&gt; user, go to the &lt;strong&gt;Main menu&lt;/strong&gt; (top-left waffle icon) and click &lt;strong&gt;"Admin"&lt;/strong&gt; (the wrench icon).&lt;/li&gt;
&lt;li&gt; In the Admin Area's left-hand sidebar, navigate to &lt;strong&gt;Overview &amp;gt; Users&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; Click the &lt;strong&gt;"New user"&lt;/strong&gt; button in the top-right.&lt;/li&gt;
&lt;li&gt; Fill out the form for your new "daily driver" account:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Full name:&lt;/strong&gt; (e.g., &lt;code&gt;Warren Jitsing&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Username:&lt;/strong&gt; (e.g., &lt;code&gt;warren.jitsing&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Email:&lt;/strong&gt; (e.g., &lt;code&gt;your.email@gmail.com&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Password:&lt;/strong&gt; The "Password" field is just a reset link. The new user will receive an email at the address you just provided, prompting them to set their own password.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Access level:&lt;/strong&gt; Leave this as &lt;strong&gt;"Regular"&lt;/strong&gt;. This means they are a regular user, not an instance administrator.&lt;/li&gt;
&lt;li&gt; Click &lt;strong&gt;"Create user"&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You will now need to log out of your &lt;code&gt;root&lt;/code&gt; account, check your email, and follow the confirmation link to set the password for your new &lt;code&gt;warren.jitsing&lt;/code&gt; user.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Add Your User to Your Groups
&lt;/h3&gt;

&lt;p&gt;After setting your new user's password, log out and log back in as your new, non-admin user (e.g., &lt;code&gt;warren.jitsing&lt;/code&gt;). You will notice you can't see any of your projects. This is because your new user has not been granted any permissions.&lt;/p&gt;

&lt;p&gt;We must now add this user to our Groups as a &lt;strong&gt;Maintainer&lt;/strong&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Log out of your new user and log &lt;em&gt;back in&lt;/em&gt; as &lt;code&gt;root&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; Navigate to your groups: &lt;strong&gt;Main menu &amp;gt; Groups &amp;gt; Your groups&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; Click on the &lt;strong&gt;&lt;code&gt;CICD-Stack&lt;/code&gt;&lt;/strong&gt; group.&lt;/li&gt;
&lt;li&gt; In the group's left-hand sidebar, navigate to &lt;strong&gt;Manage &amp;gt; Members&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; Click the &lt;strong&gt;"Invite members"&lt;/strong&gt; button.&lt;/li&gt;
&lt;li&gt; In the "GitLab member or email address" box, type the username of your new user (e.g., &lt;code&gt;warren.jitsing&lt;/code&gt;) and select them.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Select a role:&lt;/strong&gt; Choose &lt;strong&gt;"Maintainer"&lt;/strong&gt;. This gives the user high-level access &lt;em&gt;within the group&lt;/em&gt; (like creating new projects and managing branch rules) without making them an instance administrator.&lt;/li&gt;
&lt;li&gt; Click &lt;strong&gt;"Invite"&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; Repeat this entire process for your &lt;strong&gt;&lt;code&gt;Articles&lt;/code&gt;&lt;/strong&gt; group.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Next Steps: Moving Away from &lt;code&gt;root&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;You can now log out as &lt;code&gt;root&lt;/code&gt; and log in as your new "Maintainer" user. You will have full access to both groups and their projects.&lt;/p&gt;

&lt;p&gt;From this point forward, we will stop using the &lt;code&gt;root&lt;/code&gt; account and its Personal Access Token (PAT). In the next article, when we configure Jenkins, we will explore more secure authentication methods, such as using &lt;strong&gt;Group Access Tokens&lt;/strong&gt; or &lt;strong&gt;Project Access Tokens&lt;/strong&gt;, which are not tied to any human user and are the best practice for CI/CD automation.&lt;/p&gt;

</description>
      <category>gitlab</category>
      <category>devops</category>
      <category>cicd</category>
      <category>git</category>
    </item>
    <item>
      <title>Part 02: Building a Sovereign Software Factory: The Local Root CA &amp; Trust Chains</title>
      <dc:creator>Warren Jitsing</dc:creator>
      <pubDate>Tue, 16 Dec 2025 05:05:13 +0000</pubDate>
      <link>https://dev.to/warren_jitsing_dd1c1d6fc6/building-a-sovereign-software-factory-the-local-root-ca-trust-chains-1igf</link>
      <guid>https://dev.to/warren_jitsing_dd1c1d6fc6/building-a-sovereign-software-factory-the-local-root-ca-trust-chains-1igf</guid>
      <description>&lt;p&gt;GitHub: &lt;a href="https://github.com/InfiniteConsult/0006_cicd_part02_certificate_authority" rel="noopener noreferrer"&gt;https://github.com/InfiniteConsult/0006_cicd_part02_certificate_authority&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; In this installment, we solve the "Trust Gap" inherent in self-hosted infrastructure. We reject the insecurity of HTTP and the limitations of public CAs (Let's Encrypt) by building our own &lt;strong&gt;Local Certificate Authority&lt;/strong&gt;. We use &lt;code&gt;openssl&lt;/code&gt; to mint a Root CA, automate the issuance of SAN-compliant certificates for every service, and manually inject trust into the fragmented keystores of our Host OS, Browsers, and Java applications to eliminate SSL handshake failures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Sovereign Software Factory Series:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Part 01:&lt;/strong&gt; Building a Sovereign Software Factory: Docker Networking &amp;amp; Persistence&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Part 02: Building a Sovereign Software Factory: The Local Root CA &amp;amp; Trust Chains&lt;/strong&gt; (You are here)&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Part 03:&lt;/strong&gt; Building a Sovereign Software Factory: Self-Hosted GitLab &amp;amp; Secrets Management&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Part 04:&lt;/strong&gt; Building a Sovereign Software Factory: Jenkins Configuration as Code (JCasC)&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Part 05:&lt;/strong&gt; Building a Sovereign Software Factory: Artifactory &amp;amp; The "Strict TLS" Trap&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Part 06:&lt;/strong&gt; Building a Sovereign Software Factory: SonarQube Quality Gates&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Part 07:&lt;/strong&gt; Building a Sovereign Software Factory: ChatOps with Mattermost&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Part 08:&lt;/strong&gt; Building a Sovereign Software Factory: Observability with the ELK Stack&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Part 09:&lt;/strong&gt; Building a Sovereign Software Factory: Monitoring with Prometheus &amp;amp; Grafana&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 10:&lt;/strong&gt; Building a Sovereign Software Factory: The Python API Package (Capstone)&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Chapter 1: The "HTTPS Everywhere" Mandate
&lt;/h1&gt;

&lt;h2&gt;
  
  
  1.1 The Problem: The "Not Secure" Pain Point
&lt;/h2&gt;

&lt;p&gt;We have successfully built our foundational "city". We have a "Control Center" (DooD), "roads" (&lt;code&gt;cicd-net&lt;/code&gt;), and "foundations" (our hybrid persistence model).&lt;/p&gt;

&lt;p&gt;But we have a new, critical "pain point." Our services can communicate, but they do so over unencrypted &lt;code&gt;http&lt;/code&gt;. This is insecure, unprofessional, and does not emulate a real-world environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pedagogical Failure (Browsers)
&lt;/h3&gt;

&lt;p&gt;If we were to deploy GitLab right now on &lt;code&gt;http://gitlab.cicd.local&lt;/code&gt;, our browsers would immediately betray the problem. We would be faced with a "Not Secure" warning in the address bar. This breaks user trust and, for many modern web features, can block functionality entirely.&lt;/p&gt;

&lt;p&gt;If we tried to use a "magic" self-signed certificate (which we will deconstruct later), the problem gets &lt;em&gt;worse&lt;/em&gt;. The browser would present a full-page, "Your connection is not private" interstitial error (like &lt;code&gt;NET::ERR_CERT_AUTHORITY_INVALID&lt;/code&gt; or &lt;code&gt;ERR_INSECURE_RESPONSE&lt;/code&gt;). This is a hard stop for most users.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pedagogical Failure (Tools)
&lt;/h3&gt;

&lt;p&gt;This "pain point" is not just cosmetic. It's a functional "stack killer" for our specific set of tools.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;curl&lt;/code&gt;&lt;/strong&gt;: Any script using &lt;code&gt;curl&lt;/code&gt; to access an HTTPS service with a bad certificate will fail with a hard error: &lt;code&gt;curl: (60) SSL certificate problem: unable to get local issuer certificate&lt;/code&gt;. The only way around this is to use the &lt;code&gt;--insecure&lt;/code&gt; flag, which is a terrible security practice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The "Java Gotcha"&lt;/strong&gt;: This is the most critical failure for our stack. Jenkins, Artifactory, and SonarQube are all Java applications. When our Jenkins container tries to make an API call or receive a webhook from our &lt;code&gt;https://gitlab.cicd.local&lt;/code&gt; server, the Java Virtual Machine (JVM) will reject the untrusted certificate and throw a fatal &lt;code&gt;javax.net.ssl.SSLHandshakeException&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This single Java exception will bring our entire automated pipeline to a grinding halt.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Goal
&lt;/h3&gt;

&lt;p&gt;Our goal is not just to "get rid of the warning." It is to build a professional, secure, and trusted internal ecosystem. To do this, we must become our own "passport office."&lt;/p&gt;

&lt;h1&gt;
  
  
  Chapter 2: A "First Principles" Guide to PKI and Trust
&lt;/h1&gt;

&lt;h2&gt;
  
  
  2.1 The Solution: Public Key Infrastructure (PKI)
&lt;/h2&gt;

&lt;p&gt;To solve our "Not Secure" problem, we must adopt the same solution the entire internet uses: &lt;strong&gt;HTTPS&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;HTTPS is not a single thing; it's a combination of two: &lt;code&gt;http&lt;/code&gt; (the protocol we already know) layered on top of &lt;strong&gt;SSL/TLS&lt;/strong&gt; (Secure Sockets Layer/Transport Layer Security). This secure layer provides two fundamental guarantees:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Encryption&lt;/strong&gt;: It creates a secure, private "tunnel" between the client and the server. Any data sent through this tunnel (passwords, API keys, source code) is scrambled and unreadable to eavesdroppers.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Identity&lt;/strong&gt;: This is the more complex and crucial part. How does your browser &lt;em&gt;know&lt;/em&gt; it's talking to the real &lt;code&gt;gitlab&lt;/code&gt; and not an imposter? The server must present a form of identification.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This system of identification is called &lt;strong&gt;Public Key Infrastructure (PKI)&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The Analogy: "The Passport Office"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;PKI works exactly like the international passport system.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;A Server Certificate is a "Passport"&lt;/strong&gt;: It's a digital file that proves a server's identity (e.g., "I am &lt;code&gt;gitlab&lt;/code&gt;").&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;A Certificate Authority (CA) is a "Passport Office"&lt;/strong&gt;: It's a trusted entity (like a government) that issues and signs the "passport," attesting to its validity.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;A Trust Store is a "List of Trusted Governments"&lt;/strong&gt;: Your browser and operating system come with a pre-installed "trust store" that contains a list of all the major, legitimate "passport offices" (like Let's Encrypt, DigiCert, etc.) in the world.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When your browser connects to a server, it looks at the "passport" (the certificate) and checks the "signature" on it. If it was signed by a "passport office" (CA) in its trusted list, it grants access. If not, it blocks the connection.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  2.2 Deconstructing Our Options
&lt;/h2&gt;

&lt;p&gt;We need a "passport" (a certificate) for our services. We have three options for getting one.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 1: A Public CA (e.g., Let's Encrypt)
&lt;/h3&gt;

&lt;p&gt;This is the standard for all public websites. Let's Encrypt is a globally trusted "passport office." Your browser and OS already have it in their trust store.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why we &lt;em&gt;can't&lt;/em&gt; use it:&lt;/strong&gt; Let's Encrypt must &lt;em&gt;validate&lt;/em&gt; that you own a domain before issuing a certificate for it. It does this by connecting to your domain over the public internet. This process (Domain Validation or DV) is impossible for our local stack. The CA's servers cannot reach &lt;code&gt;localhost&lt;/code&gt; or our container's hostname (&lt;code&gt;gitlab&lt;/code&gt;). We cannot prove we "own" these private names, so the public CA will refuse to issue a certificate.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 2: A Simple Self-Signed Certificate
&lt;/h3&gt;

&lt;p&gt;This is the "quick and dirty" method. A self-signed certificate is one that is signed by itself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it fails:&lt;/strong&gt; This is the equivalent of a &lt;strong&gt;"hand-drawn passport"&lt;/strong&gt;. You are telling the browser, "I am &lt;code&gt;gitlab&lt;/code&gt;, and I vouch for myself." The browser, acting as the security guard, has no reason to trust you. It checks its list of "trusted passport offices" (the trust store) and finds no match. It correctly throws a full-page security warning (&lt;code&gt;NET::ERR_CERT_AUTHORITY_INVALID&lt;/code&gt;) to protect you from a potential man-in-the-middle attack. This is the very &lt;code&gt;SSLHandshakeException&lt;/code&gt; that will break our Java-based tools.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 3: Our Private/Local CA (The Solution)
&lt;/h3&gt;

&lt;p&gt;This is the professional solution that balances our two problems. We will build our &lt;em&gt;own&lt;/em&gt; "passport office".&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; We will become our own &lt;strong&gt;Certificate Authority (CA)&lt;/strong&gt; by creating a &lt;strong&gt;Root CA certificate&lt;/strong&gt;. This is our "passport office's business license."&lt;/li&gt;
&lt;li&gt; We will then use our new CA to issue "passports" (service certificates) for &lt;code&gt;gitlab&lt;/code&gt;, &lt;code&gt;jenkins&lt;/code&gt;, and all our other services.&lt;/li&gt;
&lt;li&gt; Finally, we will solve the "trust" problem by manually telling our local computers and containers: "This new 'passport office' is an official, trusted authority."&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;From that point on, &lt;em&gt;any&lt;/em&gt; passport (certificate) issued by our office will be automatically trusted by our local tools, browsers, and services.&lt;/p&gt;

&lt;h1&gt;
  
  
  Chapter 3: The "Hard Way" - Building Our CA with &lt;code&gt;openssl&lt;/code&gt;
&lt;/h1&gt;

&lt;h2&gt;
  
  
  3.1 Why We Use &lt;code&gt;openssl&lt;/code&gt; (The "First Principles")
&lt;/h2&gt;

&lt;p&gt;We will begin by creating our Certificate Authority using the &lt;code&gt;openssl&lt;/code&gt; command-line tool directly. This is the "hard way."&lt;/p&gt;

&lt;p&gt;We are doing this for a specific pedagogical reason. We could use a simpler tool like &lt;code&gt;easyrsa&lt;/code&gt;, but &lt;code&gt;easyrsa&lt;/code&gt; is just a &lt;strong&gt;shell script wrapper&lt;/strong&gt; that hides all the complexity. It automates all the tedious, "first principles" steps that a real CA must perform.&lt;/p&gt;

&lt;p&gt;By using &lt;code&gt;openssl&lt;/code&gt; directly, we are forced to deconstruct those steps. We will have to manually:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Build the CA's "Blueprint"&lt;/strong&gt;: We will create an &lt;code&gt;openssl.cnf&lt;/code&gt; configuration file that defines the rules and policies of our "passport office."&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Create the "CA Database"&lt;/strong&gt;: We will manually create the &lt;code&gt;index.txt&lt;/code&gt; "master ledger" and the &lt;code&gt;serial&lt;/code&gt; "ticket number dispenser" that &lt;code&gt;openssl&lt;/code&gt; needs to track the certificates it issues.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Manage the "Keys"&lt;/strong&gt;: We will build the secure directory structure to protect our CA's "master stamp" (its private key).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By doing this "hard way" first, you will understand what is &lt;em&gt;actually&lt;/em&gt; happening under the hood. Only then will we introduce the "easy way" (&lt;code&gt;easyrsa&lt;/code&gt;) as the automation tool it is.&lt;/p&gt;

&lt;p&gt;We will build our entire PKI (Public Key Infrastructure) inside the &lt;code&gt;~/cicd_stack/ca&lt;/code&gt; directory we created on our &lt;strong&gt;host machine&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  3.2 The "CA Blueprint": &lt;code&gt;openssl.cnf&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Our first step is to create the "blueprint" for our new "passport office." This is a configuration file, &lt;code&gt;openssl.cnf&lt;/code&gt;, that tells &lt;code&gt;openssl&lt;/code&gt; all of its rules. It defines the file locations, the default policies, and, most importantly, the &lt;em&gt;extensions&lt;/em&gt; that make our root certificate a &lt;strong&gt;Certificate Authority&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Create this file on your &lt;strong&gt;host machine&lt;/strong&gt; at &lt;code&gt;~/cicd_stack/ca/openssl.cnf&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[ ca ]&lt;/span&gt;
&lt;span class="py"&gt;default_ca&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;CA_default&lt;/span&gt;

&lt;span class="nn"&gt;[ CA_default ]&lt;/span&gt;
&lt;span class="c"&gt;# --- Directory and file locations ---
&lt;/span&gt;&lt;span class="py"&gt;dir&lt;/span&gt;               &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;./pki&lt;/span&gt;
&lt;span class="py"&gt;certs&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$dir/certs&lt;/span&gt;
&lt;span class="py"&gt;crl_dir&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$dir/crl&lt;/span&gt;
&lt;span class="py"&gt;new_certs_dir&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$dir/newcerts&lt;/span&gt;
&lt;span class="py"&gt;database&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$dir/index.txt&lt;/span&gt;
&lt;span class="py"&gt;serial&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$dir/serial&lt;/span&gt;
&lt;span class="py"&gt;RANDFILE&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$dir/private/.rand&lt;/span&gt;

&lt;span class="c"&gt;# --- CA Key and Certificate ---
&lt;/span&gt;&lt;span class="py"&gt;private_key&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$dir/private/ca.key&lt;/span&gt;
&lt;span class="py"&gt;certificate&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$dir/certs/ca.pem&lt;/span&gt;

&lt;span class="c"&gt;# --- Policy &amp;amp; Extensions ---
&lt;/span&gt;&lt;span class="py"&gt;default_md&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;sha256&lt;/span&gt;
&lt;span class="py"&gt;default_days&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;3650  # 10 years&lt;/span&gt;
&lt;span class="py"&gt;policy&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;policy_strict&lt;/span&gt;
&lt;span class="py"&gt;x509_extensions&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;v3_ca&lt;/span&gt;

&lt;span class="nn"&gt;[ policy_strict ]&lt;/span&gt;
&lt;span class="c"&gt;# Rules for signing: which fields must match the CA
&lt;/span&gt;&lt;span class="py"&gt;countryName&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;match&lt;/span&gt;
&lt;span class="py"&gt;stateOrProvinceName&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;match&lt;/span&gt;
&lt;span class="py"&gt;organizationName&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;match&lt;/span&gt;
&lt;span class="py"&gt;organizationalUnitName&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;optional&lt;/span&gt;
&lt;span class="py"&gt;commonName&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;supplied&lt;/span&gt;
&lt;span class="py"&gt;emailAddress&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;optional&lt;/span&gt;

&lt;span class="nn"&gt;[ req ]&lt;/span&gt;
&lt;span class="c"&gt;# This section is used by the 'openssl req' command
&lt;/span&gt;&lt;span class="py"&gt;default_bits&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;4096&lt;/span&gt;
&lt;span class="py"&gt;distinguished_name&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;req_distinguished_name&lt;/span&gt;
&lt;span class="py"&gt;string_mask&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;utf8only&lt;/span&gt;
&lt;span class="py"&gt;default_md&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;sha256&lt;/span&gt;
&lt;span class="c"&gt;# Tell 'openssl req -x509' to use our 'v3_ca' extensions
&lt;/span&gt;&lt;span class="py"&gt;x509_extensions&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;v3_ca&lt;/span&gt;

&lt;span class="nn"&gt;[ req_distinguished_name ]&lt;/span&gt;
&lt;span class="c"&gt;# These are the DN fields it will prompt you for
&lt;/span&gt;&lt;span class="py"&gt;countryName&lt;/span&gt;                     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;Country Name (2 letter code)&lt;/span&gt;
&lt;span class="py"&gt;stateOrProvinceName&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;State or Province Name&lt;/span&gt;
&lt;span class="py"&gt;localityName&lt;/span&gt;                    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;Locality Name (eg, city)&lt;/span&gt;
&lt;span class="py"&gt;organizationName&lt;/span&gt;                &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;Organization Name&lt;/span&gt;
&lt;span class="py"&gt;organizationalUnitName&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;Organizational Unit Name (eg, section)&lt;/span&gt;
&lt;span class="py"&gt;commonName&lt;/span&gt;                      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;Common Name (e.g. server FQDN or YOUR name)&lt;/span&gt;
&lt;span class="py"&gt;emailAddress&lt;/span&gt;                    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;Email Address&lt;/span&gt;

&lt;span class="nn"&gt;[ v3_ca ]&lt;/span&gt;
&lt;span class="c"&gt;# --- Extensions for the Root CA certificate itself ---
&lt;/span&gt;&lt;span class="py"&gt;subjectKeyIdentifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;hash&lt;/span&gt;
&lt;span class="py"&gt;authorityKeyIdentifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;keyid:always,issuer&lt;/span&gt;
&lt;span class="py"&gt;basicConstraints&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;critical, CA:TRUE, pathlen:0&lt;/span&gt;
&lt;span class="py"&gt;keyUsage&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;critical, digitalSignature, cRLSign, keyCertSign&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deconstruction
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;[ CA_default ]&lt;/code&gt;: This block defines our working directory and file structure. We are telling &lt;code&gt;openssl&lt;/code&gt; to look for its database in the &lt;code&gt;pki&lt;/code&gt; directory (&lt;code&gt;dir = ./pki&lt;/code&gt;), to find its "ledger" at &lt;code&gt;pki/index.txt&lt;/code&gt;, and its "ticket dispenser" at &lt;code&gt;pki/serial&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;[ policy_strict ]&lt;/code&gt;: This defines the rules for signing. We are telling our CA that it's allowed to sign certificates for other organizations (as long as &lt;code&gt;organizationName&lt;/code&gt; matches) and they must supply their own &lt;code&gt;commonName&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;[ v3_ca ]&lt;/code&gt;: This is the most important section. It defines the &lt;em&gt;powers&lt;/em&gt; of our Root CA.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;basicConstraints = critical, CA:TRUE&lt;/code&gt;&lt;/strong&gt;: This is the master switch. It tells the world this certificate is a &lt;strong&gt;Certificate Authority&lt;/strong&gt; (a "passport office") and has the power to sign other certificates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;keyUsage = ... keyCertSign, cRLSign&lt;/code&gt;&lt;/strong&gt;: This specifies &lt;em&gt;what&lt;/em&gt; the key can be used for. &lt;code&gt;keyCertSign&lt;/code&gt; is the specific permission that allows it to "sign other certificates." &lt;code&gt;cRLSign&lt;/code&gt; allows it to sign a "revocation list" (a list of "stolen passports").&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  3.3 The "CA Database"
&lt;/h2&gt;

&lt;p&gt;Now that we have our "blueprint" (&lt;code&gt;openssl.cnf&lt;/code&gt;), we must create the physical "office" and "filing system" that &lt;code&gt;openssl&lt;/code&gt; will use. This is the "database" that our CA will use to track every "passport" it ever issues.&lt;/p&gt;

&lt;p&gt;This is the second "first principles" component that wrappers like &lt;code&gt;easyrsa&lt;/code&gt; automate and hide from you. Our &lt;code&gt;openssl.cnf&lt;/code&gt; file told &lt;code&gt;openssl&lt;/code&gt; to &lt;em&gt;look for&lt;/em&gt; these files in the &lt;code&gt;./pki&lt;/code&gt; directory; now, we must create them.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;pki/private/&lt;/code&gt;: This will be our "vault." It will hold our CA's highly-sensitive private key.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pki/certs/&lt;/code&gt;: This will be our "public filing cabinet" for public-facing certificates, like our main CA certificate.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pki/newcerts/&lt;/code&gt;: This will be the "output tray" where &lt;code&gt;openssl&lt;/code&gt; places all the new service certificates it signs.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pki/index.txt&lt;/code&gt;: This is the CA's "master ledger." It's a simple text file that &lt;code&gt;openssl&lt;/code&gt; uses to keep a database of every certificate it has signed, noting whether it's "valid," "revoked," or "expired."&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pki/serial&lt;/code&gt;: This is the "ticket number dispenser." It's a file that contains the &lt;em&gt;next&lt;/em&gt; unique serial number (in hexadecimal) to be issued. &lt;code&gt;openssl&lt;/code&gt; will read this file, assign the number, and then increment the file, ensuring no two certificates ever get the same serial number.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Action Plan: Creating the CA Directory
&lt;/h3&gt;

&lt;p&gt;We will now create this entire structure using our first script, &lt;code&gt;01-create-ca.sh&lt;/code&gt;. This script will also generate our private key and our root certificate, completing our CA setup in one go.&lt;/p&gt;

&lt;p&gt;Here is the code for &lt;code&gt;01-create-ca.sh&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="c"&gt;# This script creates the CA directory structure, database,&lt;/span&gt;
&lt;span class="c"&gt;# and generates the Root CA private key and certificate.&lt;/span&gt;

&lt;span class="nb"&gt;cd&lt;/span&gt; ~/cicd_stack/ca &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"no CICD directory present"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;exit&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"openssl.cnf"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"openssl.cnf not found in ~/cicd_stack/ca. Please create it before proceeding"&lt;/span&gt;
&lt;span class="nb"&gt;exit
&lt;/span&gt;&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# --- Configuration ---&lt;/span&gt;
&lt;span class="c"&gt;# The password for your new Root CA private key.&lt;/span&gt;
&lt;span class="c"&gt;# IMPORTANT: Change this to a strong, secure password.&lt;/span&gt;
&lt;span class="nv"&gt;CA_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"your_secure_password"&lt;/span&gt;

&lt;span class="c"&gt;# The Distinguished Name (DN) for your Root CA.&lt;/span&gt;
&lt;span class="c"&gt;# Customize these values for your organization.&lt;/span&gt;
&lt;span class="nv"&gt;COUNTRY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"ZA"&lt;/span&gt;
&lt;span class="nv"&gt;STATE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Gauteng"&lt;/span&gt;
&lt;span class="nv"&gt;LOCALITY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Johannesburg"&lt;/span&gt;
&lt;span class="nv"&gt;ORG_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Local CICD Stack"&lt;/span&gt;
&lt;span class="nv"&gt;CA_CN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Local CICD Root CA"&lt;/span&gt;

&lt;span class="c"&gt;# --- Setup ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Creating PKI directory structure ---"&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; pki/&lt;span class="o"&gt;{&lt;/span&gt;private,certs,newcerts,crl&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Set correct permissions for the "vault"&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;700 pki/private

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Creating CA database ---"&lt;/span&gt;
&lt;span class="nb"&gt;touch &lt;/span&gt;pki/index.txt
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"1000"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; pki/serial

&lt;span class="c"&gt;# --- Generate Root CA ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- 1. Generating Root CA Private Key (ca.key) ---"&lt;/span&gt;
&lt;span class="c"&gt;# Use -passout to provide the password non-interactively&lt;/span&gt;
openssl genrsa &lt;span class="nt"&gt;-aes256&lt;/span&gt; &lt;span class="nt"&gt;-passout&lt;/span&gt; pass:&lt;span class="nv"&gt;$CA_PASSWORD&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-out&lt;/span&gt; pki/private/ca.key 4096

&lt;span class="c"&gt;# Secure the key&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;400 pki/private/ca.key

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- 2. Generating Root CA Certificate (ca.pem) ---"&lt;/span&gt;
&lt;span class="c"&gt;# Use the '-subj' flag to pass DN info non-interactively&lt;/span&gt;
openssl req &lt;span class="nt"&gt;-config&lt;/span&gt; openssl.cnf &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;-key&lt;/span&gt; pki/private/ca.key &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;-passin&lt;/span&gt; pass:&lt;span class="nv"&gt;$CA_PASSWORD&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;-new&lt;/span&gt; &lt;span class="nt"&gt;-x509&lt;/span&gt; &lt;span class="nt"&gt;-days&lt;/span&gt; 3650 &lt;span class="nt"&gt;-sha256&lt;/span&gt; &lt;span class="nt"&gt;-extensions&lt;/span&gt; v3_ca &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;-subj&lt;/span&gt; &lt;span class="s2"&gt;"/C=&lt;/span&gt;&lt;span class="nv"&gt;$COUNTRY&lt;/span&gt;&lt;span class="s2"&gt;/ST=&lt;/span&gt;&lt;span class="nv"&gt;$STATE&lt;/span&gt;&lt;span class="s2"&gt;/L=&lt;/span&gt;&lt;span class="nv"&gt;$LOCALITY&lt;/span&gt;&lt;span class="s2"&gt;/O=&lt;/span&gt;&lt;span class="nv"&gt;$ORG_NAME&lt;/span&gt;&lt;span class="s2"&gt;/CN=&lt;/span&gt;&lt;span class="nv"&gt;$CA_CN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;-out&lt;/span&gt; pki/certs/ca.pem

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- CA creation complete ---"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"CA Private Key: pki/private/ca.key"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"CA Certificate: pki/certs/ca.pem"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deconstruction
&lt;/h3&gt;

&lt;p&gt;Let's deconstruct the "Action" part of this script:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;mkdir -p ...&lt;/code&gt; &amp;amp; &lt;code&gt;chmod 700 ...&lt;/code&gt;: We create our directory structure and immediately secure the "vault" (&lt;code&gt;pki/private&lt;/code&gt;). We give it &lt;code&gt;700&lt;/code&gt; (&lt;code&gt;rwx------&lt;/code&gt;) permissions, which means only the owner (our user) can read, write, or enter it. This is the fix for the &lt;code&gt;Permission denied&lt;/code&gt; error we discovered during our testing.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;touch pki/index.txt&lt;/code&gt; &amp;amp; &lt;code&gt;echo "1000" &amp;gt; pki/serial&lt;/code&gt;: We create the "ledger" and "ticket dispenser" files.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;openssl genrsa -aes256 ...&lt;/code&gt;: This is &lt;strong&gt;Step 1: Create the CA Private Key&lt;/strong&gt; (the "master stamp").

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-aes256&lt;/code&gt;: We encrypt the key with a strong cipher.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-passout pass:$CA_PASSWORD&lt;/code&gt;: We provide the password non-interactively from our variable.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-out pki/private/ca.key&lt;/code&gt;: We save the key directly into our secure "vault."&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;chmod 400 pki/private/ca.key&lt;/code&gt;: We immediately set the key file itself to be &lt;strong&gt;read-only&lt;/strong&gt; (&lt;code&gt;r--------&lt;/code&gt;) for the owner, protecting it from accidental modification.&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;openssl req -x509 ...&lt;/code&gt;: This is &lt;strong&gt;Step 2: Create the CA Certificate&lt;/strong&gt; (the "business license").

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-key pki/private/ca.key&lt;/code&gt; &amp;amp; &lt;code&gt;-passin ...&lt;/code&gt;: We specify our new "master stamp" and provide its password.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-x509&lt;/code&gt;: This is a critical flag. It tells &lt;code&gt;openssl&lt;/code&gt; to &lt;em&gt;not&lt;/em&gt; create a "signing request" (a CSR) but to create a &lt;strong&gt;self-signed public certificate&lt;/strong&gt;. This is what makes it a "Root" CA.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-extensions v3_ca&lt;/code&gt;: This is the most important part. It tells &lt;code&gt;openssl&lt;/code&gt; to look at our &lt;code&gt;openssl.cnf&lt;/code&gt; file and apply the extensions from the &lt;code&gt;[ v3_ca ]&lt;/code&gt; section. This is what flips the switch and embeds the &lt;code&gt;CA:TRUE&lt;/code&gt; "power" into our certificate.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-subj "..."&lt;/code&gt;: This provides all the "passport office" details (Country, Organization, etc.) non-interactively.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Action
&lt;/h3&gt;

&lt;p&gt;Place the &lt;code&gt;openssl.cnf&lt;/code&gt; file and the &lt;code&gt;01-create-ca.sh&lt;/code&gt; script in your &lt;code&gt;~/cicd_stack/ca&lt;/code&gt; directory on your host machine.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# (Run on HOST, inside ~/cicd_stack/ca)&lt;/span&gt;

&lt;span class="c"&gt;# 1. Make the script executable&lt;/span&gt;
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x 01-create-ca.sh

&lt;span class="c"&gt;# 2. Run the script&lt;/span&gt;
./01-create-ca.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt;&lt;br&gt;
The script will run non-interactively. When it finishes, you will have a fully functional Certificate Authority.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;--- Creating PKI directory structure ---
--- Creating CA database ---
--- 1. Generating Root CA Private Key (ca.key) ---
--- 2. Generating Root CA Certificate (ca.pem) ---
--- CA creation complete ---
CA Private Key: pki/private/ca.key
CA Certificate: pki/certs/ca.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Chapter 4: Issuing Service Certificates (The SAN "Gotcha")
&lt;/h1&gt;

&lt;h2&gt;
  
  
  4.1 The Goal: Creating Reusable "Passports"
&lt;/h2&gt;

&lt;p&gt;We have successfully built our "passport office" (our Root CA). We have a secure "vault" for our &lt;code&gt;ca.key&lt;/code&gt; and a public "business license" (&lt;code&gt;ca.pem&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Now, we must start issuing "passports" (service certificates) for each of our 10 services. We could do this one by one, but that would be a tedious and error-prone manual process. Instead, we will immediately automate this.&lt;/p&gt;

&lt;p&gt;We will create a single, reusable script named &lt;code&gt;02-issue-service-cert.sh&lt;/code&gt;. This script will be our "passport printer." We will be able to run &lt;code&gt;./02-issue-service-cert.sh gitlab&lt;/code&gt; or &lt;code&gt;./02-issue-service-cert.sh jenkins.cicd.local&lt;/code&gt;, and it will automatically:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Create a secure, isolated directory for the service.&lt;/li&gt;
&lt;li&gt; Generate a new private key for that service.&lt;/li&gt;
&lt;li&gt; Generate a Certificate Signing Request (CSR) with the correct "first principles" configuration.&lt;/li&gt;
&lt;li&gt; Use our Root CA to sign the request and issue a valid, trusted "passport."&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  4.2 Action Plan: &lt;code&gt;02-issue-service-cert.sh&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Here is the code for our "passport printer" script. Create this file on your &lt;strong&gt;host machine&lt;/strong&gt; at &lt;code&gt;~/cicd_stack/ca/02-issue-service-cert.sh&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="c"&gt;# This script issues a new, signed certificate for a service.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# USAGE: ./02-issue-service-cert.sh &amp;lt;service_name_or_path&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# EXAMPLE: ./02-issue-service-cert.sh gitlab&lt;/span&gt;
&lt;span class="c"&gt;# EXAMPLE: ./02-issue-service-cert.sh elk/elasticsearch&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# This will create a new directory 'pki/services/gitlab'&lt;/span&gt;
&lt;span class="c"&gt;# containing 'gitlab.key.pem' and 'gitlab.crt.pem'.&lt;/span&gt;

&lt;span class="nb"&gt;cd&lt;/span&gt; ~/cicd_stack/ca &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"no CICD directory present"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;exit&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# --- Configuration ---&lt;/span&gt;
&lt;span class="c"&gt;# This MUST match the password you used in '01-create-ca.sh'&lt;/span&gt;
&lt;span class="nv"&gt;CA_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"your_secure_password"&lt;/span&gt;

&lt;span class="c"&gt;# --- Script ---&lt;/span&gt;
&lt;span class="nv"&gt;SERVICE_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
&lt;span class="nv"&gt;CERT_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SERVICE_NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SERVICE_NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: No service name provided."&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"USAGE: ./02-issue-service-cert.sh &amp;lt;service_name_or_path&amp;gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Preparing environment for service: &lt;/span&gt;&lt;span class="nv"&gt;$SERVICE_NAME&lt;/span&gt;&lt;span class="s2"&gt; ---"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Certificate simple name will be: &lt;/span&gt;&lt;span class="nv"&gt;$CERT_NAME&lt;/span&gt;&lt;span class="s2"&gt; ---"&lt;/span&gt;

&lt;span class="c"&gt;# 1. Define all file paths&lt;/span&gt;
&lt;span class="nv"&gt;SERVICE_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"pki/services/&lt;/span&gt;&lt;span class="nv"&gt;$SERVICE_NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;KEY_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SERVICE_DIR&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$CERT_NAME&lt;/span&gt;&lt;span class="s2"&gt;.key.pem"&lt;/span&gt;
&lt;span class="nv"&gt;CSR_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SERVICE_DIR&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$CERT_NAME&lt;/span&gt;&lt;span class="s2"&gt;.csr"&lt;/span&gt;
&lt;span class="nv"&gt;CERT_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SERVICE_DIR&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$CERT_NAME&lt;/span&gt;&lt;span class="s2"&gt;.crt.pem"&lt;/span&gt;
&lt;span class="nv"&gt;EXT_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SERVICE_DIR&lt;/span&gt;&lt;span class="s2"&gt;/v3.ext"&lt;/span&gt;

&lt;span class="c"&gt;# 2. Create the service's private directory&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="nv"&gt;$SERVICE_DIR&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;700 &lt;span class="nv"&gt;$SERVICE_DIR&lt;/span&gt;

&lt;span class="c"&gt;# 3. Generate the service's Private Key&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- 1. Generating Private Key (&lt;/span&gt;&lt;span class="nv"&gt;$CERT_NAME&lt;/span&gt;&lt;span class="s2"&gt;.key.pem) ---"&lt;/span&gt;
openssl genrsa &lt;span class="nt"&gt;-out&lt;/span&gt; &lt;span class="nv"&gt;$KEY_FILE&lt;/span&gt; 4096
&lt;span class="nb"&gt;chmod &lt;/span&gt;400 &lt;span class="nv"&gt;$KEY_FILE&lt;/span&gt;

&lt;span class="c"&gt;# 4. Create the SAN (Subject Alternative Name) config file&lt;/span&gt;
&lt;span class="c"&gt;# This is critical for modern browser compatibility.&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- 2. Creating SAN config file (v3.ext) ---"&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$EXT_FILE&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt; &lt;span class="no"&gt;EOM&lt;/span&gt;&lt;span class="sh"&gt;
[ v3_ext ]
subjectAltName = @alt_names
[alt_names]
DNS.1 = &lt;/span&gt;&lt;span class="nv"&gt;$CERT_NAME&lt;/span&gt;&lt;span class="sh"&gt;
DNS.2 = localhost
IP.1 = 127.0.0.1
&lt;/span&gt;&lt;span class="no"&gt;EOM

&lt;/span&gt;&lt;span class="c"&gt;# 5. Generate a Certificate Signing Request (CSR)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- 3. Generating Certificate Signing Request (&lt;/span&gt;&lt;span class="nv"&gt;$CERT_NAME&lt;/span&gt;&lt;span class="s2"&gt;.csr) ---"&lt;/span&gt;
openssl req &lt;span class="nt"&gt;-new&lt;/span&gt; &lt;span class="nt"&gt;-sha256&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;-key&lt;/span&gt; &lt;span class="nv"&gt;$KEY_FILE&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;-subj&lt;/span&gt; &lt;span class="s2"&gt;"/C=ZA/ST=Gauteng/L=Johannesburg/O=Local CICD Stack/CN=&lt;/span&gt;&lt;span class="nv"&gt;$CERT_NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;-out&lt;/span&gt; &lt;span class="nv"&gt;$CSR_FILE&lt;/span&gt;

&lt;span class="c"&gt;# 6. Sign the CSR with our Root CA&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- 4. Signing the certificate with our Root CA ---"&lt;/span&gt;
openssl ca &lt;span class="nt"&gt;-config&lt;/span&gt; openssl.cnf &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;-extensions&lt;/span&gt; v3_ext &lt;span class="nt"&gt;-extfile&lt;/span&gt; &lt;span class="nv"&gt;$EXT_FILE&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;-days&lt;/span&gt; 365 &lt;span class="nt"&gt;-notext&lt;/span&gt; &lt;span class="nt"&gt;-md&lt;/span&gt; sha256 &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;-passin&lt;/span&gt; pass:&lt;span class="nv"&gt;$CA_PASSWORD&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;-in&lt;/span&gt; &lt;span class="nv"&gt;$CSR_FILE&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;-out&lt;/span&gt; &lt;span class="nv"&gt;$CERT_FILE&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;-batch&lt;/span&gt; &lt;span class="c"&gt;# Use non-interactive "batch" mode&lt;/span&gt;

&lt;span class="c"&gt;# 7. Clean up temporary files&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nv"&gt;$CSR_FILE&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nv"&gt;$EXT_FILE&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Successfully issued certificate for &lt;/span&gt;&lt;span class="nv"&gt;$SERVICE_NAME&lt;/span&gt;&lt;span class="s2"&gt; ---"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Certificate: &lt;/span&gt;&lt;span class="nv"&gt;$CERT_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Verifying SANs..."&lt;/span&gt;

&lt;span class="c"&gt;# 8. Verify the certificate's SANs&lt;/span&gt;
openssl x509 &lt;span class="nt"&gt;-in&lt;/span&gt; &lt;span class="nv"&gt;$CERT_FILE&lt;/span&gt; &lt;span class="nt"&gt;-noout&lt;/span&gt; &lt;span class="nt"&gt;-text&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-A1&lt;/span&gt; &lt;span class="s2"&gt;"Subject Alternative Name"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4.3 Deconstruction (The SAN "Gotcha")
&lt;/h2&gt;

&lt;p&gt;This script is the heart of our PKI, and it contains two critical, "first principles" lessons that we discovered during our testing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lesson 1: The "SAN Gotcha" (Why &lt;code&gt;CN&lt;/code&gt; is Deprecated)
&lt;/h3&gt;

&lt;p&gt;The most important part of this script is &lt;strong&gt;Step 4&lt;/strong&gt;. We don't just generate a CSR; we first create a temporary config file called &lt;code&gt;v3.ext&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# (Inside 02-issue-service-cert.sh)&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$EXT_FILE&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt; &lt;span class="no"&gt;EOM&lt;/span&gt;&lt;span class="sh"&gt;
[ v3_ext ]
subjectAltName = @alt_names
[alt_names]
DNS.1 = &lt;/span&gt;&lt;span class="nv"&gt;$CERT_NAME&lt;/span&gt;&lt;span class="sh"&gt;
DNS.2 = localhost
IP.1 = 127.0.0.1
&lt;/span&gt;&lt;span class="no"&gt;EOM
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the &lt;strong&gt;Subject Alternative Name (SAN)&lt;/strong&gt; field. In the early days of the internet, browsers only checked the &lt;strong&gt;Common Name (CN)&lt;/strong&gt; (which we set in Step 5 with &lt;code&gt;-subj "/CN=..."&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;However, this &lt;code&gt;CN&lt;/code&gt; field was ambiguous and is now &lt;strong&gt;deprecated&lt;/strong&gt;. All modern browsers and tools (including Java) &lt;strong&gt;ignore the CN field&lt;/strong&gt; and &lt;em&gt;only&lt;/em&gt; validate the SAN list.&lt;/p&gt;

&lt;p&gt;Our script fixes this by:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Creating a &lt;code&gt;v3.ext&lt;/code&gt; file that defines the SAN extension (&lt;code&gt;subjectAltName = @alt_names&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt; Populating the &lt;code&gt;[alt_names]&lt;/code&gt; list with all the names this certificate should be valid for.&lt;/li&gt;
&lt;li&gt; Telling &lt;code&gt;openssl ca&lt;/code&gt; to use these extensions with the &lt;code&gt;-extensions v3_ext -extfile $EXT_FILE&lt;/code&gt; flags.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We include three entries for maximum compatibility:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;DNS.1 = $CERT_NAME&lt;/code&gt;: (e.g., &lt;code&gt;DNS:gitlab&lt;/code&gt;) For &lt;strong&gt;container-to-container&lt;/strong&gt; communication on &lt;code&gt;cicd-net&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DNS.2 = localhost&lt;/code&gt;: For us to access the UI from our host browser via &lt;code&gt;https://localhost&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;IP.1 = 127.0.0.1&lt;/code&gt;: To fix the &lt;code&gt;curl https://127.0.0.1&lt;/code&gt; error we discovered.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Lesson 2: The "Nested Path Gotcha" (Using &lt;code&gt;basename&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;The second "pain point" we discovered was when we tried to issue a certificate for &lt;code&gt;elk/elasticsearch.cicd.local&lt;/code&gt;. Our script failed because it tried to create a file named &lt;code&gt;.../elk/elasticsearch.cicd.local/elk/elasticsearch.cicd.local.key.pem&lt;/code&gt;, which is redundant and wrong.&lt;/p&gt;

&lt;p&gt;The solution is the &lt;code&gt;basename&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# (Inside 02-issue-service-cert.sh)&lt;/span&gt;
&lt;span class="nv"&gt;SERVICE_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
&lt;span class="nv"&gt;CERT_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SERVICE_NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command is a standard Linux utility that strips the directory path from a string.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If &lt;code&gt;$SERVICE_NAME&lt;/code&gt; is &lt;code&gt;gitlab.cicd.local&lt;/code&gt;, &lt;code&gt;basename&lt;/code&gt; returns &lt;code&gt;gitlab.cicd.local&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;$SERVICE_NAME&lt;/code&gt; is &lt;code&gt;elk/elasticsearch.cicd.local&lt;/code&gt;, &lt;code&gt;basename&lt;/code&gt; returns &lt;code&gt;elasticsearch.cicd.local&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This allows us to use the full &lt;code&gt;$SERVICE_NAME&lt;/code&gt; to create the &lt;em&gt;directory path&lt;/em&gt; (&lt;code&gt;pki/services/elk/elasticsearch.cicd.local&lt;/code&gt;) but use the clean &lt;code&gt;$CERT_NAME&lt;/code&gt; for all the &lt;em&gt;filenames&lt;/em&gt; (e.g., &lt;code&gt;elasticsearch.cicd.local.key.pem&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Lesson 3: The &lt;code&gt;openssl ca&lt;/code&gt; Command
&lt;/h3&gt;

&lt;p&gt;The final command, &lt;code&gt;openssl ca&lt;/code&gt;, is the "passport office" at work.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# (Inside 02-issue-service-cert.sh)&lt;/span&gt;
openssl ca &lt;span class="nt"&gt;-config&lt;/span&gt; openssl.cnf &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;-extensions&lt;/span&gt; v3_ext &lt;span class="nt"&gt;-extfile&lt;/span&gt; &lt;span class="nv"&gt;$EXT_FILE&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;-days&lt;/span&gt; 365 &lt;span class="nt"&gt;-notext&lt;/span&gt; &lt;span class="nt"&gt;-md&lt;/span&gt; sha256 &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;-passin&lt;/span&gt; pass:&lt;span class="nv"&gt;$CA_PASSWORD&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;-in&lt;/span&gt; &lt;span class="nv"&gt;$CSR_FILE&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;-out&lt;/span&gt; &lt;span class="nv"&gt;$CERT_FILE&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;-batch&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-config openssl.cnf&lt;/code&gt;: "Use our main 'blueprint'."&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-passin pass:$CA_PASSWORD&lt;/code&gt;: "Here is the password for the 'master stamp' (the CA key)."&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-in $CSR_FILE&lt;/code&gt;: "Here is the 'passport application' (the CSR) to be signed."&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-extensions v3_ext -extfile $EXT_FILE&lt;/code&gt;: "Apply the SANs from our temporary file."&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-batch&lt;/code&gt;: "Don't ask me any questions ('Sign this? y/n'). Just approve it."&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-out $CERT_FILE&lt;/code&gt;: "Put the final, signed 'passport' here."&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  4.4 Action Plan: &lt;code&gt;03-issue-all-certs.sh&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Now that we have our powerful, reusable "passport printer" script, we can create a simple automation script to issue "passports" for all 10 of our services.&lt;/p&gt;

&lt;p&gt;Create this file on your &lt;strong&gt;host machine&lt;/strong&gt; at &lt;code&gt;~/cicd_stack/ca/03-issue-all-certs.sh&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="c"&gt;# This script automates the issuance of certificates for all&lt;/span&gt;
&lt;span class="c"&gt;# services in our 10-article CI/CD stack by looping&lt;/span&gt;
&lt;span class="c"&gt;# over an array and calling our '02-issue-service-cert.sh' script.&lt;/span&gt;

&lt;span class="c"&gt;# --- Configuration ---&lt;/span&gt;
&lt;span class="c"&gt;# This array defines the service names. These names will be used&lt;/span&gt;
&lt;span class="c"&gt;# for the certificate Common Name (CN) and the directory path&lt;/span&gt;
&lt;span class="c"&gt;# inside 'pki/services/'.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# Note the nested paths for ELK, which our script handles automatically.&lt;/span&gt;
&lt;span class="nv"&gt;SERVICES&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;
  &lt;span class="s2"&gt;"gitlab.cicd.local"&lt;/span&gt;
  &lt;span class="s2"&gt;"jenkins.cicd.local"&lt;/span&gt;
  &lt;span class="s2"&gt;"artifactory.cicd.local"&lt;/span&gt;
  &lt;span class="s2"&gt;"sonarqube.cicd.local"&lt;/span&gt;
  &lt;span class="s2"&gt;"mattermost.cicd.local"&lt;/span&gt;
  &lt;span class="s2"&gt;"elk/elasticsearch.cicd.local"&lt;/span&gt;
  &lt;span class="s2"&gt;"elk/logstash.cicd.local"&lt;/span&gt;
  &lt;span class="s2"&gt;"elk/kibana.cicd.local"&lt;/span&gt;
  &lt;span class="s2"&gt;"prometheus.cicd.local"&lt;/span&gt;
  &lt;span class="s2"&gt;"grafana.cicd.local"&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nv"&gt;ISSUER_SCRIPT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"./02-issue-service-cert.sh"&lt;/span&gt;

&lt;span class="c"&gt;# --- Pre-run Checks ---&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ISSUER_SCRIPT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: Issuer script '&lt;/span&gt;&lt;span class="nv"&gt;$ISSUER_SCRIPT&lt;/span&gt;&lt;span class="s2"&gt;' not found."&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Please ensure you are in the '~/cicd_stack/ca' directory."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-x&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ISSUER_SCRIPT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: Issuer script '&lt;/span&gt;&lt;span class="nv"&gt;$ISSUER_SCRIPT&lt;/span&gt;&lt;span class="s2"&gt;' is not executable."&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Please run: chmod +x &lt;/span&gt;&lt;span class="nv"&gt;$ISSUER_SCRIPT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"---=== Issuing all service certificates ===---"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"This will use the CA password hardcoded in '&lt;/span&gt;&lt;span class="nv"&gt;$ISSUER_SCRIPT&lt;/span&gt;&lt;span class="s2"&gt;'."&lt;/span&gt;

&lt;span class="c"&gt;# --- Main Loop ---&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;service &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SERVICES&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"------------------------------------------------"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Issuing certificate for: &lt;/span&gt;&lt;span class="nv"&gt;$service&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"------------------------------------------------"&lt;/span&gt;

    &lt;span class="c"&gt;# Call the issuer script with the service name&lt;/span&gt;
    ./&lt;span class="nv"&gt;$ISSUER_SCRIPT&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$service&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt; &lt;span class="nt"&gt;-ne&lt;/span&gt; 0 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: Failed to issue certificate for &lt;/span&gt;&lt;span class="nv"&gt;$service&lt;/span&gt;&lt;span class="s2"&gt;. Aborting."&lt;/span&gt;
        &lt;span class="nb"&gt;exit &lt;/span&gt;1
    &lt;span class="k"&gt;fi
done

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"------------------------------------------------"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"---=== All service certificates issued successfully ===---"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"You can view the new directory structure:"&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-lR&lt;/span&gt; ~/cicd_stack/ca/pki/services/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Action
&lt;/h3&gt;

&lt;p&gt;From your &lt;strong&gt;host machine&lt;/strong&gt; (inside &lt;code&gt;~/cicd_stack/ca&lt;/code&gt;), make both scripts executable, then run the automation script.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# (Run on HOST, inside ~/cicd_stack/ca)&lt;/span&gt;
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x 02-issue-service-cert.sh
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x 03-issue-all-certs.sh

./03-issue-all-certs.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Verification:&lt;/strong&gt;&lt;br&gt;
The script will loop through all 10 services. The final &lt;code&gt;ls -lR&lt;/code&gt; command will display the complete, organized directory structure you've created, with each service's key and certificate in its own isolated folder (e.g., &lt;code&gt;pki/services/gitlab/&lt;/code&gt;, &lt;code&gt;pki/services/elk/elasticsearch/&lt;/code&gt;, etc.).&lt;/p&gt;
&lt;h1&gt;
  
  
  Chapter 5: The &lt;em&gt;New&lt;/em&gt; "Pain Point" - Establishing Trust
&lt;/h1&gt;
&lt;h2&gt;
  
  
  5.1 The "Failure First": The "Untrusted Issuer"
&lt;/h2&gt;

&lt;p&gt;We have successfully built our "passport office" and printed a full set of valid "passports" for all our services. Our &lt;code&gt;pki/services&lt;/code&gt; directory is full of certificates that are cryptographically perfect, have a valid date range, and contain the correct &lt;strong&gt;Subject Alternative Names (SANs)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This leads us to a new, critical "pain point."&lt;/p&gt;

&lt;p&gt;If we were to deploy a service right now using our new &lt;code&gt;gitlab.cicd.local.key.pem&lt;/code&gt; certificate, our browsers would &lt;em&gt;still&lt;/em&gt; show a full-page security warning.&lt;/p&gt;

&lt;p&gt;However, the error would be different. It would no longer be &lt;code&gt;NET::ERR_CERT_COMMON_NAME_INVALID&lt;/code&gt; (which our SANs fixed). Instead, it would be &lt;strong&gt;&lt;code&gt;NET::ERR_CERT_AUTHORITY_INVALID&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The Analogy: The "Untrusted Passport Office"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This new error is a crucial distinction. The browser is no longer complaining about the "passport" itself (&lt;code&gt;gitlab.cicd.local.crt.pem&lt;/code&gt;). It's complaining about the &lt;em&gt;issuer&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The browser is acting as the border agent, looking at our valid "passport" and saying:&lt;/p&gt;

&lt;p&gt;"This passport looks perfectly filled out. The photo matches, the name is correct, and it's not expired. But I've never heard of the 'passport office' (our 'Local CICD Root CA') that issued this. For all I know, you built that office in your garage. I don't trust it."&lt;/p&gt;

&lt;p&gt;We have created a valid certificate from an untrusted authority. Our next and final step is to establish that trust.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  5.2 The "Disjointed Trust Stores" Principle
&lt;/h2&gt;

&lt;p&gt;To solve this new "untrusted" pain point, we must understand a critical, "first principles" concept: there is &lt;strong&gt;no single "trust" button&lt;/strong&gt; on a modern operating system.&lt;/p&gt;

&lt;p&gt;When we ran our &lt;code&gt;04-trust-ca-host.sh&lt;/code&gt; script, we discovered that &lt;code&gt;curl&lt;/code&gt; was fixed, but Chrome and Firefox were not. This is because our "government" (the host OS) is fragmented, and different applications pledge allegiance to different "lists of trusted passport offices" (Trust Stores).&lt;/p&gt;

&lt;p&gt;To fix this, we must &lt;em&gt;manually&lt;/em&gt; add our &lt;code&gt;ca-cert.pem&lt;/code&gt; to several independent trust stores:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;The OpenSSL System Trust Store&lt;/strong&gt;: This is the main list for the operating system, located at &lt;code&gt;/etc/ssl/certs/&lt;/code&gt;. It is used by most command-line tools like &lt;code&gt;curl&lt;/code&gt;, &lt;code&gt;wget&lt;/code&gt;, &lt;code&gt;git&lt;/code&gt;, and &lt;code&gt;apt&lt;/code&gt;. We already fixed this with &lt;code&gt;sudo update-ca-certificates&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The Firefox NSS Trust Store&lt;/strong&gt;: Firefox, by design, ignores the system trust store. It maintains its own private, isolated database (a file named &lt;code&gt;cert9.db&lt;/code&gt;) inside your user's profile directory.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The System-wide NSS Trust Store&lt;/strong&gt;: Google Chrome &lt;em&gt;also&lt;/em&gt; uses the NSS (Network Security Services) database, but it looks at a different, system-wide one (often &lt;code&gt;~/.pki/nssdb/&lt;/code&gt;). This is why fixing the OS store didn't fix Chrome.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The "Java Gotcha" (&lt;code&gt;cacerts&lt;/code&gt;)&lt;/strong&gt;: This is the most important "gotcha" for our stack. The Java Virtual Machine (JVM) &lt;strong&gt;ignores all system trust stores&lt;/strong&gt;. Java has its &lt;em&gt;own&lt;/em&gt; separate, single-file database called &lt;code&gt;cacerts&lt;/code&gt;. When we deploy Jenkins, Artifactory, and SonarQube, we &lt;em&gt;must&lt;/em&gt; manually import our CA into this specific file, or they will all fail with &lt;code&gt;SSLHandshakeException&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Our "Action Plan" must therefore be a multi-pronged attack, targeting each of these trust stores individually.&lt;/p&gt;
&lt;h2&gt;
  
  
  5.3 Action Plan: Trusting Our CA on the Host
&lt;/h2&gt;

&lt;p&gt;Now that we understand the problem, we will execute the solution. Our script &lt;code&gt;04-trust-ca-host.sh&lt;/code&gt; is a precision tool designed to inject our &lt;code&gt;ca.pem&lt;/code&gt; file into all three "disjointed trust stores" on our host machine.&lt;/p&gt;

&lt;p&gt;Here is the code for &lt;code&gt;04-trust-ca-host.sh&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="c"&gt;# This script trusts our Root CA on the host machine.&lt;/span&gt;
&lt;span class="c"&gt;# It will prompt for your password for 'sudo' commands.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# It trusts the CA in THREE places:&lt;/span&gt;
&lt;span class="c"&gt;# 1. The Host OS (for curl, apt, git, etc.)&lt;/span&gt;
&lt;span class="c"&gt;# 2. The system-wide NSS database (for Chrome/Chromium)&lt;/span&gt;
&lt;span class="c"&gt;# 3. The Firefox browser (which uses its own private store)&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Navigating to CA directory ---"&lt;/span&gt;
&lt;span class="c"&gt;# Navigate to the CA directory to find the certs&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/cicd_stack/ca &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: ~/cicd_stack/ca directory not found."&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;exit&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nv"&gt;CA_CERT_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"cicd-stack-ca.crt"&lt;/span&gt;
&lt;span class="nv"&gt;CA_CERT_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"pki/certs/ca.pem"&lt;/span&gt;
&lt;span class="nv"&gt;CA_NICKNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Local CICD Root CA"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CA_CERT_PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: Root CA certificate not found at &lt;/span&gt;&lt;span class="nv"&gt;$CA_CERT_PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# --- 0. Install Prerequisites (NSS Tools) ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Installing 'libnss3-tools' (for certutil) ---"&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update &lt;span class="nt"&gt;-qq&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nt"&gt;-qq&lt;/span&gt; libnss3-tools

&lt;span class="c"&gt;# --- 1. Trust CA in Host OS (for curl, Chrome, ...) ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- 1. Trusting CA in Host OS (OpenSSL store) ---"&lt;/span&gt;
&lt;span class="nb"&gt;sudo cp&lt;/span&gt; &lt;span class="nv"&gt;$CA_CERT_PATH&lt;/span&gt; /usr/local/share/ca-certificates/&lt;span class="nv"&gt;$CA_CERT_NAME&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;update-ca-certificates

&lt;span class="c"&gt;# --- 2. Trust CA in System-wide NSS DB (for Chrome) ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- 2. Trusting CA in NSS DB (for Chrome) ---"&lt;/span&gt;

&lt;span class="c"&gt;# Create the NSS database directory if it doesn't exist&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="nv"&gt;$HOME&lt;/span&gt;/.pki/nssdb

&lt;span class="c"&gt;# Add the certificate to the NSS database&lt;/span&gt;
&lt;span class="c"&gt;# -d sql:$HOME/.pki/nssdb : Specifies the database path&lt;/span&gt;
&lt;span class="c"&gt;# -A : Add a certificate&lt;/span&gt;
&lt;span class="c"&gt;# -n : Nickname&lt;/span&gt;
&lt;span class="c"&gt;# -t "C,T,C" : Set trust attributes (C=SSL Client, T=SSL Server, C=Email)&lt;/span&gt;
certutil &lt;span class="nt"&gt;-A&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CA_NICKNAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="s2"&gt;"C,T,C"&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nv"&gt;$CA_CERT_PATH&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; sql:&lt;span class="nv"&gt;$HOME&lt;/span&gt;/.pki/nssdb

&lt;span class="c"&gt;# --- 3. Trusting CA in Firefox ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- 3. Trusting CA in Firefox (private store) ---"&lt;/span&gt;

&lt;span class="c"&gt;# Find all Firefox profile databases (cert9.db)&lt;/span&gt;
&lt;span class="nv"&gt;FF_PROFILES_DB&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;find &lt;span class="nv"&gt;$HOME&lt;/span&gt;/.mozilla/firefox &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"cert9.db"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$FF_PROFILES_DB&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"WARNING: Could not find Firefox cert9.db. Skipping Firefox trust."&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"You may need to add the CA manually via Firefox settings."&lt;/span&gt;
&lt;span class="k"&gt;else
    for &lt;/span&gt;db &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$FF_PROFILES_DB&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
        &lt;/span&gt;&lt;span class="nv"&gt;PROFILE_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="nv"&gt;$db&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Found Firefox profile, adding CA to: &lt;/span&gt;&lt;span class="nv"&gt;$PROFILE_PATH&lt;/span&gt;&lt;span class="s2"&gt; ---"&lt;/span&gt;

        &lt;span class="c"&gt;# Add the CA to the Firefox NSS database&lt;/span&gt;
        certutil &lt;span class="nt"&gt;-A&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CA_NICKNAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="s2"&gt;"C,T,C"&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nv"&gt;$CA_CERT_PATH&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; sql:&lt;span class="nv"&gt;$PROFILE_PATH&lt;/span&gt;
    &lt;span class="k"&gt;done
fi

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Host trust setup complete ---"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"IMPORTANT: Please restart Google Chrome for changes to take effect."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deconstruction
&lt;/h3&gt;

&lt;p&gt;This script is a perfect example of our "first principles" approach, as it surgically targets each trust store:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Step 0: Install &lt;code&gt;libnss3-tools&lt;/code&gt;&lt;/strong&gt;: We first install the &lt;code&gt;certutil&lt;/code&gt; command, which is the "first principles" tool for managing both the Chrome and Firefox NSS databases.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step 1: Host OS (OpenSSL)&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;sudo cp ... /usr/local/share/ca-certificates/&lt;/code&gt;: This copies our "passport office license" into the system's "pending applications" directory.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sudo update-ca-certificates&lt;/code&gt;: This is the system command that processes that directory, rebuilds the master trust file (&lt;code&gt;/etc/ssl/certs/ca-certificates.crt&lt;/code&gt;), and officially trusts our CA. This fixes &lt;code&gt;curl&lt;/code&gt;, &lt;code&gt;git&lt;/code&gt;, &lt;code&gt;apt&lt;/code&gt;, and other system tools.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Step 2: Chrome (NSS System Store)&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;mkdir -p $HOME/.pki/nssdb&lt;/code&gt;: We ensure the "filing cabinet" for Chrome's trust store exists.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;certutil -A ... -d sql:$HOME/.pki/nssdb&lt;/code&gt;: We use &lt;code&gt;certutil&lt;/code&gt; to add our CA to this &lt;em&gt;specific&lt;/em&gt; database, fixing the &lt;code&gt;net::ERR_CERT_AUTHORITY_INVALID&lt;/code&gt; error in Chrome.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Step 3: Firefox (NSS Profile Store)&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;find $HOME/.mozilla/firefox -name "cert9.db"&lt;/code&gt;: We find the &lt;em&gt;separate, private&lt;/em&gt; "filing cabinet" that Firefox maintains for your user profile.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;certutil -A ... -d sql:$PROFILE_PATH&lt;/code&gt;: We run the &lt;em&gt;same&lt;/em&gt; &lt;code&gt;certutil&lt;/code&gt; command, but this time we target the Firefox-specific database path, fixing trust for Firefox.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Action
&lt;/h3&gt;

&lt;p&gt;From your &lt;strong&gt;host machine&lt;/strong&gt; (inside your &lt;code&gt;0006_cicd_part02_certificate_authority&lt;/code&gt; directory), make the script executable and run it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# (Run on HOST)&lt;/span&gt;
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x 04-trust-ca-host.sh
./04-trust-ca-host.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt;&lt;br&gt;
The script will prompt for your &lt;code&gt;sudo&lt;/code&gt; password, then proceed to install the tools and update all three trust stores. You will see output like "1 added, 0 removed; done." from &lt;code&gt;update-ca-certificates&lt;/code&gt; and confirmation from &lt;code&gt;certutil&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  5.4 Action Plan: Trusting the "Control Center" (&lt;code&gt;dev-container&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;We have successfully taught our &lt;strong&gt;host machine&lt;/strong&gt; to trust our CA. Now we must do the same for our &lt;strong&gt;&lt;code&gt;dev-container&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This is a critical step. Our &lt;code&gt;dev-container&lt;/code&gt; is our "Control Center." Its tools (&lt;code&gt;curl&lt;/code&gt;, &lt;code&gt;git&lt;/code&gt;, and most importantly, our future Python API scripts) must be able to make secure HTTPS connections to our new services. If our container doesn't trust our CA, our entire automation plan will fail with the same &lt;code&gt;SSLHandshakeException&lt;/code&gt; and &lt;code&gt;certificate problem&lt;/code&gt; errors.&lt;/p&gt;
&lt;h3&gt;
  
  
  The "No-Rebuild" Solution
&lt;/h3&gt;

&lt;p&gt;We will use the flexible architecture we built in Article 1 to solve this without rebuilding our container image.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;The &lt;code&gt;data/&lt;/code&gt; Mount&lt;/strong&gt;: Our &lt;code&gt;dev-container.sh&lt;/code&gt; script mounts our host's &lt;code&gt;data/&lt;/code&gt; directory into the container.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The &lt;code&gt;entrypoint.sh&lt;/code&gt; Mount&lt;/strong&gt;: Our &lt;code&gt;dev-container.sh&lt;/code&gt; script &lt;em&gt;also&lt;/em&gt; mounts our host's &lt;code&gt;entrypoint.sh&lt;/code&gt; script, which runs &lt;em&gt;every time&lt;/em&gt; the container starts.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We will copy our public CA certificate into the host's &lt;code&gt;data/&lt;/code&gt; directory. Then, we will edit our &lt;code&gt;entrypoint.sh&lt;/code&gt; script to automatically install this certificate &lt;em&gt;at startup&lt;/em&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Action 1: Copy the CA Certificate
&lt;/h3&gt;

&lt;p&gt;From your &lt;strong&gt;host machine&lt;/strong&gt;, copy your public "business license" (&lt;code&gt;ca.pem&lt;/code&gt;) into the &lt;code&gt;data/&lt;/code&gt; directory of your &lt;code&gt;FromFirstPrinciples&lt;/code&gt; project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# (Run on HOST, from your 0006_cicd... directory)&lt;/span&gt;
&lt;span class="c"&gt;# This copies our CA cert into the shared data folder&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; ~/cicd_stack/ca/pki/certs/ca.pem ~/Documents/FromFirstPrinciples/data/ca-cert.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Action 2: Edit &lt;code&gt;entrypoint.sh&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Now, open your &lt;code&gt;entrypoint.sh&lt;/code&gt; file (in your &lt;code&gt;FromFirstPrinciples&lt;/code&gt; directory) with your text editor. Add the following block of code directly after the &lt;code&gt;sudo service ssh restart&lt;/code&gt; line.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;service ssh restart

&lt;span class="c"&gt;# --- ADD THIS BLOCK TO TRUST THE LOCAL CA ---&lt;/span&gt;
&lt;span class="nv"&gt;CA_CERT_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"data/ca-cert.pem"&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CA_CERT_PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Found Local CA certificate, installing to system trust store ---"&lt;/span&gt;
    &lt;span class="c"&gt;# Copy the CA cert into the system's trust store&lt;/span&gt;
    &lt;span class="nb"&gt;sudo cp&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CA_CERT_PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; /usr/local/share/ca-certificates/cicd-stack-ca.crt
    &lt;span class="c"&gt;# Update the system's CA list&lt;/span&gt;
    &lt;span class="nb"&gt;sudo &lt;/span&gt;update-ca-certificates
&lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- No Local CA certificate found at &lt;/span&gt;&lt;span class="nv"&gt;$CA_CERT_PATH&lt;/span&gt;&lt;span class="s2"&gt;, skipping system trust ---"&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="c"&gt;# --- END OF BLOCK ---&lt;/span&gt;

&lt;span class="c"&gt;# Check for GPG key and gitconfig on the persistent data volume&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; data/private.pgp &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;gpg &lt;span class="nt"&gt;--import&lt;/span&gt; data/private.pgp
&lt;span class="k"&gt;fi&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(The rest of your &lt;code&gt;entrypoint.sh&lt;/code&gt; file remains the same.)&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Deconstruction
&lt;/h3&gt;

&lt;p&gt;This logic is simple and powerful:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; It checks if the &lt;code&gt;data/ca-cert.pem&lt;/code&gt; file (which we just copied) is present.&lt;/li&gt;
&lt;li&gt; If it is, it copies it into the container's system trust directory (&lt;code&gt;/usr/local/share/ca-certificates/&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt; It runs &lt;code&gt;sudo update-ca-certificates&lt;/code&gt; &lt;em&gt;inside the container&lt;/em&gt; to make its system tools (like &lt;code&gt;curl&lt;/code&gt; and &lt;code&gt;git&lt;/code&gt;) trust our CA.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Action 3: Recreate the &lt;code&gt;dev-container&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Because we just changed the &lt;code&gt;entrypoint.sh&lt;/code&gt; script (which is bind-mounted), we don't need to &lt;em&gt;rebuild&lt;/em&gt; the image, but we do need to &lt;em&gt;recreate&lt;/em&gt; the container to run the new script.&lt;/p&gt;

&lt;p&gt;As we've noted, your &lt;code&gt;entrypoint.sh&lt;/code&gt; script also handles your GPG key, which requires an interactive prompt. Therefore, we must use our &lt;code&gt;docker stop/rm&lt;/code&gt; and &lt;code&gt;./dev-container.sh&lt;/code&gt; flow.&lt;/p&gt;

&lt;p&gt;From your &lt;strong&gt;host machine&lt;/strong&gt; (in the &lt;code&gt;FromFirstPrinciples&lt;/code&gt; directory):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# (Run on HOST)&lt;/span&gt;
&lt;span class="c"&gt;# 1. Stop and remove the old container&lt;/span&gt;
docker stop dev-container &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; docker &lt;span class="nb"&gt;rm &lt;/span&gt;dev-container

&lt;span class="c"&gt;# 2. Run the new container (which will use the modified entrypoint)&lt;/span&gt;
./dev-container.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt;&lt;br&gt;
As the container starts, you will see our new messages in the log output, culminating in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;--- Found Local CA certificate, installing to system trust store ---
Updating certificates in /etc/ssl/certs...
1 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d...
done.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This confirms our "Control Center" now also trusts our CA.&lt;/p&gt;

&lt;h2&gt;
  
  
  5.5 Verification: The "Success Second" (Python HTTPS Server)
&lt;/h2&gt;

&lt;p&gt;We have now executed three complex, "first principles" operations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; We created a Root CA and a full set of service certificates, solving the "SAN Gotcha" and "Nested Path Gotcha."&lt;/li&gt;
&lt;li&gt; We manually configured our &lt;strong&gt;Host OS&lt;/strong&gt; to trust our CA, fixing &lt;code&gt;curl&lt;/code&gt;, Chrome, and Firefox.&lt;/li&gt;
&lt;li&gt; We configured our &lt;strong&gt;&lt;code&gt;dev-container&lt;/code&gt;&lt;/strong&gt; to trust our CA by modifying its &lt;code&gt;entrypoint.sh&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The plan is complete, but we must &lt;em&gt;verify&lt;/em&gt; it. We will now run a simple, local HTTPS server &lt;em&gt;on our host machine&lt;/em&gt; using one of the "passports" we just printed (our &lt;code&gt;gitlab.cicd.local&lt;/code&gt; certificate). We will then test if our browsers and our &lt;code&gt;dev-container&lt;/code&gt; trust it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Action 1: The Python Test Server
&lt;/h3&gt;

&lt;p&gt;Here is the code for &lt;code&gt;05-verify-trust.py&lt;/code&gt;. This script is a simple, standard-library-only HTTPS server.&lt;/p&gt;

&lt;p&gt;Create this file on your &lt;strong&gt;host machine&lt;/strong&gt; at &lt;code&gt;~/Documents/FromFirstPrinciples/articles/0006_cicd_part02_certificate_authority/05-verify-trust.py&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env python3
&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;http.server&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ssl&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;

&lt;span class="c1"&gt;# --- Configuration ---
# This is the "simple name" of the certificate we want to use.
# It must match one of the subdirectories we created in pki/services/
# We will use 'gitlab' as our test certificate.
&lt;/span&gt;&lt;span class="n"&gt;CERT_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gitlab.cicd.local&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# Find the '~/cicd_stack/ca' directory from the user's home
&lt;/span&gt;&lt;span class="n"&gt;HOME_DIR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;home&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;CA_DIR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HOME_DIR&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cicd_stack&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ca&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;PKI_DIR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CA_DIR&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pki&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;CERT_DIR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PKI_DIR&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;services&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;CERT_NAME&lt;/span&gt;

&lt;span class="c1"&gt;# Paths to the certificate and private key files
&lt;/span&gt;&lt;span class="n"&gt;CERT_FILE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CERT_DIR&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;CERT_NAME&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.crt.pem&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;KEY_FILE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CERT_DIR&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;CERT_NAME&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.key.pem&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# Server configuration
&lt;/span&gt;&lt;span class="n"&gt;HOST&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;localhost&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8443&lt;/span&gt;

&lt;span class="c1"&gt;# --- Validation ---
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;PKI_DIR&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--- ERROR ---&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Could not find CA directory at: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;CA_DIR&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Please ensure you have run &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;01-create-ca.sh&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; successfully.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;CERT_FILE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;KEY_FILE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--- ERROR ---&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Could not find certificate files for &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;CERT_NAME&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Expected cert at: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;CERT_FILE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Expected key at:  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;KEY_FILE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Please run &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;./03-issue-all-certs.sh&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; first.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# --- Server Setup ---
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--- Starting HTTPS server on https://&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;HOST&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;PORT&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; ---&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Loading certificate: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;CERT_FILE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Loading private key: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;KEY_FILE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Create a standard TCP server
&lt;/span&gt;&lt;span class="n"&gt;httpd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;HTTPServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HOST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SimpleHTTPRequestHandler&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Create an SSL context
&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SSLContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PROTOCOL_TLS_SERVER&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load_cert_chain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;certfile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;CERT_FILE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;keyfile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;KEY_FILE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Wrap the server socket with our new SSL context
&lt;/span&gt;&lt;span class="n"&gt;httpd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wrap_socket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;httpd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;server_side&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--- Server is running. Open https://&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;HOST&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;PORT&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; in your browser. ---&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Press Ctrl+C to stop.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;httpd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;serve_forever&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;KeyboardInterrupt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Server stopped. ---&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deconstruction
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;CERT_DIR = HOME_DIR / "cicd_stack" / "ca" ...&lt;/code&gt;: This Python code robustly finds the certificates we created by looking in our &lt;code&gt;~/cicd_stack/ca&lt;/code&gt; directory.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;context = ssl.SSLContext(...)&lt;/code&gt;: This creates the secure SSL/TLS context for our server.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;context.load_cert_chain(...)&lt;/code&gt;: This is the key line. It loads our service "passport" (&lt;code&gt;gitlab.cicd.local.crt.pem&lt;/code&gt;) and its corresponding "passport key" (&lt;code&gt;gitlab.cicd.local.key.pem&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;httpd.socket = context.wrap_socket(...)&lt;/code&gt;: This "wraps" our standard HTTP server in the secure SSL context, upgrading it to HTTPS.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Action 2: Run the Server and Verify
&lt;/h3&gt;

&lt;p&gt;From your &lt;strong&gt;host machine&lt;/strong&gt; (inside your &lt;code&gt;0006_cicd_part02_certificate_authority&lt;/code&gt; directory), make the script executable and run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# (Run on HOST)&lt;/span&gt;
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x 05-verify-trust.py
./05-verify-trust.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server is now running on &lt;code&gt;https://localhost:8443&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Verification Step 1: Browsers (Host)
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt; Open &lt;strong&gt;Google Chrome&lt;/strong&gt; (in an Incognito window to be safe) and go to &lt;code&gt;https://localhost:8443&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; Open &lt;strong&gt;Firefox&lt;/strong&gt; and go to &lt;code&gt;https://localhost:8443&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Result&lt;/strong&gt;: Both browsers should &lt;strong&gt;show a secure padlock icon 🔒&lt;/strong&gt;. They will &lt;em&gt;not&lt;/em&gt; show a "Your connection is not private" warning. This proves that our &lt;code&gt;04-trust-ca-host.sh&lt;/code&gt; script worked and successfully configured all three trust stores.&lt;/p&gt;

&lt;h3&gt;
  
  
  Verification Step 2: &lt;code&gt;curl&lt;/code&gt; (Host)
&lt;/h3&gt;

&lt;p&gt;Open a &lt;em&gt;new&lt;/em&gt; host terminal (leaving the server running) and test with &lt;code&gt;curl&lt;/code&gt; using both the name and the IP address.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# (Run on HOST)&lt;/span&gt;
&lt;span class="c"&gt;# Test 1: By hostname&lt;/span&gt;
curl https://localhost:8443

&lt;span class="c"&gt;# Test 2: By IP address&lt;/span&gt;
curl https://127.0.0.1:8443
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result&lt;/strong&gt;: Both commands will succeed and print the HTML for a directory listing. They will &lt;em&gt;not&lt;/em&gt; fail with an "SSL certificate problem." This proves our host's OpenSSL trust store is fixed &lt;em&gt;and&lt;/em&gt; our SAN &lt;code&gt;IP:127.0.0.1&lt;/code&gt; entry is working.&lt;/p&gt;

&lt;h3&gt;
  
  
  Verification Step 3: &lt;code&gt;curl&lt;/code&gt; (Container)
&lt;/h3&gt;

&lt;p&gt;This is the final and most important test. We must prove that our &lt;code&gt;dev-container&lt;/code&gt;—our "Control Center"—also trusts our new CA. We will do this by running our Python test server &lt;em&gt;inside&lt;/em&gt; the container and then, from a &lt;em&gt;second&lt;/em&gt; container shell, trying to &lt;code&gt;curl&lt;/code&gt; it.&lt;/p&gt;

&lt;p&gt;This is a perfect, hermetic test of the container's internal trust store.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Action 1 (Host): Copy Certs into the "Dropbox"&lt;/strong&gt;&lt;br&gt;
Our test server, &lt;code&gt;05-verify-trust.py&lt;/code&gt;, is configured to find certificates at &lt;code&gt;~/cicd_stack/ca/pki/services/...&lt;/code&gt;. This path doesn't exist inside our container, so we must first get the &lt;code&gt;gitlab&lt;/code&gt; certificate folder into the container. We will use our mounted &lt;code&gt;data/&lt;/code&gt; directory as the "dropbox."&lt;/p&gt;

&lt;p&gt;From your &lt;strong&gt;host machine&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# (Run on HOST)&lt;/span&gt;
&lt;span class="c"&gt;# Copy the gitlab certs/key folder into the 'data' directory&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; ~/cicd_stack/ca/pki/services/gitlab.cicd.local/ ~/Documents/FromFirstPrinciples/data/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Action 2 (Container): Run the Server&lt;/strong&gt;&lt;br&gt;
Now, we'll enter our &lt;code&gt;dev-container&lt;/code&gt; and set up the test. This requires two terminals.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Terminal 1 (dev-container shell 1):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# (Run on HOST)&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; dev-container bash

&lt;span class="c"&gt;# (Inside dev-container - Terminal 1)&lt;/span&gt;
&lt;span class="c"&gt;# 1. Re-create the path structure the script expects&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/cicd_stack/ca/pki/services/

&lt;span class="c"&gt;# 2. Copy the certs from our 'dropbox' to the expected path&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; ~/data/gitlab.cicd.local/ ~/cicd_stack/ca/pki/services/

&lt;span class="c"&gt;# 3. Navigate to the article directory&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/articles/0006_cicd_part02_certificate_authority/

&lt;span class="c"&gt;# 4. Run the Python server&lt;/span&gt;
python3 05-verify-trust.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt;&lt;br&gt;
The server will start successfully inside the container, as it can now find its certificates.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;--- Starting HTTPS server on https://localhost:8443 ---
Loading certificate: /home/warren_jitsing/cicd_stack/ca/pki/services/gitlab.cicd.local/gitlab.cicd.local.crt.pem
Loading private key: /home/warren_jitsing/cicd_stack/ca/pki/services/gitlab.cicd.local/gitlab.cicd.local.key.pem
--- Server is running. Open https://localhost:8443 in your browser. ---
Press Ctrl+C to stop.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Terminal 2 (dev-container shell 2):&lt;/strong&gt;&lt;br&gt;
While the server is running in the first terminal, open a &lt;em&gt;second&lt;/em&gt; host terminal and get another shell inside the &lt;code&gt;dev-container&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# (Run on HOST - Terminal 2)&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; dev-container bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, from this second shell, we will run &lt;code&gt;curl&lt;/code&gt; to connect to the server running in our &lt;em&gt;first&lt;/em&gt; shell.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# (Inside dev-container - Terminal 2)&lt;/span&gt;
&lt;span class="c"&gt;# 5. Test connection to localhost&lt;/span&gt;
curl https://localhost:8443

&lt;span class="c"&gt;# 6. Test connection to 127.0.0.1&lt;/span&gt;
curl https://127.0.0.1:8443
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt;&lt;br&gt;
Both commands will succeed, returning the HTML of the directory listing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;br&gt;
This is the ultimate verification. &lt;code&gt;curl&lt;/code&gt; (running inside the container) is using the container's system trust store. This test proves that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Our &lt;code&gt;entrypoint.sh&lt;/code&gt; script successfully ran at startup.&lt;/li&gt;
&lt;li&gt; It correctly found &lt;code&gt;data/ca-cert.pem&lt;/code&gt; and ran &lt;code&gt;sudo update-ca-certificates&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; The container's trust store is now fixed, and all tools inside our "Control Center" (like &lt;code&gt;curl&lt;/code&gt;, &lt;code&gt;git&lt;/code&gt;, and our future Python scripts) will fully trust our new CA.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Cleanup (Terminal 1):&lt;/strong&gt;&lt;br&gt;
You can now stop the Python server with &lt;code&gt;Ctrl+C&lt;/code&gt; and remove the temporary certificate directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# (Inside dev-container - Terminal 1)&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; ~/cicd_stack/
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; ~/data/gitlab.cicd.local/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Chapter 6: The "Easy Way" - Automating with &lt;code&gt;easyrsa&lt;/code&gt;
&lt;/h1&gt;

&lt;h2&gt;
  
  
  6.1 The "Why": Appreciating Automation
&lt;/h2&gt;

&lt;p&gt;In the previous chapters, we built a fully functional, secure Certificate Authority "the hard way." We meticulously crafted our &lt;code&gt;openssl.cnf&lt;/code&gt;, built our PKI directory structure, and manually managed our "ledger" (&lt;code&gt;index.txt&lt;/code&gt;) and "ticket dispenser" (&lt;code&gt;serial&lt;/code&gt;). We deconstructed the complex &lt;code&gt;openssl ca&lt;/code&gt; command and learned, through failure, that we must manually build and pass a &lt;code&gt;v3.ext&lt;/code&gt; file to handle the critical &lt;strong&gt;Subject Alternative Name (SAN)&lt;/strong&gt; requirement.&lt;/p&gt;

&lt;p&gt;This was an essential "first principles" exercise. Now that we have done the hard work and understand every moving part, we can appreciate the "easy button."&lt;/p&gt;

&lt;p&gt;We will now introduce &lt;strong&gt;&lt;code&gt;easyrsa&lt;/code&gt;&lt;/strong&gt;, a tool whose sole purpose is to automate every tedious step we just learned.&lt;/p&gt;

&lt;h2&gt;
  
  
  6.2 The "What": &lt;code&gt;easyrsa&lt;/code&gt; is a Wrapper
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;easyrsa&lt;/code&gt; is not a &lt;em&gt;replacement&lt;/em&gt; for &lt;code&gt;openssl&lt;/code&gt;. It is a &lt;strong&gt;shell script wrapper&lt;/strong&gt; &lt;em&gt;around&lt;/em&gt; &lt;code&gt;openssl&lt;/code&gt;. It was developed by the OpenVPN project to simplify the creation of a PKI.&lt;/p&gt;

&lt;p&gt;It is our "automated passport office clerk." It takes our simple, high-level commands (like "make a CA" or "make a server certificate") and, behind the scenes, runs the same complex &lt;code&gt;openssl&lt;/code&gt; commands we just did, automatically managing the &lt;code&gt;index.txt&lt;/code&gt;, &lt;code&gt;serial&lt;/code&gt; file, and configuration templates for us.&lt;/p&gt;

&lt;p&gt;We will now run a self-contained demo to prove this.&lt;/p&gt;

&lt;h2&gt;
  
  
  6.3 Action Plan: &lt;code&gt;06-run-easyrsa-demo.sh&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Here is the code for &lt;code&gt;06-run-easyrsa-demo.sh&lt;/code&gt;. This script will install &lt;code&gt;easyrsa&lt;/code&gt; by cloning its Git repository (as it is not available in the default Debian repositories), and then use it to build a &lt;em&gt;second&lt;/em&gt;, separate "demo" CA.&lt;/p&gt;

&lt;p&gt;Create this file on your &lt;strong&gt;host machine&lt;/strong&gt; at &lt;code&gt;~/Documents/FromFirstPrinciples/articles/0006_cicd_part02_certificate_authority/06-run-easyrsa-demo.sh&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="c"&gt;# This script demonstrates the "easy way" to create a CA&lt;/span&gt;
&lt;span class="c"&gt;# and issue a certificate using the 'easyrsa' wrapper.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# It is for demonstration only and does not use our&lt;/span&gt;
&lt;span class="c"&gt;# main '~/cicd_stack/ca' directory.&lt;/span&gt;

&lt;span class="nv"&gt;DEMO_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"easyrsa-demo"&lt;/span&gt;
&lt;span class="nv"&gt;REPO_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://github.com/OpenVPN/easy-rsa.git"&lt;/span&gt;
&lt;span class="nv"&gt;REPO_BRANCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"v3.2.4"&lt;/span&gt;
&lt;span class="nv"&gt;SERVICE_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"gitlab-demo"&lt;/span&gt;

&lt;span class="c"&gt;# --- 1. Installation (via Git) ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- 1. Installing 'git' (prerequisite) ---"&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update &lt;span class="nt"&gt;-qq&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nt"&gt;-qq&lt;/span&gt; git

&lt;span class="c"&gt;# --- 2. Setup ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- 2. Cloning easyrsa branch '&lt;/span&gt;&lt;span class="nv"&gt;$REPO_BRANCH&lt;/span&gt;&lt;span class="s2"&gt;' into demo directory: &lt;/span&gt;&lt;span class="nv"&gt;$DEMO_DIR&lt;/span&gt;&lt;span class="s2"&gt; ---"&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; &lt;span class="nv"&gt;$DEMO_DIR&lt;/span&gt;
git clone &lt;span class="nt"&gt;--depth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nt"&gt;-b&lt;/span&gt; &lt;span class="nv"&gt;$REPO_BRANCH&lt;/span&gt; &lt;span class="nv"&gt;$REPO_URL&lt;/span&gt; &lt;span class="nv"&gt;$DEMO_DIR&lt;/span&gt;

&lt;span class="c"&gt;# Navigate into the new demo directory&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="nv"&gt;$DEMO_DIR&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Failed to clone easyrsa. Aborting."&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Navigate into the easyrsa3 directory where the executable is&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;easyrsa3 &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Failed to find 'easyrsa3' directory. Aborting."&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1&lt;span class="o"&gt;)&lt;/span&gt;


&lt;span class="c"&gt;# --- 3. Create the PKI and Root CA ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- 3. Initializing PKI and building Root CA ---"&lt;/span&gt;

&lt;span class="c"&gt;# Initialize the PKI (creates 'pki' dir, index, serial, etc.)&lt;/span&gt;
./easyrsa init-pki

&lt;span class="c"&gt;# Build the CA, 'nopass' = no password on the private key&lt;/span&gt;
&lt;span class="c"&gt;# 'batch' = use defaults for the DN (Country, Org, etc.)&lt;/span&gt;
./easyrsa &lt;span class="nt"&gt;--batch&lt;/span&gt; build-ca nopass

&lt;span class="c"&gt;# --- 4. Issue a Service Certificate ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- 4. Issuing service certificate for '&lt;/span&gt;&lt;span class="nv"&gt;$SERVICE_NAME&lt;/span&gt;&lt;span class="s2"&gt;' (with SANs) ---"&lt;/span&gt;

&lt;span class="c"&gt;# We MUST explicitly pass the SANs. We will add the service name,&lt;/span&gt;
&lt;span class="c"&gt;# localhost, and 127.0.0.1, just like our "hard way" script.&lt;/span&gt;
&lt;span class="nv"&gt;SANS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"DNS:&lt;/span&gt;&lt;span class="nv"&gt;$SERVICE_NAME&lt;/span&gt;&lt;span class="s2"&gt;,DNS:localhost,IP:127.0.0.1"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Using SANs: &lt;/span&gt;&lt;span class="nv"&gt;$SANS&lt;/span&gt;&lt;span class="s2"&gt; ---"&lt;/span&gt;
./easyrsa &lt;span class="nt"&gt;--batch&lt;/span&gt; &lt;span class="nt"&gt;--san&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SANS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; build-server-full &lt;span class="nv"&gt;$SERVICE_NAME&lt;/span&gt; nopass

&lt;span class="c"&gt;# --- 5. Verification ---&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Demo complete. Verifying generated files: ---"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"1. CA Certificate:"&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; pki/ca.crt

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"2. Service Private Key:"&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; pki/private/&lt;span class="nv"&gt;$SERVICE_NAME&lt;/span&gt;.key

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"3. Service Certificate:"&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; pki/issued/&lt;span class="nv"&gt;$SERVICE_NAME&lt;/span&gt;.crt

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"--- Verifying SANs in the 'easyrsa' generated cert: ---"&lt;/span&gt;
openssl x509 &lt;span class="nt"&gt;-in&lt;/span&gt; pki/issued/&lt;span class="nv"&gt;$SERVICE_NAME&lt;/span&gt;.crt &lt;span class="nt"&gt;-noout&lt;/span&gt; &lt;span class="nt"&gt;-text&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-A1&lt;/span&gt; &lt;span class="s2"&gt;"Subject Alternative Name"&lt;/span&gt;

&lt;span class="c"&gt;# Clean up by returning to the original directory&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; ../..
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deconstruction
&lt;/h3&gt;

&lt;p&gt;This script automates the entire process, perfectly illustrating &lt;em&gt;why&lt;/em&gt; &lt;code&gt;openssl&lt;/code&gt; is so complex.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;git clone ...&lt;/code&gt;&lt;/strong&gt;: First, we install &lt;code&gt;git&lt;/code&gt; and clone the &lt;code&gt;easyrsa&lt;/code&gt; repository, as it's not available in the default Debian package manager. We check out a specific, stable version (&lt;code&gt;v3.2.4&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;./easyrsa init-pki&lt;/code&gt;&lt;/strong&gt;: This single command does what took us several steps in Chapter 3. It creates the &lt;em&gt;entire&lt;/em&gt; PKI directory structure (&lt;code&gt;pki/&lt;/code&gt;), including the &lt;code&gt;index.txt&lt;/code&gt; "ledger" and the &lt;code&gt;serial&lt;/code&gt; "ticket dispenser".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;./easyrsa --batch build-ca nopass&lt;/code&gt;&lt;/strong&gt;: This one command replaces our &lt;code&gt;01-create-ca.sh&lt;/code&gt; script. It generates the &lt;code&gt;ca.key&lt;/code&gt; and &lt;code&gt;ca.pem&lt;/code&gt;, automatically applying the correct &lt;code&gt;CA:TRUE&lt;/code&gt; extensions from its own internal &lt;code&gt;openssl.cnf&lt;/code&gt; template.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;./easyrsa --batch --san="..." build-server-full ...&lt;/code&gt;&lt;/strong&gt;: This is the most powerful command. It replaces our entire &lt;code&gt;02-issue-service-cert.sh&lt;/code&gt; script. It automatically handles the key generation, CSR, and signing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The &lt;code&gt;--san&lt;/code&gt; Flag&lt;/strong&gt;: This is the most critical lesson. Even this "easy" tool, when run in non-interactive &lt;code&gt;batch&lt;/code&gt; mode, &lt;em&gt;still&lt;/em&gt; required us to &lt;strong&gt;explicitly pass the SANs&lt;/strong&gt;. This proves that our "first principles" lesson was correct: in modern PKI, SANs are non-negotiable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Final Verification&lt;/strong&gt;: We use our "hard way" &lt;code&gt;openssl&lt;/code&gt; command at the end to "check the work" of the "easy way" wrapper. The output &lt;code&gt;DNS:gitlab-demo, DNS:localhost, IP Address:127.0.0.1&lt;/code&gt; proves that &lt;code&gt;easyrsa&lt;/code&gt; is just a powerful, convenient shell script running the same &lt;code&gt;openssl&lt;/code&gt; logic we already learned.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Action
&lt;/h3&gt;

&lt;p&gt;From your &lt;strong&gt;host machine&lt;/strong&gt; (inside your &lt;code&gt;0006_cicd_part02_certificate_authority&lt;/code&gt; directory), make this new script executable and run it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# (Run on HOST)&lt;/span&gt;
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x 06-run-easyrsa-demo.sh
./06-run-easyrsa-demo.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt;&lt;br&gt;
The script will install &lt;code&gt;git&lt;/code&gt;, clone the &lt;code&gt;easyrsa&lt;/code&gt; repository, and then run the simplified workflow. The final &lt;code&gt;openssl&lt;/code&gt; command will print the SAN field, proving that &lt;code&gt;easyrsa&lt;/code&gt; is just an automated wrapper around the same "first principles" we learned.&lt;/p&gt;

&lt;h1&gt;
  
  
  Chapter 7: Conclusion and Next Steps
&lt;/h1&gt;

&lt;h2&gt;
  
  
  7.1 What We've Built
&lt;/h2&gt;

&lt;p&gt;We have successfully built a complete, local Public Key Infrastructure (PKI) from "first principles." We started with the "pain point" of insecure &lt;code&gt;http&lt;/code&gt; and browser errors and deconstructed the entire chain of trust.&lt;/p&gt;

&lt;p&gt;We rejected the "magic" of simple, self-signed certificates and instead built our own "passport office"—a full Certificate Authority. We did it the "hard way" with &lt;code&gt;openssl&lt;/code&gt; to understand every moving part:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We created a secure &lt;code&gt;openssl.cnf&lt;/code&gt; "blueprint."&lt;/li&gt;
&lt;li&gt;We manually built the CA's "filing system" (&lt;code&gt;index.txt&lt;/code&gt;, &lt;code&gt;serial&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;We solved the critical "SAN Gotcha" by creating a &lt;code&gt;v3.ext&lt;/code&gt; file to issue modern, compliant certificates.&lt;/li&gt;
&lt;li&gt;We solved the "Nested Path Gotcha" using &lt;code&gt;basename&lt;/code&gt; to make our issuer script reusable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We then solved the "new pain point" of trust by deconstructing the "disjointed trust stores" on our host. We surgically added our CA to the &lt;strong&gt;OpenSSL store&lt;/strong&gt; (for &lt;code&gt;curl&lt;/code&gt;), the &lt;strong&gt;NSS store&lt;/strong&gt; (for Chrome), and the &lt;strong&gt;Firefox store&lt;/strong&gt; (for Firefox) using our &lt;code&gt;04-trust-ca-host.sh&lt;/code&gt; script.&lt;/p&gt;

&lt;p&gt;Finally, we verified our entire setup end-to-end, proving that both our host machine and our &lt;code&gt;dev-container&lt;/code&gt; now fully trust our new "passport office."&lt;/p&gt;

&lt;h2&gt;
  
  
  7.2 Next Steps
&lt;/h2&gt;

&lt;p&gt;Our "city" is finally ready.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We have a "Control Center" (DooD).&lt;/li&gt;
&lt;li&gt;We have our "roads" (&lt;code&gt;cicd-net&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;We have our "foundations" (volumes and bind mounts).&lt;/li&gt;
&lt;li&gt;And now, we have our "security system" (our trusted CA).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We are &lt;em&gt;finally&lt;/em&gt; ready to build our first "skyscraper." In the next article, we will deploy &lt;strong&gt;GitLab&lt;/strong&gt;, the "Central Library" for our source code. We will use the &lt;code&gt;docker run&lt;/code&gt; command to deploy it onto our foundation and use the &lt;code&gt;gitlab.crt.pem&lt;/code&gt; and &lt;code&gt;gitlab.key.pem&lt;/code&gt; files we just created to make it fully secure with HTTPS from the moment it starts.&lt;/p&gt;

</description>
      <category>security</category>
      <category>devops</category>
      <category>containers</category>
      <category>tls</category>
    </item>
  </channel>
</rss>
