I wanted to build something that would let me experiment with multiple isolated browser sessions, while also giving me control over the network environment behind each session.
That led to WebSessionForge — a Windows desktop application built with C# and .NET 8 that combines WebView2 browser sessions, proxy management, proxy health monitoring, proxy rotation, failure recovery, and structured runtime logging.
GitHub: https://github.com/shagarithvik/WebSessionForge
What Is WebSessionForge?
WebSessionForge is a browser-session and network testing platform.
It provides a central desktop interface for creating and managing multiple WebView2 sessions while independently managing their network assignments.
The core idea is:
WebSessionForge
│
┌────────────┼────────────┐
│ │ │
▼ ▼ ▼
Session 1 Session 2 Session N
│ │ │
▼ ▼ ▼
WebView2 WebView2 WebView2
│ │ │
▼ ▼ ▼
Proxy A Proxy B Proxy N
The interesting engineering challenge isn't simply opening multiple browsers.
It is coordinating:
- Browser lifecycle
- Session isolation
- Proxy allocation
- Proxy health
- Proxy rotation
- Failure recovery
- Concurrent operations
- Runtime observability
Why I Built It
Browser automation becomes complicated when network infrastructure is part of the test.
A session can fail because:
- A proxy becomes unavailable
- A provider returns malformed data
- A connection times out
- A browser session encounters an error
- Multiple sessions compete for the same proxy
- Network conditions change during execution
I wanted these responsibilities separated instead of putting everything into one large automation class.
The resulting architecture became:
Browser Sessions
│
├── Session lifecycle
│
├── Proxy assignment
│
├── Proxy health
│
├── Failure recovery
│
└── Logging
Technology Stack
| Component | Technology |
|---|---|
| Language | C# |
| Framework | .NET 8 |
| Desktop UI | WPF |
| Browser | Microsoft Edge WebView2 |
| Configuration | JSON |
| Proxy input | CSV / Provider endpoints |
| Logging | JSONL |
| Platform | Windows 10/11 |
Architecture
The application is organized around several major components:
┌─────────────────────┐
│ WebSessionForge │
│ WPF Dashboard │
└──────────┬──────────┘
│
┌─────────────────────┼─────────────────────┐
│ │ │
▼ ▼ ▼
Session Manager Proxy Manager Logger
│ │ │
│ ┌──────┴──────┐ │
│ │ │ │
▼ ▼ ▼ ▼
WebView2 CSV Import Providers JSONL Logs
Sessions │ │
│ └──────┬──────┘
│ │
└──────────────┬──────┘
▼
Proxy Pool
The main principle is separation of responsibilities.
🔧 Concrete Implementation Walkthrough
This is where the architecture becomes practical.
The following walkthrough shows how the major pieces fit together conceptually in C#.
The exact class names and implementation details may evolve as the project develops; the snippets illustrate the intended implementation pattern.
1. Define a Proxy Model
Everything starts with a common representation for a proxy.
Instead of passing strings throughout the application, represent a proxy as an object.
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; }
}
Now every subsystem can work with the same structure.
For example:
CSV
│
▼
ProxyInfo
│
├── Health Checker
├── Proxy Pool
├── Session Manager
└── Logger
This is much cleaner than passing raw strings between services.
2. Normalize Proxy Input
Different proxy sources can produce different formats.
For example:
http://host:8080
or:
host,8080,http,user,password
The importer converts these into the same internal model.
Conceptually:
ProxyInfo ParseProxy(string value)
{
// Parse scheme, host, port and optional credentials.
// Normalize the result into ProxyInfo.
}
The important design principle is:
Normalize at the boundary.
Once a proxy enters the application, the rest of the system should not care where it came from.
3. Build the Proxy Pool
The proxy manager maintains the available proxies.
Conceptually:
private readonly List<ProxyInfo> _proxies = new();
Adding a proxy becomes:
public void Add(ProxyInfo proxy)
{
if (!_proxies.Any(p =>
p.Host == proxy.Host &&
p.Port == proxy.Port &&
p.Scheme == proxy.Scheme))
{
_proxies.Add(proxy);
}
}
This gives the application a central location for:
- Deduplication
- Health state
- Assignment
- Release
- Replacement
4. Health Check the Proxy
A proxy shouldn't automatically become available just because it was parsed successfully.
The health-check layer tests connectivity.
A simplified conceptual implementation is:
public async Task<bool> 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;
}
}
The actual implementation needs to account for the proxy protocol and appropriate timeout behavior.
The important flow is:
Proxy
│
▼
Connectivity Test
│
┌┴─────────┐
▼ ▼
Healthy Failed
│ │
▼ ▼
Pool Remove/Retry
5. Reserve a Proxy for a Session
Once healthy proxies exist, sessions need to obtain them safely.
A simple conceptual abstraction is:
public ProxyInfo? Acquire()
{
lock (_sync)
{
var proxy = _proxies
.FirstOrDefault(p =>
p.IsHealthy &&
!IsReserved(p));
if (proxy == null)
return null;
Reserve(proxy);
return proxy;
}
}
The important part here is synchronization.
Without it, two sessions could perform:
Session A → find Proxy A
Session B → find Proxy A
before either session marks it as reserved.
With synchronized allocation:
Proxy Pool
│
├── Proxy A → Session 1
├── Proxy B → Session 2
└── Proxy C → Available
This becomes particularly important as the number of concurrent sessions increases.
6. Create a WebView2 Session
A session manager can then create a browser environment for the assigned session.
Conceptually:
public sealed class BrowserSession
{
public string Id { get; }
public ProxyInfo? Proxy { get; }
public BrowserSession(
string id,
ProxyInfo? proxy)
{
Id = id;
Proxy = proxy;
}
}
The lifecycle becomes:
Create Session
│
▼
Acquire Proxy
│
▼
Create WebView2 Environment
│
▼
Initialize Browser
│
▼
Navigate
WebView2 itself then becomes the browser execution layer, while the session manager controls the higher-level lifecycle.
7. Keep Session State Separate
Each session should have an identity.
For example:
var sessionId = $"session-{index}";
The session can then be associated with:
Session ID
Proxy
Browser Environment
Status
Created Time
Log File
That makes it possible to answer questions like:
Which proxy was assigned to Session 4 when the failure occurred?
This becomes extremely useful during debugging.
8. Start Multiple Sessions
The dashboard can coordinate multiple sessions.
Conceptually:
for (int i = 0; i < sessionCount; i++)
{
var session = await sessionManager.CreateAsync(
$"session-{i + 1}");
sessions.Add(session);
}
In a production implementation, these operations need proper asynchronous lifecycle management rather than blindly creating everything at once.
The overall flow becomes:
Dashboard
│
▼
Session Manager
│
├── Session 1
├── Session 2
├── Session 3
└── Session N
9. Handle Proxy Failure
Suppose Session 2 is currently using Proxy B.
A network operation fails.
The session manager can perform:
Session 2
│
▼
Network Failure
│
▼
Mark Proxy B Failed
│
▼
Release Proxy B
│
▼
Acquire Replacement
│
▼
Proxy C
Conceptually:
public async Task<bool> ReplaceProxyAsync(
BrowserSession session)
{
Release(session.Proxy);
var replacement = Acquire();
if (replacement == null)
return false;
session.SetProxy(replacement);
return true;
}
The exact WebView2 reconfiguration strategy depends on the session lifecycle and browser environment implementation.
The important architectural idea is that proxy failure is handled by the session/proxy layers rather than the UI.
10. Implement Rotation
Rotation can be represented as an asynchronous background operation.
Conceptually:
while (!cancellationToken.IsCancellationRequested)
{
await Task.Delay(rotationInterval, cancellationToken);
await RotateSessionAsync(session);
}
The rotation process is:
Wait
│
▼
Rotation Due
│
▼
Release Current Proxy
│
▼
Acquire Replacement
│
▼
Update Session
│
▼
Log Event
│
▼
Continue
A jitter value can be applied around the configured interval when the testing scenario requires variation.
11. Structured Logging
Instead of writing arbitrary strings:
"proxy failed"
the application can record structured events.
Conceptually:
await logger.WriteAsync(new
{
Timestamp = DateTimeOffset.UtcNow,
Event = "ProxyFailure",
SessionId = session.Id,
Proxy = proxy.Host,
Port = proxy.Port
});
The result can be stored as JSONL:
{
"timestamp": "2026-10-02T12:00:00Z",
"event": "ProxyFailure",
"sessionId": "session-2",
"proxy": "proxy.example",
"port": 8080
}
Now logs can be filtered programmatically.
For example:
All failures
All events for Session 2
All proxy rotations
All health-check failures
12. Connect the UI
The WPF dashboard acts as the orchestration layer.
Conceptually:
MainWindow
│
├── Start
│ ↓
│ SessionManager
│
├── Stop
│ ↓
│ SessionManager
│
├── Proxy Status
│ ↓
│ ProxyManager
│
├── Browser Matrix
│ ↓
│ Active Sessions
│
└── Logs
↓
Logger
The UI should display state rather than contain the business logic itself.
That distinction becomes increasingly important as the project grows.
13. Configuration-Driven Behavior
Runtime settings belong in configuration rather than hardcoded constants.
For example:
{
"SessionCount": 6,
"RotationMinutes": 5,
"RotationJitterSeconds": 30,
"ProxyHealthTimeoutSeconds": 10,
"ProviderRefreshMinutes": 5,
"ProviderUrls": [],
"Headless": false
}
The application can load this during startup:
var json = await File.ReadAllTextAsync(
"config/config.json");
var config = JsonSerializer.Deserialize<AppConfig>(json);
The result is passed to the relevant services.
Configuration
│
├── Session Manager
├── Proxy Manager
├── Health Checker
└── Provider Manager
This makes behavior reproducible and easier to modify.
14. Complete Runtime Flow
Putting everything together:
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
This is the core of WebSessionForge.
Proxy Provider Pipeline
Provider integration follows the same architecture.
Provider URL
│
▼
HTTP Request
│
▼
Response
│
▼
Format Detection
│
┌───┼──────────┐
▼ ▼ ▼
JSON Text Object
│ │ │
└────┴──────────┘
│
▼
Proxy Parser
│
▼
ProxyInfo[]
│
▼
Proxy Manager
The provider layer therefore doesn't need to know anything about browser sessions.
It simply supplies normalized proxy objects.
Failure Isolation
One of the most important architectural goals is preventing one failure from cascading into everything else.
For example:
Proxy A fails
│
▼
Session 1 affected
│
├── Proxy A marked failed
├── Proxy A removed
└── Replacement requested
Instead of:
Proxy A fails
│
▼
Entire application crashes
The system treats failures as local events whenever possible.
Browser Matrix Implementation Concept
The browser matrix is essentially a presentation layer over the active sessions.
Conceptually:
Observable Session Collection
│
▼
Browser Matrix
│
┌─────┼─────┐
▼ ▼ ▼
Session Session Session
1 2 3
Each visual container can host the corresponding WebView2 control.
This provides an easy way to inspect the state of multiple sessions without losing the central dashboard.
Observability Architecture
The logging architecture can be visualized as:
Session Manager ──────┐
│
Proxy Manager ────────┤
├──► Logger ───► JSONL
Health Checker ───────┤
│
Provider Manager ─────┘
This is useful because logging remains centralized.
A session doesn't need to know where logs are stored.
It simply emits an event.
The logger handles persistence.
Performance Considerations
Multiple WebView2 instances are considerably more expensive than simple HTTP clients.
Resource consumption depends on:
Session Count
+
Page Complexity
+
JavaScript
+
Network Activity
+
Proxy Latency
+
CPU
+
RAM
For that reason, the recommended development workflow is:
1 session
↓
2 sessions
↓
4 sessions
↓
6 sessions
↓
Scale gradually
This makes it easier to identify the point where resource consumption becomes problematic.
Project Structure
The project follows a modular layout:
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
The intended responsibility boundaries are:
| Directory | Responsibility |
|---|---|
Models/ |
Domain/application models |
Providers/ |
External proxy sources |
Services/ |
Session, proxy, health and logging logic |
config/ |
Runtime configuration |
logs/ |
Runtime event data |
*.xaml |
WPF interface |
Configuration
The main configuration file is:
config/config.json
Example:
{
"SessionCount": 6,
"RotationMinutes": 5,
"RotationJitterSeconds": 30,
"ProxyHealthTimeoutSeconds": 10,
"ProviderRefreshMinutes": 5,
"ProviderUrls": [],
"Headless": false
}
Important options:
| Setting | Purpose |
|---|---|
SessionCount |
Number of browser sessions |
RotationMinutes |
Proxy rotation interval |
RotationJitterSeconds |
Rotation variation |
ProxyHealthTimeoutSeconds |
Health-check timeout |
ProviderRefreshMinutes |
Provider refresh |
ProviderUrls |
Provider endpoints |
Headless |
Headless configuration |
Logging
WebSessionForge uses JSON Lines.
Application logs:
logs/application-YYYY-MM-DD.jsonl
Session logs:
logs/session-1-YYYY-MM-DD.jsonl
logs/session-2-YYYY-MM-DD.jsonl
logs/session-3-YYYY-MM-DD.jsonl
Events can include:
- Session creation
- Session termination
- Proxy assignment
- Proxy rotation
- Health checks
- Failures
- Retries
- Recovery
- Runtime errors
Structured logs make concurrent browser testing much easier to investigate.
Running the Project
Requirements:
- Windows 10/11
- .NET 8 SDK
- Microsoft Edge WebView2 Runtime
Clone:
git clone https://github.com/shagarithvik/WebSessionForge.git
Build:
cd WebSessionForge
dotnet restore
dotnet build
Run:
dotnet run
Publish:
dotnet publish -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -p:DebugType=None
Testing Scenarios
WebSessionForge is useful for controlled testing of:
Session Isolation
Verify independent browser state.
Proxy Reliability
Observe behavior when a network endpoint becomes unavailable.
Proxy Rotation
Test session behavior when network assignments change.
Failure Recovery
Verify replacement and recovery workflows.
Provider Reliability
Test malformed or unavailable provider responses.
Observability
Reconstruct a test run from structured JSONL events.
What I Learned
Browser sessions are expensive
A browser environment is much heavier than an HTTP client.
Network failures are normal
Proxy infrastructure should be designed around failure.
Health checks are not guarantees
A successful check only tells us that a proxy worked at that particular moment.
Logging matters
Concurrent systems become difficult to debug without structured events.
Separation matters
Keeping the UI, session manager, proxy manager, providers, and logger independent makes the project easier to maintain.
Responsible Use
WebSessionForge is intended for authorized testing and experimentation.
Use it with systems and infrastructure where you have permission.
It should not be used to:
- Manipulate platform metrics
- Generate artificial engagement
- Circumvent access controls
- Evade anti-bot systems
- Bypass rate limits
- Access systems without authorization
- Abuse third-party infrastructure
The project is intended as a browser-session and QA/testing platform.
Roadmap
Architecture
- [ ] Further MVVM adoption
- [ ] Dependency injection
- [ ] More interfaces
- [ ] Improved testability
Proxy System
- [ ] Provider health scoring
- [ ] Advanced proxy statistics
- [ ] Better failure classification
- [ ] Improved validation
Browser Sessions
- [ ] Improved WebView2 lifecycle
- [ ] Configurable profiles
- [ ] Session telemetry
- [ ] Performance metrics
Testing
- [ ] Unit tests
- [ ] Integration tests
- [ ] Mock proxy provider
- [ ] Local test infrastructure
Developer Experience
- [ ] CI/CD
- [ ] Automated releases
- [ ] Configuration validation
- [ ] Exportable reports
Why WebSessionForge?
The name reflects the direction of the project.
WebSession represents the primary unit of the platform.
Forge represents creating and managing controlled browser-session environments.
The project started around proxy-backed browser experimentation, but the architecture is broader:
Browser Sessions
+
Network Conditions
+
Testing
+
Observability
+
Automation
Final Thoughts
WebSessionForge started as an experiment around multiple browser sessions and proxy management.
It has evolved into a small platform for studying how:
- Browser environments
- Network conditions
- Failure recovery
- Concurrency
- Observability
interact in a real desktop application.
The most important lesson has been simple:
Reliable automation isn't just about making something work. It's about making failures understandable and recoverable.
That's the direction I want to continue taking WebSessionForge.
🔗 Project
GitHub:
https://github.com/shagarithvik/WebSessionForge
Author:
Rithvik Shaga
Stack:
C# · .NET 8 · WPF · WebView2
License:
MIT
⭐ Get Involved
The repository is open source.
Feedback and contributions around:
- Architecture
- WebView2
- Session management
- Proxy infrastructure
- Reliability
- Performance
- Testing
- Observability
are welcome.
GitHub: https://github.com/shagarithvik/WebSessionForge
Top comments (0)