<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[WebSessionForge]]></title><description><![CDATA[WebSessionForge]]></description><link>https://websessionforge.hashnode.dev</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1593680282896/kNC7E8IR4.png</url><title>WebSessionForge</title><link>https://websessionforge.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Wed, 07 Oct 2026 07:09:38 GMT</lastBuildDate><atom:link href="https://websessionforge.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Building WebSessionForge: A Multi-Session WebView2 Browser & Proxy Testing Platform in .NET 8]]></title><description><![CDATA[C# · .NET 8 · WPF · Microsoft Edge WebView2 · Proxy Management · Browser Testing
I wanted to build something that would let me experiment with multiple isolated browser sessions, while also giving me ]]></description><link>https://websessionforge.hashnode.dev/websessionforge-a-multi-session-webview2-browser-proxy-testing-platform-in-net-8</link><guid isPermaLink="true">https://websessionforge.hashnode.dev/websessionforge-a-multi-session-webview2-browser-proxy-testing-platform-in-net-8</guid><category><![CDATA[C#]]></category><category><![CDATA[dotnet]]></category><category><![CDATA[wpf]]></category><category><![CDATA[webview]]></category><category><![CDATA[Open Source]]></category><dc:creator><![CDATA[RITHVIK SHAGA]]></dc:creator><pubDate>Fri, 02 Oct 2026 18:30:19 GMT</pubDate><content:encoded><![CDATA[<p><strong>C# · .NET 8 · WPF · Microsoft Edge WebView2 · Proxy Management · Browser Testing</strong></p>
<p>I wanted to build something that would let me experiment with <strong>multiple isolated browser sessions</strong>, while also giving me control over the network environment behind each session.</p>
<p>That led to <strong>WebSessionForge</strong> — a Windows desktop application built with <strong>C# and .NET 8</strong> that combines WebView2 browser sessions, proxy management, proxy health monitoring, proxy rotation, failure recovery, and structured runtime logging.</p>
<p><strong>GitHub:</strong> <a href="https://github.com/shagarithvik/WebSessionForge">https://github.com/shagarithvik/WebSessionForge</a></p>
<hr />
<h1>What Is WebSessionForge?</h1>
<p>WebSessionForge is a <strong>browser-session and network testing platform</strong>.</p>
<p>It provides a central desktop interface for creating and managing multiple WebView2 sessions while independently managing their network assignments.</p>
<p>The core idea is:</p>
<pre><code class="language-text">                 WebSessionForge
                       │
          ┌────────────┼────────────┐
          │            │            │
          ▼            ▼            ▼
      Session 1    Session 2    Session N
          │            │            │
          ▼            ▼            ▼
       WebView2      WebView2      WebView2
          │            │            │
          ▼            ▼            ▼
       Proxy A      Proxy B      Proxy N
</code></pre>
<p>The interesting engineering challenge isn't simply opening multiple browsers.</p>
<p>It is coordinating:</p>
<ul>
<li><p>Browser lifecycle</p>
</li>
<li><p>Session isolation</p>
</li>
<li><p>Proxy allocation</p>
</li>
<li><p>Proxy health</p>
</li>
<li><p>Proxy rotation</p>
</li>
<li><p>Failure recovery</p>
</li>
<li><p>Concurrent operations</p>
</li>
<li><p>Runtime observability</p>
</li>
</ul>
<hr />
<h1>Why I Built It</h1>
<p>Browser automation becomes complicated when network infrastructure is part of the test.</p>
<p>A session can fail because:</p>
<ul>
<li><p>A proxy becomes unavailable</p>
</li>
<li><p>A provider returns malformed data</p>
</li>
<li><p>A connection times out</p>
</li>
<li><p>A browser session encounters an error</p>
</li>
<li><p>Multiple sessions compete for the same proxy</p>
</li>
<li><p>Network conditions change during execution</p>
</li>
</ul>
<p>I wanted these responsibilities separated instead of putting everything into one large automation class.</p>
<p>The resulting architecture became:</p>
<pre><code class="language-text">Browser Sessions
       │
       ├── Session lifecycle
       │
       ├── Proxy assignment
       │
       ├── Proxy health
       │
       ├── Failure recovery
       │
       └── Logging
</code></pre>
<hr />
<h1>Technology Stack</h1>
<table>
<thead>
<tr>
<th>Component</th>
<th>Technology</th>
</tr>
</thead>
<tbody><tr>
<td>Language</td>
<td>C#</td>
</tr>
<tr>
<td>Framework</td>
<td>.NET 8</td>
</tr>
<tr>
<td>Desktop UI</td>
<td>WPF</td>
</tr>
<tr>
<td>Browser</td>
<td>Microsoft Edge WebView2</td>
</tr>
<tr>
<td>Configuration</td>
<td>JSON</td>
</tr>
<tr>
<td>Proxy input</td>
<td>CSV / Provider endpoints</td>
</tr>
<tr>
<td>Logging</td>
<td>JSONL</td>
</tr>
<tr>
<td>Platform</td>
<td>Windows 10/11</td>
</tr>
</tbody></table>
<hr />
<h1>Architecture</h1>
<p>The application is organized around several major components:</p>
<pre><code class="language-text">                         ┌─────────────────────┐
                         │   WebSessionForge   │
                         │     WPF Dashboard   │
                         └──────────┬──────────┘
                                    │
              ┌─────────────────────┼─────────────────────┐
              │                     │                     │
              ▼                     ▼                     ▼
       Session Manager        Proxy Manager           Logger
              │                     │                     │
              │              ┌──────┴──────┐              │
              │              │             │              │
              ▼              ▼             ▼              ▼
        WebView2        CSV Import    Providers       JSONL Logs
        Sessions             │             │
              │              └──────┬──────┘
              │                     │
              └──────────────┬──────┘
                             ▼
                       Proxy Pool
</code></pre>
<p>The main principle is <strong>separation of responsibilities</strong>.</p>
<hr />
<h1>🔧 Concrete Implementation Walkthrough</h1>
<p>This is where the architecture becomes practical.</p>
<p>The following walkthrough shows how the major pieces fit together conceptually in C#.</p>
<p>The exact class names and implementation details may evolve as the project develops; the snippets illustrate the intended implementation pattern.</p>
<hr />
<h2>1. Define a Proxy Model</h2>
<p>Everything starts with a common representation for a proxy.</p>
<p>Instead of passing strings throughout the application, represent a proxy as an object.</p>
<pre><code class="language-csharp">public sealed class ProxyInfo
{
    public string Host { get; init; } = string.Empty;
    public int Port { get; init; }

    public string Scheme { get; init; } = "http";

    public string? Username { get; init; }
    public string? Password { get; init; }

    public string? Provider { get; init; }

    public TimeSpan? Latency { get; set; }

    public bool IsHealthy { get; set; }

    public int FailureCount { get; set; }
}
</code></pre>
<p>Now every subsystem can work with the same structure.</p>
<p>For example:</p>
<pre><code class="language-text">CSV
 │
 ▼
ProxyInfo
 │
 ├── Health Checker
 ├── Proxy Pool
 ├── Session Manager
 └── Logger
</code></pre>
<p>This is much cleaner than passing raw strings between services.</p>
<hr />
<h1>2. Normalize Proxy Input</h1>
<p>Different proxy sources can produce different formats.</p>
<p>For example:</p>
<pre><code class="language-text">http://host:8080
</code></pre>
<p>or:</p>
<pre><code class="language-text">host,8080,http,user,password
</code></pre>
<p>The importer converts these into the same internal model.</p>
<p>Conceptually:</p>
<pre><code class="language-csharp">ProxyInfo ParseProxy(string value)
{
    // Parse scheme, host, port and optional credentials.
    // Normalize the result into ProxyInfo.
}
</code></pre>
<p>The important design principle is:</p>
<blockquote>
<p><strong>Normalize at the boundary.</strong></p>
</blockquote>
<p>Once a proxy enters the application, the rest of the system should not care where it came from.</p>
<hr />
<h1>3. Build the Proxy Pool</h1>
<p>The proxy manager maintains the available proxies.</p>
<p>Conceptually:</p>
<pre><code class="language-csharp">private readonly List&lt;ProxyInfo&gt; _proxies = new();
</code></pre>
<p>Adding a proxy becomes:</p>
<pre><code class="language-csharp">public void Add(ProxyInfo proxy)
{
    if (!_proxies.Any(p =&gt;
        p.Host == proxy.Host &amp;&amp;
        p.Port == proxy.Port &amp;&amp;
        p.Scheme == proxy.Scheme))
    {
        _proxies.Add(proxy);
    }
}
</code></pre>
<p>This gives the application a central location for:</p>
<ul>
<li><p>Deduplication</p>
</li>
<li><p>Health state</p>
</li>
<li><p>Assignment</p>
</li>
<li><p>Release</p>
</li>
<li><p>Replacement</p>
</li>
</ul>
<hr />
<h1>4. Health Check the Proxy</h1>
<p>A proxy shouldn't automatically become available just because it was parsed successfully.</p>
<p>The health-check layer tests connectivity.</p>
<p>A simplified conceptual implementation is:</p>
<pre><code class="language-csharp">public async Task&lt;bool&gt; CheckAsync(
    ProxyInfo proxy,
    CancellationToken cancellationToken)
{
    try
    {
        using var client = CreateClient(proxy);

        using var response = await client.GetAsync(
            TestEndpoint,
            cancellationToken);

        proxy.IsHealthy = response.IsSuccessStatusCode;

        return proxy.IsHealthy;
    }
    catch
    {
        proxy.IsHealthy = false;
        proxy.FailureCount++;

        return false;
    }
}
</code></pre>
<p>The actual implementation needs to account for the proxy protocol and appropriate timeout behavior.</p>
<p>The important flow is:</p>
<pre><code class="language-text">Proxy
  │
  ▼
Connectivity Test
  │
 ┌┴─────────┐
 ▼          ▼
Healthy    Failed
 │          │
 ▼          ▼
Pool       Remove/Retry
</code></pre>
<hr />
<h1>5. Reserve a Proxy for a Session</h1>
<p>Once healthy proxies exist, sessions need to obtain them safely.</p>
<p>A simple conceptual abstraction is:</p>
<pre><code class="language-csharp">public ProxyInfo? Acquire()
{
    lock (_sync)
    {
        var proxy = _proxies
            .FirstOrDefault(p =&gt;
                p.IsHealthy &amp;&amp;
                !IsReserved(p));

        if (proxy == null)
            return null;

        Reserve(proxy);

        return proxy;
    }
}
</code></pre>
<p>The important part here is synchronization.</p>
<p>Without it, two sessions could perform:</p>
<pre><code class="language-text">Session A → find Proxy A
Session B → find Proxy A
</code></pre>
<p>before either session marks it as reserved.</p>
<p>With synchronized allocation:</p>
<pre><code class="language-text">Proxy Pool
    │
    ├── Proxy A → Session 1
    ├── Proxy B → Session 2
    └── Proxy C → Available
</code></pre>
<p>This becomes particularly important as the number of concurrent sessions increases.</p>
<hr />
<h1>6. Create a WebView2 Session</h1>
<p>A session manager can then create a browser environment for the assigned session.</p>
<p>Conceptually:</p>
<pre><code class="language-csharp">public sealed class BrowserSession
{
    public string Id { get; }
    public ProxyInfo? Proxy { get; }

    public BrowserSession(
        string id,
        ProxyInfo? proxy)
    {
        Id = id;
        Proxy = proxy;
    }
}
</code></pre>
<p>The lifecycle becomes:</p>
<pre><code class="language-text">Create Session
      │
      ▼
Acquire Proxy
      │
      ▼
Create WebView2 Environment
      │
      ▼
Initialize Browser
      │
      ▼
Navigate
</code></pre>
<p>WebView2 itself then becomes the browser execution layer, while the session manager controls the higher-level lifecycle.</p>
<hr />
<h1>7. Keep Session State Separate</h1>
<p>Each session should have an identity.</p>
<p>For example:</p>
<pre><code class="language-csharp">var sessionId = $"session-{index}";
</code></pre>
<p>The session can then be associated with:</p>
<pre><code class="language-text">Session ID
Proxy
Browser Environment
Status
Created Time
Log File
</code></pre>
<p>That makes it possible to answer questions like:</p>
<blockquote>
<p>Which proxy was assigned to Session 4 when the failure occurred?</p>
</blockquote>
<p>This becomes extremely useful during debugging.</p>
<hr />
<h1>8. Start Multiple Sessions</h1>
<p>The dashboard can coordinate multiple sessions.</p>
<p>Conceptually:</p>
<pre><code class="language-csharp">for (int i = 0; i &lt; sessionCount; i++)
{
    var session = await sessionManager.CreateAsync(
        $"session-{i + 1}");

    sessions.Add(session);
}
</code></pre>
<p>In a production implementation, these operations need proper asynchronous lifecycle management rather than blindly creating everything at once.</p>
<p>The overall flow becomes:</p>
<pre><code class="language-text">Dashboard
    │
    ▼
Session Manager
    │
    ├── Session 1
    ├── Session 2
    ├── Session 3
    └── Session N
</code></pre>
<hr />
<h1>9. Handle Proxy Failure</h1>
<p>Suppose Session 2 is currently using Proxy B.</p>
<p>A network operation fails.</p>
<p>The session manager can perform:</p>
<pre><code class="language-text">Session 2
   │
   ▼
Network Failure
   │
   ▼
Mark Proxy B Failed
   │
   ▼
Release Proxy B
   │
   ▼
Acquire Replacement
   │
   ▼
Proxy C
</code></pre>
<p>Conceptually:</p>
<pre><code class="language-csharp">public async Task&lt;bool&gt; ReplaceProxyAsync(
    BrowserSession session)
{
    Release(session.Proxy);

    var replacement = Acquire();

    if (replacement == null)
        return false;

    session.SetProxy(replacement);

    return true;
}
</code></pre>
<p>The exact WebView2 reconfiguration strategy depends on the session lifecycle and browser environment implementation.</p>
<p>The important architectural idea is that <strong>proxy failure is handled by the session/proxy layers rather than the UI</strong>.</p>
<hr />
<h1>10. Implement Rotation</h1>
<p>Rotation can be represented as an asynchronous background operation.</p>
<p>Conceptually:</p>
<pre><code class="language-csharp">while (!cancellationToken.IsCancellationRequested)
{
    await Task.Delay(rotationInterval, cancellationToken);

    await RotateSessionAsync(session);
}
</code></pre>
<p>The rotation process is:</p>
<pre><code class="language-text">Wait
 │
 ▼
Rotation Due
 │
 ▼
Release Current Proxy
 │
 ▼
Acquire Replacement
 │
 ▼
Update Session
 │
 ▼
Log Event
 │
 ▼
Continue
</code></pre>
<p>A jitter value can be applied around the configured interval when the testing scenario requires variation.</p>
<hr />
<h1>11. Structured Logging</h1>
<p>Instead of writing arbitrary strings:</p>
<pre><code class="language-text">"proxy failed"
</code></pre>
<p>the application can record structured events.</p>
<p>Conceptually:</p>
<pre><code class="language-csharp">await logger.WriteAsync(new
{
    Timestamp = DateTimeOffset.UtcNow,
    Event = "ProxyFailure",
    SessionId = session.Id,
    Proxy = proxy.Host,
    Port = proxy.Port
});
</code></pre>
<p>The result can be stored as JSONL:</p>
<pre><code class="language-json">{
  "timestamp": "2026-10-02T12:00:00Z",
  "event": "ProxyFailure",
  "sessionId": "session-2",
  "proxy": "proxy.example",
  "port": 8080
}
</code></pre>
<p>Now logs can be filtered programmatically.</p>
<p>For example:</p>
<pre><code class="language-text">All failures
All events for Session 2
All proxy rotations
All health-check failures
</code></pre>
<hr />
<h1>12. Connect the UI</h1>
<p>The WPF dashboard acts as the orchestration layer.</p>
<p>Conceptually:</p>
<pre><code class="language-text">MainWindow
    │
    ├── Start
    │      ↓
    │   SessionManager
    │
    ├── Stop
    │      ↓
    │   SessionManager
    │
    ├── Proxy Status
    │      ↓
    │   ProxyManager
    │
    ├── Browser Matrix
    │      ↓
    │   Active Sessions
    │
    └── Logs
           ↓
        Logger
</code></pre>
<p>The UI should display state rather than contain the business logic itself.</p>
<p>That distinction becomes increasingly important as the project grows.</p>
<hr />
<h1>13. Configuration-Driven Behavior</h1>
<p>Runtime settings belong in configuration rather than hardcoded constants.</p>
<p>For example:</p>
<pre><code class="language-json">{
  "SessionCount": 6,
  "RotationMinutes": 5,
  "RotationJitterSeconds": 30,
  "ProxyHealthTimeoutSeconds": 10,
  "ProviderRefreshMinutes": 5,
  "ProviderUrls": [],
  "Headless": false
}
</code></pre>
<p>The application can load this during startup:</p>
<pre><code class="language-csharp">var json = await File.ReadAllTextAsync(
    "config/config.json");

var config = JsonSerializer.Deserialize&lt;AppConfig&gt;(json);
</code></pre>
<p>The result is passed to the relevant services.</p>
<pre><code class="language-text">Configuration
      │
      ├── Session Manager
      ├── Proxy Manager
      ├── Health Checker
      └── Provider Manager
</code></pre>
<p>This makes behavior reproducible and easier to modify.</p>
<hr />
<h1>14. Complete Runtime Flow</h1>
<p>Putting everything together:</p>
<pre><code class="language-text">                    START
                      │
                      ▼
               Load Configuration
                      │
                      ▼
              Load Proxy Sources
                      │
                      ▼
              Normalize Proxies
                      │
                      ▼
                Health Checks
                      │
             ┌────────┴────────┐
             ▼                 ▼
          Healthy             Failed
             │                 │
             ▼                 ▼
        Proxy Pool          Excluded
             │
             ▼
       Create Session
             │
             ▼
       Acquire Proxy
             │
             ▼
       Create WebView2
             │
             ▼
          Navigate
             │
             ▼
           Monitor
             │
       ┌─────┴──────┐
       ▼            ▼
    Healthy       Failure
       │            │
       │            ▼
       │       Mark Proxy Failed
       │            │
       │            ▼
       │       Acquire Replacement
       │
       ▼
   Rotation Due
       │
       ▼
   Rotate Proxy
       │
       ▼
     Continue
</code></pre>
<p>This is the core of WebSessionForge.</p>
<hr />
<h1>Proxy Provider Pipeline</h1>
<p>Provider integration follows the same architecture.</p>
<pre><code class="language-text">Provider URL
     │
     ▼
HTTP Request
     │
     ▼
Response
     │
     ▼
Format Detection
     │
 ┌───┼──────────┐
 ▼   ▼          ▼
JSON Text     Object
 │    │          │
 └────┴──────────┘
          │
          ▼
      Proxy Parser
          │
          ▼
      ProxyInfo[]
          │
          ▼
      Proxy Manager
</code></pre>
<p>The provider layer therefore doesn't need to know anything about browser sessions.</p>
<p>It simply supplies normalized proxy objects.</p>
<hr />
<h1>Failure Isolation</h1>
<p>One of the most important architectural goals is preventing one failure from cascading into everything else.</p>
<p>For example:</p>
<pre><code class="language-text">Proxy A fails
     │
     ▼
Session 1 affected
     │
     ├── Proxy A marked failed
     ├── Proxy A removed
     └── Replacement requested
</code></pre>
<p>Instead of:</p>
<pre><code class="language-text">Proxy A fails
     │
     ▼
Entire application crashes
</code></pre>
<p>The system treats failures as local events whenever possible.</p>
<hr />
<h1>Browser Matrix Implementation Concept</h1>
<p>The browser matrix is essentially a presentation layer over the active sessions.</p>
<p>Conceptually:</p>
<pre><code class="language-text">Observable Session Collection
          │
          ▼
      Browser Matrix
          │
    ┌─────┼─────┐
    ▼     ▼     ▼
 Session Session Session
    1      2      3
</code></pre>
<p>Each visual container can host the corresponding WebView2 control.</p>
<p>This provides an easy way to inspect the state of multiple sessions without losing the central dashboard.</p>
<hr />
<h1>Observability Architecture</h1>
<p>The logging architecture can be visualized as:</p>
<pre><code class="language-text">Session Manager ──────┐
                      │
Proxy Manager ────────┤
                      ├──► Logger ───► JSONL
Health Checker ───────┤
                      │
Provider Manager ─────┘
</code></pre>
<p>This is useful because logging remains centralized.</p>
<p>A session doesn't need to know where logs are stored.</p>
<p>It simply emits an event.</p>
<p>The logger handles persistence.</p>
<hr />
<h1>Performance Considerations</h1>
<p>Multiple WebView2 instances are considerably more expensive than simple HTTP clients.</p>
<p>Resource consumption depends on:</p>
<pre><code class="language-text">Session Count
      +
Page Complexity
      +
JavaScript
      +
Network Activity
      +
Proxy Latency
      +
CPU
      +
RAM
</code></pre>
<p>For that reason, the recommended development workflow is:</p>
<pre><code class="language-text">1 session
   ↓
2 sessions
   ↓
4 sessions
   ↓
6 sessions
   ↓
Scale gradually
</code></pre>
<p>This makes it easier to identify the point where resource consumption becomes problematic.</p>
<hr />
<h1>Project Structure</h1>
<p>The project follows a modular layout:</p>
<pre><code class="language-text">WebSessionForge/
│
├── Models/
│
├── Providers/
│
├── Services/
│
├── config/
│
├── logs/
│
├── App.xaml
├── App.xaml.cs
├── MainWindow.xaml
├── MainWindow.xaml.cs
├── BrowserWindow.xaml
├── BrowserWindow.xaml.cs
├── WebSessionForge.csproj
├── DOCUMENTATION.md
├── README.md
└── LICENSE
</code></pre>
<p>The intended responsibility boundaries are:</p>
<table>
<thead>
<tr>
<th>Directory</th>
<th>Responsibility</th>
</tr>
</thead>
<tbody><tr>
<td><code>Models/</code></td>
<td>Domain/application models</td>
</tr>
<tr>
<td><code>Providers/</code></td>
<td>External proxy sources</td>
</tr>
<tr>
<td><code>Services/</code></td>
<td>Session, proxy, health and logging logic</td>
</tr>
<tr>
<td><code>config/</code></td>
<td>Runtime configuration</td>
</tr>
<tr>
<td><code>logs/</code></td>
<td>Runtime event data</td>
</tr>
<tr>
<td><code>*.xaml</code></td>
<td>WPF interface</td>
</tr>
</tbody></table>
<hr />
<h1>Configuration</h1>
<p>The main configuration file is:</p>
<pre><code class="language-text">config/config.json
</code></pre>
<p>Example:</p>
<pre><code class="language-json">{
  "SessionCount": 6,
  "RotationMinutes": 5,
  "RotationJitterSeconds": 30,
  "ProxyHealthTimeoutSeconds": 10,
  "ProviderRefreshMinutes": 5,
  "ProviderUrls": [],
  "Headless": false
}
</code></pre>
<p>Important options:</p>
<table>
<thead>
<tr>
<th>Setting</th>
<th>Purpose</th>
</tr>
</thead>
<tbody><tr>
<td><code>SessionCount</code></td>
<td>Number of browser sessions</td>
</tr>
<tr>
<td><code>RotationMinutes</code></td>
<td>Proxy rotation interval</td>
</tr>
<tr>
<td><code>RotationJitterSeconds</code></td>
<td>Rotation variation</td>
</tr>
<tr>
<td><code>ProxyHealthTimeoutSeconds</code></td>
<td>Health-check timeout</td>
</tr>
<tr>
<td><code>ProviderRefreshMinutes</code></td>
<td>Provider refresh</td>
</tr>
<tr>
<td><code>ProviderUrls</code></td>
<td>Provider endpoints</td>
</tr>
<tr>
<td><code>Headless</code></td>
<td>Headless configuration</td>
</tr>
</tbody></table>
<hr />
<h1>Logging</h1>
<p>WebSessionForge uses JSON Lines.</p>
<p>Application logs:</p>
<pre><code class="language-text">logs/application-YYYY-MM-DD.jsonl
</code></pre>
<p>Session logs:</p>
<pre><code class="language-text">logs/session-1-YYYY-MM-DD.jsonl
logs/session-2-YYYY-MM-DD.jsonl
logs/session-3-YYYY-MM-DD.jsonl
</code></pre>
<p>Events can include:</p>
<ul>
<li><p>Session creation</p>
</li>
<li><p>Session termination</p>
</li>
<li><p>Proxy assignment</p>
</li>
<li><p>Proxy rotation</p>
</li>
<li><p>Health checks</p>
</li>
<li><p>Failures</p>
</li>
<li><p>Retries</p>
</li>
<li><p>Recovery</p>
</li>
<li><p>Runtime errors</p>
</li>
</ul>
<p>Structured logs make concurrent browser testing much easier to investigate.</p>
<hr />
<h1>Running the Project</h1>
<p>Requirements:</p>
<ul>
<li><p>Windows 10/11</p>
</li>
<li><p>.NET 8 SDK</p>
</li>
<li><p>Microsoft Edge WebView2 Runtime</p>
</li>
</ul>
<p>Clone:</p>
<pre><code class="language-bash">git clone https://github.com/shagarithvik/WebSessionForge.git
</code></pre>
<p>Build:</p>
<pre><code class="language-bash">cd WebSessionForge
dotnet restore
dotnet build
</code></pre>
<p>Run:</p>
<pre><code class="language-bash">dotnet run
</code></pre>
<p>Publish:</p>
<pre><code class="language-bash">dotnet publish -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -p:DebugType=None
</code></pre>
<hr />
<h1>Testing Scenarios</h1>
<p>WebSessionForge is useful for controlled testing of:</p>
<h2>Session Isolation</h2>
<p>Verify independent browser state.</p>
<h2>Proxy Reliability</h2>
<p>Observe behavior when a network endpoint becomes unavailable.</p>
<h2>Proxy Rotation</h2>
<p>Test session behavior when network assignments change.</p>
<h2>Failure Recovery</h2>
<p>Verify replacement and recovery workflows.</p>
<h2>Provider Reliability</h2>
<p>Test malformed or unavailable provider responses.</p>
<h2>Observability</h2>
<p>Reconstruct a test run from structured JSONL events.</p>
<hr />
<h1>What I Learned</h1>
<h2>Browser sessions are expensive</h2>
<p>A browser environment is much heavier than an HTTP client.</p>
<h2>Network failures are normal</h2>
<p>Proxy infrastructure should be designed around failure.</p>
<h2>Health checks are not guarantees</h2>
<p>A successful check only tells us that a proxy worked at that particular moment.</p>
<h2>Logging matters</h2>
<p>Concurrent systems become difficult to debug without structured events.</p>
<h2>Separation matters</h2>
<p>Keeping the UI, session manager, proxy manager, providers, and logger independent makes the project easier to maintain.</p>
<hr />
<h1>Responsible Use</h1>
<p>WebSessionForge is intended for <strong>authorized testing and experimentation</strong>.</p>
<p>Use it with systems and infrastructure where you have permission.</p>
<p>It should not be used to:</p>
<ul>
<li><p>Manipulate platform metrics</p>
</li>
<li><p>Generate artificial engagement</p>
</li>
<li><p>Circumvent access controls</p>
</li>
<li><p>Evade anti-bot systems</p>
</li>
<li><p>Bypass rate limits</p>
</li>
<li><p>Access systems without authorization</p>
</li>
<li><p>Abuse third-party infrastructure</p>
</li>
</ul>
<p>The project is intended as a <strong>browser-session and QA/testing platform</strong>.</p>
<hr />
<h1>Roadmap</h1>
<h3>Architecture</h3>
<ul>
<li><p>[ ] Further MVVM adoption</p>
</li>
<li><p>[ ] Dependency injection</p>
</li>
<li><p>[ ] More interfaces</p>
</li>
<li><p>[ ] Improved testability</p>
</li>
</ul>
<h3>Proxy System</h3>
<ul>
<li><p>[ ] Provider health scoring</p>
</li>
<li><p>[ ] Advanced proxy statistics</p>
</li>
<li><p>[ ] Better failure classification</p>
</li>
<li><p>[ ] Improved validation</p>
</li>
</ul>
<h3>Browser Sessions</h3>
<ul>
<li><p>[ ] Improved WebView2 lifecycle</p>
</li>
<li><p>[ ] Configurable profiles</p>
</li>
<li><p>[ ] Session telemetry</p>
</li>
<li><p>[ ] Performance metrics</p>
</li>
</ul>
<h3>Testing</h3>
<ul>
<li><p>[ ] Unit tests</p>
</li>
<li><p>[ ] Integration tests</p>
</li>
<li><p>[ ] Mock proxy provider</p>
</li>
<li><p>[ ] Local test infrastructure</p>
</li>
</ul>
<h3>Developer Experience</h3>
<ul>
<li><p>[ ] CI/CD</p>
</li>
<li><p>[ ] Automated releases</p>
</li>
<li><p>[ ] Configuration validation</p>
</li>
<li><p>[ ] Exportable reports</p>
</li>
</ul>
<hr />
<h1>Why WebSessionForge?</h1>
<p>The name reflects the direction of the project.</p>
<p><strong>WebSession</strong> represents the primary unit of the platform.</p>
<p><strong>Forge</strong> represents creating and managing controlled browser-session environments.</p>
<p>The project started around proxy-backed browser experimentation, but the architecture is broader:</p>
<pre><code class="language-text">Browser Sessions
       +
Network Conditions
       +
Testing
       +
Observability
       +
Automation
</code></pre>
<hr />
<h1>Final Thoughts</h1>
<p>WebSessionForge started as an experiment around multiple browser sessions and proxy management.</p>
<p>It has evolved into a small platform for studying how:</p>
<ul>
<li><p>Browser environments</p>
</li>
<li><p>Network conditions</p>
</li>
<li><p>Failure recovery</p>
</li>
<li><p>Concurrency</p>
</li>
<li><p>Observability</p>
</li>
</ul>
<p>interact in a real desktop application.</p>
<p>The most important lesson has been simple:</p>
<blockquote>
<p><strong>Reliable automation isn't just about making something work. It's about making failures understandable and recoverable.</strong></p>
</blockquote>
<p>That's the direction I want to continue taking WebSessionForge.</p>
<hr />
<h1>🔗 Project</h1>
<p><strong>GitHub:</strong> <a href="https://github.com/shagarithvik/WebSessionForge">https://github.com/shagarithvik/WebSessionForge</a></p>
<p><strong>Author:</strong> <strong>Rithvik Shaga</strong></p>
<p><strong>Stack:</strong> C# · .NET 8 · WPF · WebView2</p>
<p><strong>License:</strong> MIT</p>
<hr />
<h2>⭐ Get Involved</h2>
<p>The repository is open source.</p>
<p>Feedback and contributions around:</p>
<ul>
<li><p>Architecture</p>
</li>
<li><p>WebView2</p>
</li>
<li><p>Session management</p>
</li>
<li><p>Proxy infrastructure</p>
</li>
<li><p>Reliability</p>
</li>
<li><p>Performance</p>
</li>
<li><p>Testing</p>
</li>
<li><p>Observability</p>
</li>
</ul>
<p>are welcome.</p>
<p><strong>GitHub:</strong> <a href="https://github.com/shagarithvik/WebSessionForge">https://github.com/shagarithvik/WebSessionForge</a></p>
<hr />
<h3>Built with C# • .NET 8 • WPF • WebView2</h3>
]]></content:encoded></item></channel></rss>