diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml
index 61e40c54..32dcbe7e 100644
--- a/.github/workflows/CI.yml
+++ b/.github/workflows/CI.yml
@@ -59,21 +59,6 @@ jobs:
           dotnet fantomless --recurse .
           git diff --exit-code
 
-      - name: Install dependencies of commitlint
-        run: |
-          sudo apt update
-          sudo apt install --yes git npm
-      - name: Pull our commitlint configuration
-        run: |
-          git clone https://github.com/nblockchain/conventions.git
-          rm -rf ./conventions/.git/
-      - name: Validate current commit (last commit) with commitlint
-        if: github.event_name == 'push'
-        run: ./conventions/commitlint.sh --from HEAD~1 --to HEAD --verbose
-      - name: Validate PR commits with commitlint
-        if: github.event_name == 'pull_request'
-        run: ./conventions/commitlint.sh --from ${{ github.event.pull_request.head.sha }}~${{ github.event.pull_request.commits }} --to ${{ github.event.pull_request.head.sha }} --verbose
-
   package:
     name: Package (Nuget)
     needs: sanity_check
diff --git a/NOnion.Tests/FallbackDirectorySelector.cs b/NOnion.Tests/FallbackDirectorySelector.cs
index a63d18b3..1bed1455 100644
--- a/NOnion.Tests/FallbackDirectorySelector.cs
+++ b/NOnion.Tests/FallbackDirectorySelector.cs
@@ -14,7 +14,7 @@ static internal IPEndPoint GetRandomFallbackDirectory()
         {
             if (fallbackDirectories == null)
             {
-                var urlToTorServerList = "https://raw.githubusercontent.com/torproject/tor/main/src/app/config/fallback_dirs.inc";
+                var urlToTorServerList = "https://gitlab.torproject.org/tpo/core/tor/-/raw/main/src/app/config/fallback_dirs.inc";
                 using var webClient = new WebClient();
                 var fetchedInfo = webClient.DownloadString(urlToTorServerList);
 
diff --git a/NOnion.Tests/HiddenServicesTests.cs b/NOnion.Tests/HiddenServicesTests.cs
index f75c1473..5c09f617 100644
--- a/NOnion.Tests/HiddenServicesTests.cs
+++ b/NOnion.Tests/HiddenServicesTests.cs
@@ -16,6 +16,7 @@
 using NOnion.Network;
 using NOnion.Http;
 using NOnion.Cells.Relay;
+using NOnion.Client;
 using NOnion.Directory;
 using NOnion.Tests.Utility;
 using NOnion.Services;
@@ -24,6 +25,23 @@ namespace NOnion.Tests
 {
     public class HiddenServicesTests
     {
+        [OneTimeSetUp]
+        public void Init()
+        {
+            cachePath =
+                new DirectoryInfo(
+                    Path.Combine(
+                        Path.GetTempPath(),
+                        Path.GetFileNameWithoutExtension(
+                            Path.GetRandomFileName()
+                        )
+                    )
+                );
+            cachePath.Create();
+        }
+
+        private DirectoryInfo cachePath = null;
+        
         /* It's possible that the router returned by GetRandomFallbackDirectory or
          * GetRandomRoutersForDirectoryBrowsing be inaccessable so we need to continue
          * retrying if an exceptions happened to make sure the issues are not related
@@ -33,11 +51,8 @@ public class HiddenServicesTests
 
         private async Task CreateIntroductionCircuit()
         {
-            var node = (CircuitNodeDetail.Create)(await CircuitHelper.GetRandomRoutersForDirectoryBrowsingWithRetry()).First();
-            using TorGuard guard = await TorGuard.NewClientAsync(node.EndPoint);
-            var circuit = new TorCircuit(guard);
-
-            await circuit.CreateAsync(CircuitNodeDetail.FastCreate);
+            using TorClient torClient = await TorClient.BootstrapWithGitlabAsync(cachePath);
+            var circuit = await torClient.CreateCircuitAsync(1, CircuitPurpose.Unknown, FSharpOption<CircuitNodeDetail>.None);
             await circuit.RegisterAsIntroductionPointAsync(FSharpOption<AsymmetricCipherKeyPair>.None, StubCallback, DisconnectionCallback);
         }
 
@@ -61,12 +76,8 @@ private async Task CreateRendezvousCircuit()
             var array = new byte[Constants.RendezvousCookieLength];
             RandomNumberGenerator.Create().GetNonZeroBytes(array);
 
-            var nodes = await CircuitHelper.GetRandomRoutersForDirectoryBrowsingWithRetry(2);
-            using TorGuard guard = await TorGuard.NewClientAsync(((CircuitNodeDetail.Create)nodes[0]).EndPoint);
-            var circuit = new TorCircuit(guard);
-
-            await circuit.CreateAsync(nodes[0]);
-            await circuit.ExtendAsync(nodes[1]);
+            using TorClient torClient = await TorClient.BootstrapWithGitlabAsync(cachePath);
+            var circuit = await torClient.CreateCircuitAsync(2, CircuitPurpose.Unknown, FSharpOption<CircuitNodeDetail>.None);
             await circuit.RegisterAsRendezvousPointAsync(array);
         }
 
@@ -92,10 +103,11 @@ private async Task<int> ReadExact(TorStream stream, byte[] buffer, int off, int
 
         public async Task BrowseFacebookOverHS()
         {
-            TorDirectory directory = await TorDirectory.BootstrapAsync(FallbackDirectorySelector.GetRandomFallbackDirectory(), new DirectoryInfo(Path.GetTempPath()));
+            using TorClient torClient = await TorClient.BootstrapWithGitlabAsync(cachePath);
 
-            var client = await TorServiceClient.ConnectAsync(directory, "facebookwkhpilnemxj7asaniu7vnjjbiltxjqhye3mhbshg7kx5tfyd.onion");
-            var httpClient = new TorHttpClient(client.GetStream(), "facebookwkhpilnemxj7asaniu7vnjjbiltxjqhye3mhbshg7kx5tfyd.onion");
+            var serviceClient = await TorServiceClient.ConnectAsync(torClient, "facebookwkhpilnemxj7asaniu7vnjjbiltxjqhye3mhbshg7kx5tfyd.onion");
+            var stream = await serviceClient.GetStreamAsync();
+            var httpClient = new TorHttpClient(stream, "facebookwkhpilnemxj7asaniu7vnjjbiltxjqhye3mhbshg7kx5tfyd.onion");
 
             try
             {
@@ -117,11 +129,11 @@ public void CanBrowseFacebookOverHS()
 
         public async Task BrowseFacebookOverHSWithTLS()
         {
-            TorDirectory directory = await TorDirectory.BootstrapAsync(FallbackDirectorySelector.GetRandomFallbackDirectory(), new DirectoryInfo(Path.GetTempPath()));
-
-            var client = await TorServiceClient.ConnectAsync(directory, "facebookwkhpilnemxj7asaniu7vnjjbiltxjqhye3mhbshg7kx5tfyd.onion:443");
-
-            var sslStream = new SslStream(client.GetStream(), true, (sender, cert, chain, sslPolicyErrors) => true);
+            using TorClient torClient = await TorClient.BootstrapWithGitlabAsync(cachePath);
+            
+            var serviceClient = await TorServiceClient.ConnectAsync(torClient, "facebookwkhpilnemxj7asaniu7vnjjbiltxjqhye3mhbshg7kx5tfyd.onion:443");
+            var stream = await serviceClient.GetStreamAsync();
+            var sslStream = new SslStream(stream, true, (sender, cert, chain, sslPolicyErrors) => true);
             await sslStream.AuthenticateAsClientAsync(string.Empty, null, SslProtocols.Tls12, false);
 
             var httpClientOverSslStream = new TorHttpClient(sslStream, "www.facebookwkhpilnemxj7asaniu7vnjjbiltxjqhye3mhbshg7kx5tfyd.onion");
@@ -147,10 +159,8 @@ public void CanBrowseFacebookOverHSWithTLS()
 
         public async Task EstablishAndCommunicateOverHSConnectionOnionStyle()
         {
-            int descriptorUploadRetryLimit = 2;
-
-            TorDirectory directory = await TorDirectory.BootstrapAsync(FallbackDirectorySelector.GetRandomFallbackDirectory(), new DirectoryInfo(Path.GetTempPath()));
-
+            using TorClient torClient = await TorClient.BootstrapWithGitlabAsync(cachePath);
+            
             TorLogger.Log("Finished bootstraping");
 
             SecureRandom random = new SecureRandom();
@@ -158,7 +168,7 @@ public async Task EstablishAndCommunicateOverHSConnectionOnionStyle()
             kpGen.Init(new Ed25519KeyGenerationParameters(random));
             Ed25519PrivateKeyParameters masterPrivateKey = (Ed25519PrivateKeyParameters)kpGen.GenerateKeyPair().Private;
 
-            TorServiceHost host = new TorServiceHost(directory, descriptorUploadRetryLimit, TestsRetryCount, FSharpOption<Ed25519PrivateKeyParameters>.Some(masterPrivateKey));
+            TorServiceHost host = new TorServiceHost(torClient, FSharpOption<Ed25519PrivateKeyParameters>.Some(masterPrivateKey));
             await host.StartAsync();
 
             TorLogger.Log("Finished starting HS host");
@@ -175,8 +185,8 @@ public async Task EstablishAndCommunicateOverHSConnectionOnionStyle()
 
             var clientSide =
                 Task.Run(async () => {
-                    var client = await TorServiceClient.ConnectAsync(directory, host.ExportUrl());
-                    var stream = client.GetStream();
+                    var serviceClient = await TorServiceClient.ConnectAsync(torClient, host.ExportUrl());
+                    var stream = await serviceClient.GetStreamAsync();
                     var lengthBytes = new byte[sizeof(int)];
                     await ReadExact(stream, lengthBytes, 0, lengthBytes.Length);
                     var length = BitConverter.ToInt32(lengthBytes);
diff --git a/NOnion.Tests/TorClientTests.cs b/NOnion.Tests/TorClientTests.cs
new file mode 100644
index 00000000..d017ca16
--- /dev/null
+++ b/NOnion.Tests/TorClientTests.cs
@@ -0,0 +1,47 @@
+using Microsoft.FSharp.Core;
+using System.IO;
+using System.Threading.Tasks;
+
+using NUnit.Framework;
+
+using NOnion.Client;
+
+namespace NOnion.Tests
+{
+    public class TorClientTests
+    {
+        private async Task BootstrapWithGitlab()
+        {
+            await TorClient.BootstrapWithGitlabAsync(FSharpOption<DirectoryInfo>.None);
+        }
+
+        [Test]
+        public void CanBootstrapWithGitlab()
+        {
+            Assert.DoesNotThrowAsync(BootstrapWithGitlab);
+        }
+        
+        private async Task BootstrapWithEmbeddedList()
+        {
+            await TorClient.BootstrapWithEmbeddedListAsync(FSharpOption<DirectoryInfo>.None);
+        }
+
+        [Test]
+        public void CanBootstrapWithEmbeddedList()
+        {
+            Assert.DoesNotThrowAsync(BootstrapWithEmbeddedList);
+        }
+        
+        private async Task CreateCircuit()
+        {
+            using TorClient client = await TorClient.BootstrapWithEmbeddedListAsync(FSharpOption<DirectoryInfo>.None);
+            await client.CreateCircuitAsync(3, CircuitPurpose.Unknown, FSharpOption<Network.CircuitNodeDetail>.None);
+        }
+
+        [Test]
+        public void CanCreateCircuit()
+        {
+            Assert.DoesNotThrowAsync(CreateCircuit);
+        }
+    }
+}
diff --git a/NOnion.Tests/TorProxyTests.cs b/NOnion.Tests/TorProxyTests.cs
new file mode 100644
index 00000000..9c75bd34
--- /dev/null
+++ b/NOnion.Tests/TorProxyTests.cs
@@ -0,0 +1,115 @@
+using System.Net;
+using System.Net.Http;
+using System.Threading.Tasks;
+
+using Newtonsoft.Json;
+using NUnit.Framework;
+
+using NOnion.Proxy;
+
+namespace NOnion.Tests
+{
+    internal class TorProxyTests
+    {
+        private const int MaximumRetry = 3;
+
+        private class TorProjectCheckResult
+        {
+            [JsonProperty("IsTor")]
+            internal bool IsTor { get; set; }
+
+            [JsonProperty("IP")]
+            internal string IP { get; set; }
+        }
+
+        [Test]
+        [Retry(MaximumRetry)]
+        public void CanProxyTorProjectExitNodeCheck()
+        {
+            Assert.DoesNotThrowAsync(ProxyTorProjectExitNodeCheck);
+        }
+
+        private async Task ProxyTorProjectExitNodeCheck()
+        {
+            using (await TorProxy.StartAsync(IPAddress.Loopback, 20000))
+            {
+                var handler = new HttpClientHandler
+                {
+                    Proxy = new WebProxy("http://localhost:20000")
+                };
+
+                var client = new HttpClient(handler);
+                var resultStr = await client.GetStringAsync("https://check.torproject.org/api/ip");
+                var result = JsonConvert.DeserializeObject<TorProjectCheckResult>(resultStr);
+                Assert.IsTrue(result.IsTor);
+            }
+        }
+
+        [Test]
+        [Retry(MaximumRetry)]
+        public void CanProxyHttps()
+        {
+            Assert.DoesNotThrowAsync(ProxyHttps);
+        }
+
+        private async Task ProxyHttps()
+        {
+            using (await TorProxy.StartAsync(IPAddress.Loopback, 20000))
+            {
+                var handler = new HttpClientHandler
+                {
+                    Proxy = new WebProxy("http://localhost:20000")
+                };
+
+                var client = new HttpClient(handler);
+                var googleResponse = await client.GetAsync("https://google.com");
+                Assert.That(googleResponse.StatusCode > 0);
+            }
+        }
+
+        [Test]
+        [Retry(MaximumRetry)]
+        public void CanProxyHttp()
+        {
+            Assert.DoesNotThrowAsync(ProxyHttp);
+        }
+
+        private async Task ProxyHttp()
+        {
+            using (await TorProxy.StartAsync(IPAddress.Loopback, 20000))
+            {
+                var handler = new HttpClientHandler
+                {
+                    Proxy = new WebProxy("http://localhost:20000")
+                };
+
+                var client = new HttpClient(handler);
+                var googleResponse = await client.GetAsync("http://google.com/search?q=Http+Test");
+                Assert.That(googleResponse.StatusCode > 0);
+            }
+        }
+
+        [Test]
+        [Retry(MaximumRetry)]
+        public void CanProxyHiddenService()
+        {
+            Assert.DoesNotThrowAsync(ProxyHiddenService);
+        }
+
+        private async Task ProxyHiddenService()
+        {
+            using (await TorProxy.StartAsync(IPAddress.Loopback, 20000))
+            {
+                var handler = new HttpClientHandler
+                {
+                    Proxy = new WebProxy("http://localhost:20000"),
+                    ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => true
+                };
+
+                var client = new HttpClient(handler);
+                var facebookResponse = await client.GetAsync("https://facebookwkhpilnemxj7asaniu7vnjjbiltxjqhye3mhbshg7kx5tfyd.onion");
+                Assert.That(facebookResponse.StatusCode > 0);
+            }
+        }
+    }
+}
diff --git a/NOnion/Client/TorClient.fs b/NOnion/Client/TorClient.fs
new file mode 100644
index 00000000..dc8dde56
--- /dev/null
+++ b/NOnion/Client/TorClient.fs
@@ -0,0 +1,280 @@
+namespace NOnion.Client
+
+open System
+open System.Collections.Concurrent
+open System.IO
+open System.Net
+open System.Net.Http
+open System.Text.RegularExpressions
+
+open NOnion.Directory
+open NOnion.Utility
+open NOnion
+open NOnion.Network
+
+type CircuitPurpose =
+    | Unknown
+    | Exit
+
+type TorClient internal (directory: TorDirectory) =
+    static let maximumBootstrapTries = 5
+
+    static let maximumExtendByNodeRetry = 5
+
+    static let ConvertFallbackIncToList(fallbackIncString: string) =
+        let ipv4Pattern = "\"([0-9\\.]+)\\sorport=(\\S*)\\sid=(\\S*)\""
+        let matches = Regex.Matches(fallbackIncString, ipv4Pattern)
+
+        matches
+        |> Seq.cast
+        |> Seq.map(fun (regMatch: Match) ->
+            regMatch.Groups.[1].Value, int regMatch.Groups.[2].Value
+        )
+        |> Seq.toList
+
+    static let SelectRandomEndpoints(fallbackList: List<string * int>) =
+        fallbackList
+        |> SeqUtils.TakeRandom maximumBootstrapTries
+        |> Seq.map(fun (ipString, port) ->
+            let ipAddress = IPAddress.Parse ipString
+            IPEndPoint(ipAddress, port)
+        )
+        |> Seq.toList
+
+    static let BootstrapDirectory
+        (cachePath: Option<DirectoryInfo>)
+        (ipEndPointList: List<IPEndPoint>)
+        =
+        async {
+            let rec tryBootstrap(ipEndPointList: List<IPEndPoint>) =
+                async {
+                    match ipEndPointList with
+                    | ipEndPoint :: tail ->
+                        try
+                            let cacheDirectory =
+                                match cachePath with
+                                | None ->
+                                    let tempDir =
+                                        DirectoryInfo(
+                                            Path.Combine(
+                                                Path.GetTempPath(),
+                                                Path.GetFileNameWithoutExtension(
+                                                    Path.GetRandomFileName()
+                                                )
+                                            )
+                                        )
+
+                                    tempDir.Create()
+                                    tempDir
+                                | Some path -> path
+
+                            let! directory =
+                                TorDirectory.Bootstrap ipEndPoint cacheDirectory
+
+                            return directory
+                        with
+                        | :? NOnionException -> return! tryBootstrap tail
+                    | [] -> return failwith "Maximum bootstrap tries reached!"
+                }
+
+            return! tryBootstrap ipEndPointList
+        }
+
+    static let CreateClientFromFallbackString
+        (fallbackListString: string)
+        (cachePath: Option<DirectoryInfo>)
+        =
+        async {
+            let! directory =
+                fallbackListString
+                |> ConvertFallbackIncToList
+                |> SelectRandomEndpoints
+                |> BootstrapDirectory cachePath
+
+            return new TorClient(directory)
+        }
+
+    let guardsToDispose = ConcurrentBag<TorGuard>()
+
+    static member AsyncBootstrapWithEmbeddedList
+        (cachePath: Option<DirectoryInfo>)
+        =
+        async {
+            let fallbackListString =
+                EmbeddedResourceUtility.ExtractEmbeddedResourceFileContents(
+                    "fallback_dirs.inc"
+                )
+
+            return! CreateClientFromFallbackString fallbackListString cachePath
+        }
+
+    static member BootstrapWithEmbeddedListAsync
+        (cachePath: Option<DirectoryInfo>)
+        =
+        TorClient.AsyncBootstrapWithEmbeddedList cachePath |> Async.StartAsTask
+
+    static member AsyncBootstrapWithGitlab(cachePath: Option<DirectoryInfo>) =
+        async {
+            // Don't put this inside the fallbackListString or it gets disposed
+            // before the task gets executed
+            use httpClient = new HttpClient()
+
+            let! fallbackListString =
+                let urlToTorServerList =
+                    "https://gitlab.torproject.org/tpo/core/tor/-/raw/main/src/app/config/fallback_dirs.inc"
+
+                httpClient.GetStringAsync urlToTorServerList |> Async.AwaitTask
+
+            return! CreateClientFromFallbackString fallbackListString cachePath
+        }
+
+    static member BootstrapWithGitlabAsync(cachePath: Option<DirectoryInfo>) =
+        TorClient.AsyncBootstrapWithGitlab cachePath |> Async.StartAsTask
+
+    member __.Directory = directory
+
+    member internal __.AsyncCreateCircuitWithCallback
+        (hopsCount: int)
+        (purpose: CircuitPurpose)
+        (extendByNodeOpt: Option<CircuitNodeDetail>)
+        (serviceStream: uint16 -> TorCircuit -> Async<unit>)
+        =
+        async {
+            let rec createNewGuard() =
+                async {
+                    let! ipEndPoint, nodeDetail =
+                        directory.GetRouter RouterType.Guard
+
+                    try
+                        let! guard =
+                            TorGuard.NewClientWithIdentity
+                                ipEndPoint
+                                (nodeDetail.GetIdentityKey() |> Some)
+
+                        guardsToDispose.Add guard
+                        return guard, nodeDetail
+                    with
+                    | :? GuardConnectionFailedException ->
+                        return! createNewGuard()
+                }
+
+            let rec tryCreateCircuit(tryNumber: int) =
+                async {
+                    if tryNumber > maximumExtendByNodeRetry then
+                        return raise <| DestinationNodeCantBeReachedException()
+                    else
+                        try
+                            let! guard, guardDetail = createNewGuard()
+                            let circuit = TorCircuit(guard, serviceStream)
+
+                            do!
+                                circuit.Create guardDetail
+                                |> Async.Ignore<uint16>
+
+                            let rec extend
+                                (numHopsToExtend: int)
+                                (nodesSoFar: List<CircuitNodeDetail>)
+                                =
+                                async {
+                                    if numHopsToExtend > 0 then
+                                        let rec findUnusedNode() =
+                                            async {
+                                                let! _ipEndPoint, nodeDetail =
+                                                    if numHopsToExtend = 1 then
+                                                        match purpose with
+                                                        | Unknown ->
+                                                            directory.GetRouter
+                                                                RouterType.Normal
+                                                        | Exit ->
+                                                            directory.GetRouter
+                                                                RouterType.Exit
+                                                    else
+                                                        directory.GetRouter
+                                                            RouterType.Normal
+
+                                                if (List.contains
+                                                        nodeDetail
+                                                        nodesSoFar) then
+                                                    return! findUnusedNode()
+                                                else
+                                                    return nodeDetail
+                                            }
+
+                                        let! newUnusedNode = findUnusedNode()
+
+                                        do!
+                                            circuit.Extend newUnusedNode
+                                            |> Async.Ignore<uint16>
+
+                                        return!
+                                            extend
+                                                (numHopsToExtend - 1)
+                                                (newUnusedNode :: nodesSoFar)
+                                    else
+                                        ()
+                                }
+
+                            do!
+                                extend
+                                    (hopsCount - 1)
+                                    (List.singleton guardDetail)
+
+                            match extendByNodeOpt with
+                            | Some extendByNode ->
+                                try
+                                    do!
+                                        circuit.Extend extendByNode
+                                        |> Async.Ignore<uint16>
+                                with
+                                | :? NOnionException ->
+                                    return
+                                        raise
+                                        <| DestinationNodeCantBeReachedException
+                                            ()
+                            | None -> ()
+
+                            return circuit
+                        with
+                        | :? DestinationNodeCantBeReachedException ->
+                            return! tryCreateCircuit(tryNumber + 1)
+                        | ex ->
+                            match FSharpUtil.FindException<NOnionException> ex
+                                with
+                            | Some _nonionEx ->
+                                return! tryCreateCircuit tryNumber
+                            | None -> return raise <| FSharpUtil.ReRaise ex
+                }
+
+            let startTryNumber = 1
+
+            return! tryCreateCircuit startTryNumber
+        }
+
+    member self.AsyncCreateCircuit
+        (hopsCount: int)
+        (purpose: CircuitPurpose)
+        (extendByNodeOpt: Option<CircuitNodeDetail>)
+        =
+        let noop _ _ =
+            async { return () }
+
+        self.AsyncCreateCircuitWithCallback
+            hopsCount
+            purpose
+            extendByNodeOpt
+            noop
+
+    member self.CreateCircuitAsync
+        (
+            hopsCount: int,
+            purpose: CircuitPurpose,
+            extendByNode: Option<CircuitNodeDetail>
+        ) =
+        self.AsyncCreateCircuit hopsCount purpose extendByNode
+        |> Async.StartAsTask
+
+
+    interface IDisposable with
+        member __.Dispose() =
+            for guard in guardsToDispose do
+                (guard :> IDisposable).Dispose()
diff --git a/NOnion/Exceptions.fs b/NOnion/Exceptions.fs
index 98c7ab94..757f34ab 100644
--- a/NOnion/Exceptions.fs
+++ b/NOnion/Exceptions.fs
@@ -53,3 +53,6 @@ type NOnionSocketException
             "Got socket exception during data transfer",
             innerException
         )
+
+type DestinationNodeCantBeReachedException() =
+    inherit NOnionException("Destination node can't be reached")
diff --git a/NOnion/NOnion.fsproj b/NOnion/NOnion.fsproj
index 904f1ae7..f93ff533 100644
--- a/NOnion/NOnion.fsproj
+++ b/NOnion/NOnion.fsproj
@@ -13,6 +13,7 @@
 
   <ItemGroup>
     <EmbeddedResource Include="auth_dirs.inc" />
+    <EmbeddedResource Include="fallback_dirs.inc" />
     <Compile Include="TorLogger.fs" />
     <Compile Include="Constants.fs" />
     <Compile Include="DestroyReason.fs" />
@@ -85,8 +86,10 @@
     <Compile Include="Directory\HiddenServiceFirstLayerDescriptorDocument.fs" />
     <Compile Include="Directory\HiddenServiceSecondLayerDescriptorDocument.fs" />
     <Compile Include="Directory\TorDirectory.fs" />
+    <Compile Include="Client\TorClient.fs" />
     <Compile Include="Services\TorServiceHost.fs" />
     <Compile Include="Services\TorServiceClient.fs" />
+    <Compile Include="Proxy\TorProxy.fs" />
   </ItemGroup>
 
   <ItemGroup>
diff --git a/NOnion/Network/TorCircuit.fs b/NOnion/Network/TorCircuit.fs
index 39edec50..2c72bb75 100644
--- a/NOnion/Network/TorCircuit.fs
+++ b/NOnion/Network/TorCircuit.fs
@@ -1072,6 +1072,13 @@ and TorCircuit
             failwith
                 "Should not happen: can't get circuitId for non-initialized circuit."
 
+    member __.IsActive =
+        match circuitState with
+        | Ready _
+        | ReadyAsIntroductionPoint _
+        | ReadyAsRendezvousPoint _ -> true
+        | _ -> false
+
     member __.GetLastNode() =
         async {
             let! lastNodeResult =
diff --git a/NOnion/Proxy/TorProxy.fs b/NOnion/Proxy/TorProxy.fs
new file mode 100644
index 00000000..28c7dc74
--- /dev/null
+++ b/NOnion/Proxy/TorProxy.fs
@@ -0,0 +1,246 @@
+namespace NOnion.Proxy
+
+open FSharpx.Collections
+open System
+open System.IO
+open System.Net
+open System.Net.Sockets
+open System.Text
+open System.Threading
+
+open NOnion
+open NOnion.Client
+open NOnion.Network
+open NOnion.Services
+
+type TorProxy private (listener: TcpListener, torClient: TorClient) =
+    let mutable lastActiveCircuitOpt: Option<TorCircuit> = None
+
+    let handleConnection(client: TcpClient) =
+        async {
+            let! cancelToken = Async.CancellationToken
+            cancelToken.ThrowIfCancellationRequested()
+
+            let stream = client.GetStream()
+
+            let readHeaders() =
+                async {
+                    let stringBuilder = StringBuilder()
+                    // minimum request 16 bytes: GET / HTTP/1.1\r\n\r\n
+                    let preReadLen = 18
+                    let! buffer = stream.AsyncRead preReadLen
+
+                    buffer
+                    |> Encoding.ASCII.GetString
+                    |> stringBuilder.Append
+                    |> ignore<StringBuilder>
+
+                    let rec innerReadRest() =
+                        async {
+                            if stringBuilder.ToString().EndsWith("\r\n\r\n") then
+                                return ()
+                            else
+                                let! newByte = stream.AsyncRead 1
+
+                                newByte
+                                |> Encoding.ASCII.GetString
+                                |> stringBuilder.Append
+                                |> ignore<StringBuilder>
+
+                                return! innerReadRest()
+                        }
+
+                    do! innerReadRest()
+
+                    return stringBuilder.ToString()
+                }
+
+            let! headers = readHeaders()
+
+            let headerLines =
+                headers.Split(
+                    [| "\r\n" |],
+                    StringSplitOptions.RemoveEmptyEntries
+                )
+
+            match Seq.tryHeadTail headerLines with
+            | Some(firstLine, restOfHeaders) ->
+                let firstLineParts = firstLine.Split(' ')
+
+                let method = firstLineParts.[0]
+                let url = firstLineParts.[1]
+                let protocolVersion = firstLineParts.[2]
+
+                if protocolVersion <> "HTTP/1.1" then
+                    return failwith "TorProxy: protocol version mismatch"
+
+                let rec copySourceToDestination
+                    (source: Stream)
+                    (dest: Stream)
+                    =
+                    async {
+                        do! source.CopyToAsync dest |> Async.AwaitTask
+
+                        // CopyToAsync returns when source is closed so we can close dest
+                        dest.Close()
+                    }
+
+                let createStreamToDestination(parsedUrl: Uri) =
+                    async {
+                        if parsedUrl.DnsSafeHost.EndsWith(".onion") then
+                            let! client =
+                                TorServiceClient.Connect
+                                    torClient
+                                    (sprintf
+                                        "%s:%i"
+                                        parsedUrl.DnsSafeHost
+                                        parsedUrl.Port)
+
+                            return! client.GetStream()
+                        else
+                            let! circuit =
+                                match lastActiveCircuitOpt with
+                                | Some lastActiveCircuit when
+                                    lastActiveCircuit.IsActive
+                                    ->
+                                    async {
+                                        TorLogger.Log
+                                            "TorProxy: we had active circuit, no need to recreate"
+
+                                        return lastActiveCircuit
+                                    }
+                                | _ ->
+                                    async {
+                                        TorLogger.Log
+                                            "TorProxy: we didn't have an active circuit, recreating..."
+
+                                        let! circuit =
+                                            torClient.AsyncCreateCircuit
+                                                3
+                                                CircuitPurpose.Exit
+                                                None
+
+                                        lastActiveCircuitOpt <- Some circuit
+                                        return circuit
+                                    }
+
+                            let torStream = new TorStream(circuit)
+
+                            do!
+                                torStream.ConnectToOutside
+                                    parsedUrl.DnsSafeHost
+                                    parsedUrl.Port
+                                |> Async.Ignore
+
+                            return torStream
+                    }
+
+                if method <> "CONNECT" then
+                    let parsedUrl = Uri url
+
+                    use! torStream = createStreamToDestination parsedUrl
+
+                    let firstLineToRetransmit =
+                        sprintf
+                            "%s %s HTTP/1.1\r\n"
+                            method
+                            parsedUrl.PathAndQuery
+
+                    let headersToForwardLines =
+                        restOfHeaders
+                        |> Seq.filter(fun header ->
+                            not(header.StartsWith "Proxy-")
+                        )
+                        |> Seq.map(fun header -> sprintf "%s\r\n" header)
+
+                    let headersToForward =
+                        String.Join(String.Empty, headersToForwardLines)
+
+                    do!
+                        Encoding.ASCII.GetBytes firstLineToRetransmit
+                        |> torStream.AsyncWrite
+
+                    do!
+                        Encoding.ASCII.GetBytes headersToForward
+                        |> torStream.AsyncWrite
+
+                    do! Encoding.ASCII.GetBytes "\r\n" |> torStream.AsyncWrite
+
+                    return!
+                        [
+                            copySourceToDestination torStream stream
+                            copySourceToDestination stream torStream
+                        ]
+                        |> Async.Parallel
+                        |> Async.Ignore
+                else
+                    let parsedUrl = Uri <| sprintf "http://%s" url
+
+                    use! torStream = createStreamToDestination parsedUrl
+
+                    let connectResponse =
+                        "HTTP/1.1 200 Connection Established\r\nConnection: close\r\n\r\n"
+
+                    do!
+                        Encoding.ASCII.GetBytes connectResponse
+                        |> stream.AsyncWrite
+
+                    return!
+                        [
+                            copySourceToDestination torStream stream
+                            copySourceToDestination stream torStream
+                        ]
+                        |> Async.Parallel
+                        |> Async.Ignore
+            | None ->
+                return failwith "TorProxy: incomplete http header detected"
+
+        }
+
+    let rec acceptConnections() =
+        async {
+            let! cancelToken = Async.CancellationToken
+            cancelToken.ThrowIfCancellationRequested()
+
+            let! client = listener.AcceptTcpClientAsync() |> Async.AwaitTask
+
+            Async.Start(handleConnection client, cancelToken)
+
+            return! acceptConnections()
+        }
+
+    let shutdownToken = new CancellationTokenSource()
+
+    static member Start (localAddress: IPAddress) (port: int) =
+        async {
+            let! client = TorClient.AsyncBootstrapWithEmbeddedList None
+            let listener = TcpListener(localAddress, port)
+            let proxy = new TorProxy(listener, client)
+            proxy.StartListening()
+            return proxy
+        }
+
+    static member StartAsync(localAddress: IPAddress, port: int) =
+        TorProxy.Start localAddress port |> Async.StartAsTask
+
+    member private self.StartListening() =
+        listener.Start()
+
+        Async.Start(acceptConnections(), shutdownToken.Token)
+
+    member __.GetNewIdentity() =
+        async {
+            let! newCircuit =
+                torClient.AsyncCreateCircuit 3 CircuitPurpose.Exit None
+
+            lastActiveCircuitOpt <- Some newCircuit
+        }
+
+    member self.GetNewIdentityAsync() =
+        self.GetNewIdentity() |> Async.StartAsTask
+
+    interface IDisposable with
+        member __.Dispose() =
+            shutdownToken.Cancel()
+            listener.Stop()
+            (torClient :> IDisposable).Dispose()
diff --git a/NOnion/Services/TorServiceClient.fs b/NOnion/Services/TorServiceClient.fs
index 9264dfbc..d32995fc 100644
--- a/NOnion/Services/TorServiceClient.fs
+++ b/NOnion/Services/TorServiceClient.fs
@@ -12,6 +12,7 @@ open Org.BouncyCastle.Security
 
 open NOnion
 open NOnion.Cells.Relay
+open NOnion.Client
 open NOnion.Crypto
 open NOnion.Utility
 open NOnion.Directory
@@ -21,24 +22,34 @@ open NOnion.Network
 type TorServiceClient =
     private
         {
-            RendezvousGuard: TorGuard
+            TorClient: TorClient
             RendezvousCircuit: TorCircuit
-            Stream: TorStream
+            Port: int
         }
 
     member self.GetStream() =
-        self.Stream
+        async {
+            // We can't use the "use" keyword since this stream needs
+            // to outlive this function.
+            let serviceStream = new TorStream(self.RendezvousCircuit)
+            do! serviceStream.ConnectToService self.Port |> Async.Ignore
+
+            return serviceStream
+        }
 
-    static member ConnectAsync (directory: TorDirectory) (url: string) =
-        TorServiceClient.Connect directory url |> Async.StartAsTask
+    member self.GetStreamAsync() =
+        self.GetStream() |> Async.StartAsTask
 
-    static member Connect (directory: TorDirectory) (url: string) =
+    static member ConnectAsync (client: TorClient) (url: string) =
+        TorServiceClient.Connect client url |> Async.StartAsTask
+
+    static member Connect (client: TorClient) (url: string) =
         async {
             let publicKey, port = HiddenServicesUtility.DecodeOnionUrl url
 
             let getIntroductionPointInfo() =
                 async {
-                    let! networkStatus = directory.GetLiveNetworkStatus()
+                    let! networkStatus = client.Directory.GetLiveNetworkStatus()
 
                     let periodNum, periodLength = networkStatus.GetTimePeriod()
                     let srv = networkStatus.GetCurrentSRVForClient()
@@ -49,7 +60,7 @@ type TorServiceClient =
                             publicKey
 
                     let! responsibleDirs =
-                        directory.GetResponsibleHiddenServiceDirectories
+                        client.Directory.GetResponsibleHiddenServiceDirectories
                             blindedPublicKey
                             srv
                             periodNum
@@ -64,36 +75,16 @@ type TorServiceClient =
                                     raise <| DescriptorDownloadFailedException()
                             | hsDirectory :: tail ->
                                 try
-                                    let! guardEndPoint, randomGuardNode =
-                                        directory.GetRouter RouterType.Guard
-
-                                    let! _, randomMiddleNode =
-                                        directory.GetRouter RouterType.Normal
-
                                     let! hsDirectoryNode =
-                                        directory.GetCircuitNodeDetailByIdentity
+                                        client.Directory.GetCircuitNodeDetailByIdentity
                                             hsDirectory
 
-                                    use! guardNode =
-                                        TorGuard.NewClientWithIdentity
-                                            guardEndPoint
-                                            (randomGuardNode.GetIdentityKey()
-                                             |> Some)
-
-                                    let circuit = TorCircuit guardNode
-
-                                    do!
-                                        circuit.Create randomGuardNode
-                                        |> Async.Ignore
-
-                                    do!
-                                        circuit.Extend randomMiddleNode
-                                        |> Async.Ignore
-
                                     try
-                                        do!
-                                            circuit.Extend hsDirectoryNode
-                                            |> Async.Ignore
+                                        let! circuit =
+                                            client.AsyncCreateCircuit
+                                                2
+                                                CircuitPurpose.Unknown
+                                                (Some hsDirectoryNode)
 
                                         use dirStream = new TorStream(circuit)
 
@@ -394,18 +385,14 @@ type TorServiceClient =
                 .Create()
                 .GetNonZeroBytes randomGeneratedCookie
 
-            let! endpoint, guardnode = directory.GetRouter RouterType.Guard
-            let! _, rendezvousNode = directory.GetRouter RouterType.Normal
-
-            let! rendezvousGuard =
-                TorGuard.NewClientWithIdentity
-                    endpoint
-                    (guardnode.GetIdentityKey() |> Some)
+            let! _, rendezvousNode =
+                client.Directory.GetRouter RouterType.Normal
 
-            let rendezvousCircuit = TorCircuit rendezvousGuard
-
-            do! rendezvousCircuit.Create guardnode |> Async.Ignore
-            do! rendezvousCircuit.Extend rendezvousNode |> Async.Ignore
+            let! rendezvousCircuit =
+                client.AsyncCreateCircuit
+                    1
+                    CircuitPurpose.Unknown
+                    (Some rendezvousNode)
 
             do!
                 rendezvousCircuit.RegisterAsRendezvousPoint
@@ -438,7 +425,7 @@ type TorServiceClient =
                             ]
                     }
 
-                let! networkStatus = directory.GetLiveNetworkStatus()
+                let! networkStatus = client.Directory.GetLiveNetworkStatus()
                 let periodInfo = networkStatus.GetTimePeriod()
 
                 let data, macKey =
@@ -471,13 +458,11 @@ type TorServiceClient =
                                 macKey
                     }
 
-                let introCircuit = TorCircuit rendezvousGuard
-
-                do! introCircuit.Create guardnode |> Async.Ignore
-
-                do!
-                    introCircuit.Extend introductionPointNodeDetail
-                    |> Async.Ignore
+                let! introCircuit =
+                    client.AsyncCreateCircuit
+                        1
+                        Unknown
+                        (Some introductionPointNodeDetail)
 
                 let rendezvousJoin =
                     rendezvousCircuit.WaitingForRendezvousJoin
@@ -500,22 +485,13 @@ type TorServiceClient =
                     Async.Parallel [ introduceJob; rendezvousJoin ]
                     |> Async.Ignore
 
-                // We can't use the "use" keyword since this stream needs
-                // to outlive this function.
-                let serviceStream = new TorStream(rendezvousCircuit)
-                do! serviceStream.ConnectToService port |> Async.Ignore
-
                 return
                     {
-                        RendezvousGuard = rendezvousGuard
+                        TorClient = client
                         RendezvousCircuit = rendezvousCircuit
-                        Stream = serviceStream
+                        Port = port
                     }
             | _ ->
                 return
                     failwith "Never happens. GetRouter never returns FastCreate"
         }
-
-    interface IDisposable with
-        member self.Dispose() =
-            (self.RendezvousGuard :> IDisposable).Dispose()
diff --git a/NOnion/Services/TorServiceHost.fs b/NOnion/Services/TorServiceHost.fs
index 2fb2beab..25f9b595 100644
--- a/NOnion/Services/TorServiceHost.fs
+++ b/NOnion/Services/TorServiceHost.fs
@@ -19,6 +19,7 @@ open Org.BouncyCastle.Security
 open NOnion
 open NOnion.Cells.Relay
 open NOnion.Crypto
+open NOnion.Client
 open NOnion.Directory
 open NOnion.Utility
 open NOnion.Network
@@ -36,9 +37,7 @@ type IntroductionPointInfo =
 
 type TorServiceHost
     (
-        directory: TorDirectory,
-        maxDescriptorUploadRetryCount: int,
-        maxRendezvousConnectRetryCount: int,
+        client: TorClient,
         maybeMasterPrivateKey: Option<Ed25519PrivateKeyParameters>
     ) =
 
@@ -125,28 +124,19 @@ type TorServiceHost
             introEncPubKey
             =
             async {
-                let! endPoint, randomNodeDetails =
-                    directory.GetRouter RouterType.Guard
-
-                let! guard =
-                    TorGuard.NewClientWithIdentity
-                        endPoint
-                        (randomNodeDetails.GetIdentityKey() |> Some)
-
-                let rendezvousCircuit =
-                    TorCircuit(guard, self.IncomingServiceStreamCallback)
-
-                do! rendezvousCircuit.Create randomNodeDetails |> Async.Ignore
-
-                do!
-                    rendezvousCircuit.Extend(
-                        CircuitNodeDetail.Create(
-                            rendezvousEndpoint,
-                            onionKey,
-                            rendezvousFingerPrint
-                        )
+                let lastNodeDetails =
+                    CircuitNodeDetail.Create(
+                        rendezvousEndpoint,
+                        onionKey,
+                        rendezvousFingerPrint
                     )
-                    |> Async.Ignore
+
+                let! rendezvousCircuit =
+                    client.AsyncCreateCircuitWithCallback
+                        2
+                        CircuitPurpose.Unknown
+                        (Some lastNodeDetails)
+                        self.IncomingServiceStreamCallback
 
                 do!
                     rendezvousCircuit.Rendezvous
@@ -181,7 +171,7 @@ type TorServiceHost
                 introductionPointDetails.EncryptionKey.Private
                 :?> X25519PrivateKeyParameters
 
-            let! networkStatus = directory.GetLiveNetworkStatus()
+            let! networkStatus = client.Directory.GetLiveNetworkStatus()
             let periodInfo = networkStatus.GetTimePeriod()
 
             let decryptedData, macKey =
@@ -237,7 +227,7 @@ type TorServiceHost
                 | Some linkSpecifier -> linkSpecifier.Data
                 | None -> failwith "No rendezvous fingerprint found!"
 
-            let connectToRendezvousJob =
+            do!
                 tryConnectingToRendezvous
                     rendezvousEndpoint
                     rendezvousFingerPrint
@@ -248,11 +238,6 @@ type TorServiceHost
                     introEncPrivKey
                     introEncPubKey
 
-            do!
-                FSharpUtil.Retry<SocketException, NOnionException>
-                    connectToRendezvousJob
-                    maxRendezvousConnectRetryCount
-
             return ()
         }
 
@@ -265,10 +250,10 @@ type TorServiceHost
                 async {
                     try
                         let! guardEndPoint, guardNodeDetail =
-                            directory.GetRouter RouterType.Guard
+                            client.Directory.GetRouter RouterType.Guard
 
                         let! _, introNodeDetail =
-                            directory.GetRouter RouterType.Normal
+                            client.Directory.GetRouter RouterType.Normal
 
                         match introNodeDetail with
                         | FastCreate ->
@@ -368,73 +353,52 @@ type TorServiceHost
     member self.UploadDescriptor
         (directoryToUploadTo: string)
         (document: HiddenServiceFirstLayerDescriptorDocument)
-        (retry: int)
         =
         async {
-            if retry > maxDescriptorUploadRetryCount then
-                return ()
-            else
-                try
-                    let! hsDirectoryNode =
-                        directory.GetCircuitNodeDetailByIdentity
-                            directoryToUploadTo
-
-                    let! guardEndPoint, randomGuardNode =
-                        directory.GetRouter RouterType.Guard
-
-                    let! _, randomMiddleNode =
-                        directory.GetRouter RouterType.Normal
-
-                    use! guardNode =
-                        TorGuard.NewClientWithIdentity
-                            guardEndPoint
-                            (randomGuardNode.GetIdentityKey() |> Some)
-
-                    let circuit = TorCircuit guardNode
-                    do! circuit.Create randomGuardNode |> Async.Ignore
-                    do! circuit.Extend randomMiddleNode |> Async.Ignore
-                    do! circuit.Extend hsDirectoryNode |> Async.Ignore
-
-                    use dirStream = new TorStream(circuit)
-                    do! dirStream.ConnectToDirectory() |> Async.Ignore
-
-                    let! _response =
-                        TorHttpClient(
-                            dirStream,
-                            Constants.DefaultHttpHost
-                        )
-                            .PostString
-                            (sprintf
-                                "/tor/hs/%i/publish"
-                                Constants.HiddenServices.Version)
-                            (document.ToString())
-
-                    TorLogger.Log(
-                        sprintf
-                            "TorServiceHost: descriptor uploaded to node with identity %s"
-                            directoryToUploadTo
-                    )
-
-                    return ()
-                with
-                | :? UnsuccessfulHttpResponseException ->
-                    // During testing, after migration to microdescriptor, we saw instances of
-                    // 404 error msg when trying to publish our descriptors which mean for
-                    // some reason we're trying to upload descriptor to a directory that
-                    // is not a hidden service directory, there is no point in retrying here.
-                    return ()
-                | ex ->
-                    TorLogger.Log(
-                        sprintf
-                            "TorServiceHost: hs descriptor upload failed, ex=%s"
-                            (ex.ToString())
+            try
+                let! hsDirectoryNode =
+                    client.Directory.GetCircuitNodeDetailByIdentity
+                        directoryToUploadTo
+
+                let! circuit =
+                    client.AsyncCreateCircuit
+                        2
+                        CircuitPurpose.Unknown
+                        (Some hsDirectoryNode)
+
+                use dirStream = new TorStream(circuit)
+                do! dirStream.ConnectToDirectory() |> Async.Ignore
+
+                let! _response =
+                    TorHttpClient(
+                        dirStream,
+                        Constants.DefaultHttpHost
                     )
+                        .PostString
+                        (sprintf
+                            "/tor/hs/%i/publish"
+                            Constants.HiddenServices.Version)
+                        (document.ToString())
+
+                TorLogger.Log(
+                    sprintf
+                        "TorServiceHost: descriptor uploaded to node with identity %s"
+                        directoryToUploadTo
+                )
 
-                    return!
-                        self.UploadDescriptor
-                            directoryToUploadTo
-                            document
-                            (retry + 1)
+                return ()
+            with
+            | :? DestinationNodeCantBeReachedException
+            | :? UnsuccessfulHttpResponseException ->
+                // During testing, after migration to microdescriptor, we saw instances of
+                // 404 error msg when trying to publish our descriptors which mean for
+                // some reason we're trying to upload descriptor to a directory that
+                // is not a hidden service directory, there is no point in retrying here.
+
+                // TorClient tries multiple times with different circuit to connect to
+                // the directory, if destination node can't be reached with any circuit
+                // we stop trying.
+                return ()
         }
 
     member self.BuildAndUploadDescriptor
@@ -450,7 +414,7 @@ type TorServiceHost
                     (masterPublicKey.GetEncoded())
 
             let! responsibleDirs =
-                directory.GetResponsibleHiddenServiceDirectories
+                client.Directory.GetResponsibleHiddenServiceDirectories
                     blindedPublicKey
                     srv
                     periodNum
@@ -698,7 +662,7 @@ type TorServiceHost
 
             let jobs =
                 responsibleDirs
-                |> Seq.map(fun dir -> self.UploadDescriptor dir outerWrapper 1)
+                |> Seq.map(fun dir -> self.UploadDescriptor dir outerWrapper)
 
             do!
                 Async.Parallel(
@@ -756,7 +720,7 @@ type TorServiceHost
     //TODO: this should refresh every 60-120min
     member self.KeepDescriptorsUpToDate() =
         async {
-            let! networkStatus = directory.GetLiveNetworkStatus()
+            let! networkStatus = client.Directory.GetLiveNetworkStatus()
 
             let firstDescriptorBuildJob =
                 self.UpdateFirstDescriptor networkStatus
diff --git a/NOnion/Utility/HiddenServicesUtility.fs b/NOnion/Utility/HiddenServicesUtility.fs
index e729f32c..1281e1e0 100644
--- a/NOnion/Utility/HiddenServicesUtility.fs
+++ b/NOnion/Utility/HiddenServicesUtility.fs
@@ -110,20 +110,26 @@ module HiddenServicesUtility =
         //Add a fake protocol
         let parsedUrl = Uri(sprintf "http://%s" url)
 
-        //Remove .onion suffix and decode
-        let keyBytesOpt =
-            parsedUrl.DnsSafeHost.Split '.'
-            |> Seq.tryHead
-            |> Option.map Base32Util.DecodeBase32
-
-        // PublicKey (32 bytes) + Checksum (2 bytes) + Version (1 byte)
-        let expectedOnionUrlLength =
-            Constants.HiddenServices.OnionUrl.PublicKeyLength
-            + Constants.HiddenServices.OnionUrl.ChecksumLength
-            + 1
-
-        match keyBytesOpt with
-        | Some keyBytes when keyBytes.Length = expectedOnionUrlLength ->
-            keyBytes.[0 .. Constants.HiddenServices.OnionUrl.PublicKeyLength - 1],
-            parsedUrl.Port
-        | _ -> failwith "Invalid onion service url"
+        let urlParts = parsedUrl.DnsSafeHost.Split '.'
+
+        if urlParts.Length < 2 then
+            failwith "Invalid onion service url"
+        else
+            //Remove subdomains and .onion suffix and decode
+            let keyBytesOpt =
+                urlParts
+                |> Seq.tryItem(urlParts.Length - 2)
+                |> Option.map Base32Util.DecodeBase32
+
+            // PublicKey (32 bytes) + Checksum (2 bytes) + Version (1 byte)
+            let expectedOnionUrlLength =
+                Constants.HiddenServices.OnionUrl.PublicKeyLength
+                + Constants.HiddenServices.OnionUrl.ChecksumLength
+                + 1
+
+            match keyBytesOpt with
+            | Some keyBytes when keyBytes.Length = expectedOnionUrlLength ->
+                keyBytes.[0 .. Constants.HiddenServices.OnionUrl.PublicKeyLength
+                               - 1],
+                parsedUrl.Port
+            | _ -> failwith "Unable to decode onion service url"
diff --git a/NOnion/fallback_dirs.inc b/NOnion/fallback_dirs.inc
new file mode 100644
index 00000000..1302099e
--- /dev/null
+++ b/NOnion/fallback_dirs.inc
@@ -0,0 +1,1102 @@
+/* type=fallback */
+/* version=4.0.0 */
+/* timestamp=20210412000000 */
+/* source=offer-list */
+//
+// Generated on: Thu, 12 Jan 2023 16:00:16 +0000
+
+"142.132.165.154 orport=443 id=6D9E22C1F4F0E99867F98F2546A9B76D5F08B4CF"
+" ipv6=[2a01:4f8:1c1c:43c::1]:443"
+/* nickname=libreapp01 */
+/* extrainfo=0 */
+/* ===== */
+,
+"18.18.82.18 orport=9001 id=BF54EE3193751481579BA7CC7D8E1DF0A01AFB30"
+/* nickname=gesdm */
+/* extrainfo=0 */
+/* ===== */
+,
+"72.92.146.128 orport=9001 id=C5E420FAF05680EE590542AE7216C77602FE68DC"
+/* nickname=TreeFiddy */
+/* extrainfo=0 */
+/* ===== */
+,
+"116.203.17.238 orport=9001 id=28090710ABE433A47021F22208B3EC245A912900"
+/* nickname=dismail */
+/* extrainfo=0 */
+/* ===== */
+,
+"116.203.50.182 orport=8080 id=00E1649E69FF91D7F01E74A5E62EF14F7D9915E4"
+" ipv6=[2a01:4f8:1c1c:b16b::1]:8080"
+/* nickname=dragonhoard */
+/* extrainfo=0 */
+/* ===== */
+,
+"144.76.154.13 orport=9001 id=10A5EFCCD2FB9C1A4AC20FB779A5DB11B58957A7"
+" ipv6=[2a01:4f8:200:2211::2]:9001"
+/* nickname=eridanus */
+/* extrainfo=0 */
+/* ===== */
+,
+"94.46.171.245 orport=9001 id=904F36E7AFE6346F5D1D66971F920FFCC47DF120"
+/* nickname=sunandfun03 */
+/* extrainfo=0 */
+/* ===== */
+,
+"176.9.38.121 orport=9100 id=8284C8A45D22F2981C4B6287C7FB4367116E7CCE"
+" ipv6=[2a01:4f8:161:353a::2]:9100"
+/* nickname=Turik */
+/* extrainfo=0 */
+/* ===== */
+,
+"78.31.67.22 orport=9001 id=6982D789D3875C21433D6EE10838AC5FDC6BA82C"
+/* nickname=tor2lhvmct */
+/* extrainfo=0 */
+/* ===== */
+,
+"185.220.101.206 orport=443 id=EB9DD80E64DD829A5F7C7ACA5D5FEADFEBFDD847"
+" ipv6=[2a0b:f4c2:2:1::206]:443"
+/* nickname=ForPrivacyNET */
+/* extrainfo=0 */
+/* ===== */
+,
+"185.175.158.198 orport=9090 id=A489D37070A5081D814C1F112139EEF0DDC03A48"
+/* nickname=Trurangers2 */
+/* extrainfo=0 */
+/* ===== */
+,
+"87.62.96.246 orport=9032 id=E384748293FC4429E2B427360DB4F9D4C3D619D1"
+/* nickname=PXArelay02 */
+/* extrainfo=0 */
+/* ===== */
+,
+"94.130.246.106 orport=9001 id=1C0736CF3744A3B87C2D2269B8BD3388C7E60552"
+" ipv6=[2a01:4f8:10b:3344:106::106]:9001"
+/* nickname=FreedomFries2 */
+/* extrainfo=0 */
+/* ===== */
+,
+"95.214.54.80 orport=443 id=5A79BD5CC6C128D7D8DFB4969B0246794F117FC6"
+" ipv6=[2a03:cfc0:8000:7::5fd6:3647]:443"
+/* nickname=bauruine */
+/* extrainfo=0 */
+/* ===== */
+,
+"185.220.101.72 orport=9100 id=D3DFB8F9A878F44ED80E2B34F794FDF6334FC5F9"
+/* nickname=CCCStuttgartBer */
+/* extrainfo=0 */
+/* ===== */
+,
+"51.15.150.228 orport=443 id=46E0487EEEFD694CE625CC6E12D032395C01DB82"
+/* nickname=NTH100 */
+/* extrainfo=0 */
+/* ===== */
+,
+"51.15.75.120 orport=444 id=0040A5B04C7E309D37CBE7EDB2B72D3E15D057C1"
+" ipv6=[2001:bc8:1860:1329::1]:444"
+/* nickname=dc6jgk11d */
+/* extrainfo=0 */
+/* ===== */
+,
+"185.181.229.77 orport=9001 id=44730B2450213BC3E2DAA4854458D134F0644FF2"
+/* nickname=RedLightDistrict */
+/* extrainfo=0 */
+/* ===== */
+,
+"91.219.29.94 orport=9001 id=09E0BD68FBAEB7C10107DE67624D25B007FF26A2"
+/* nickname=Unnamed */
+/* extrainfo=0 */
+/* ===== */
+,
+"51.81.56.195 orport=443 id=EEDB9FEFC9165F9B41B515A282F95A574A807FBA"
+/* nickname=trash1 */
+/* extrainfo=0 */
+/* ===== */
+,
+"89.147.108.35 orport=9105 id=B30D36FBA3DD300E11916A086D7166A6BE3169FE"
+/* nickname=LessIsMore */
+/* extrainfo=0 */
+/* ===== */
+,
+"5.255.98.7 orport=443 id=37788F0C00728A44ED8202780D853223EDCA3D3F"
+" ipv6=[2a04:52c0:103:ba24::1]:443"
+/* nickname=vhult4 */
+/* extrainfo=0 */
+/* ===== */
+,
+"144.91.125.15 orport=9001 id=F15A2A08BF91017DCFB6042171635661E13A8256"
+" ipv6=[2a02:c207:2033:4966::1]:9001"
+/* nickname=WitchNode */
+/* extrainfo=0 */
+/* ===== */
+,
+"172.98.15.136 orport=9001 id=518B031A3DF503E08F6F815DB194DBD7B15C9C57"
+/* nickname=FrozenChosen */
+/* extrainfo=0 */
+/* ===== */
+,
+"138.201.123.109 orport=9001 id=2C13A54E3E8A6AFB18E0DE5890E5B08AAF5B0F36"
+/* nickname=history */
+/* extrainfo=0 */
+/* ===== */
+,
+"216.250.119.225 orport=9001 id=A56B3845D290791CB26BCE89FE4CE08CF7DC8A2C"
+/* nickname=QECm1r4 */
+/* extrainfo=0 */
+/* ===== */
+,
+"89.58.54.129 orport=443 id=3B642FF7FE3915C42E20445A1C725A75BAA0A9E3"
+" ipv6=[2a03:4000:69:e40::1]:443"
+/* nickname=TorRelay1a */
+/* extrainfo=0 */
+/* ===== */
+,
+"185.232.70.209 orport=443 id=D36EFFB24481694E384787FAAB80F56AAA5CCD9A"
+" ipv6=[2a03:4000:4e:f48:54ac:99ff:fee4:b238]:443"
+/* nickname=08eRPfaL2Relay2 */
+/* extrainfo=0 */
+/* ===== */
+,
+"109.202.212.1 orport=9001 id=196ABA69C056520E041FAE26EFB329940AB110AF"
+" ipv6=[2001:1620:4:d401:29:64aa:f77f:d8e0]:9001"
+/* nickname=habo01 */
+/* extrainfo=0 */
+/* ===== */
+,
+"185.195.71.3 orport=443 id=3C5915348D731505C48112F4F03235FDE7B8C837"
+/* nickname=AccessNow001 */
+/* extrainfo=0 */
+/* ===== */
+,
+"212.83.43.93 orport=443 id=B86137AE9681701901C6720E55C16805B46BD8E3"
+" ipv6=[2a00:f48:2000:1038:72a7:e81a:29ff:abba]:443"
+/* nickname=BeastieJoy60 */
+/* extrainfo=0 */
+/* ===== */
+,
+"79.137.196.223 orport=9091 id=47A77C3BC4E50D6DE2D3BF24046C57A72ED1E45F"
+/* nickname=VitaminCoin */
+/* extrainfo=0 */
+/* ===== */
+,
+"91.208.162.197 orport=8081 id=D3D2E42D8E625D36489015AB1E8081CE520DF2FE"
+" ipv6=[2001:678:6d4:5100::a1b]:8081"
+/* nickname=pissnissemoldova */
+/* extrainfo=0 */
+/* ===== */
+,
+"45.91.77.77 orport=443 id=C2B6ADF4EFEA73CA2EFB2789E2B74C1034C1F5F2"
+" ipv6=[2a0f:4ac4:42::704:c0ca:1eaf]:443"
+/* nickname=c0ca1eaf */
+/* extrainfo=0 */
+/* ===== */
+,
+"82.66.61.19 orport=995 id=40FA09C151C3893B7018DEF55A9854BC9768B82C"
+" ipv6=[2a01:e0a:5d6:6de0:acab:3:3:3]:995"
+/* nickname=Abeille */
+/* extrainfo=0 */
+/* ===== */
+,
+"185.243.218.27 orport=8443 id=5B9086D4BFB9EA36C95897DAED72FC3973847B43"
+" ipv6=[2a03:94e0:ffff:185:243:218:0:27]:8443"
+/* nickname=bauruine */
+/* extrainfo=0 */
+/* ===== */
+,
+"212.83.61.218 orport=443 id=D0562BB74A5CC8872D11B222677009A8922FC38C"
+/* nickname=mitropoulos */
+/* extrainfo=0 */
+/* ===== */
+,
+"37.200.99.251 orport=9001 id=F6EC46933CE8D4FAD5CCDAA8B1C5A377685FC521"
+" ipv6=[2a00:1158:3::1ba]:9001"
+/* nickname=JPsi2 */
+/* extrainfo=0 */
+/* ===== */
+,
+"94.230.208.147 orport=8443 id=311A4533F7A2415F42346A6C8FA77E6FD279594C"
+" ipv6=[2a02:418:6017::147]:8443"
+/* nickname=DigiGesTor3e2 */
+/* extrainfo=0 */
+/* ===== */
+,
+"54.36.120.156 orport=443 id=D0273C8566CC9AECE4C762376C9B066FE0F1DADD"
+/* nickname=Kimchi */
+/* extrainfo=0 */
+/* ===== */
+,
+"5.45.102.119 orport=9100 id=189C44DD06312D6DF8FB57A944E6819FF245740C"
+" ipv6=[2a03:4000:6:608:942a:42ff:fe77:728c]:9100"
+/* nickname=Quetzalcoatl */
+/* extrainfo=0 */
+/* ===== */
+,
+"199.249.230.74 orport=443 id=9C1E7D92115D431385B8CAEA6A7C15FB89CE236B"
+" ipv6=[2620:7:6001::ffff:c759:e64a]:80"
+/* nickname=QuintexAirVPN21 */
+/* extrainfo=0 */
+/* ===== */
+,
+"145.239.206.31 orport=8001 id=912A7C57E05606AF602EC63D1D06BBD5C7AAE516"
+" ipv6=[2001:41d0:800:b1f::]:8001"
+/* nickname=0x3d02 */
+/* extrainfo=0 */
+/* ===== */
+,
+"144.217.90.187 orport=9001 id=7040C1F5728746C5FB5E12845101A26EE8636D7E"
+" ipv6=[2607:5300:201:3100::5b0d]:9001"
+/* nickname=SaruTorUmidanuki */
+/* extrainfo=0 */
+/* ===== */
+,
+"138.201.250.33 orport=9011 id=2BA2C8E96B2590E1072AECE2BDB5C48921BF8510"
+/* nickname=storm */
+/* extrainfo=0 */
+/* ===== */
+,
+"87.118.116.103 orport=443 id=26C28F29B611DF4DE23ACF5D9DC1EB4895EF5E8B"
+" ipv6=[2001:1b60:3:221:4134:101:0:1]:443"
+/* nickname=artikel5ev4 */
+/* extrainfo=0 */
+/* ===== */
+,
+"199.249.230.110 orport=443 id=7FA8E7E44F1392A4E40FFC3B69DB3B00091B7FD3"
+" ipv6=[2620:7:6001::110]:80"
+/* nickname=Quintex20 */
+/* extrainfo=0 */
+/* ===== */
+,
+"199.249.230.82 orport=443 id=A0DB820FEC87C0405F7BF05DEE5E4ADED2BB9904"
+" ipv6=[2620:7:6001::ffff:c759:e652]:80"
+/* nickname=QuintexAirVPN29 */
+/* extrainfo=0 */
+/* ===== */
+,
+"5.2.72.110 orport=9001 id=5847D5A01C47166143F738C7703344517B39EB10"
+/* nickname=7342426259276943 */
+/* extrainfo=0 */
+/* ===== */
+,
+"50.39.143.177 orport=59090 id=A1C515432EF6BF2E699A6184ED78DF0B9A595655"
+/* nickname=lilonionboi */
+/* extrainfo=0 */
+/* ===== */
+,
+"174.128.250.163 orport=443 id=F7A052D4EEA2F4BC942DFB054AF2DC54A2A37E5D"
+/* nickname=rockstars1 */
+/* extrainfo=0 */
+/* ===== */
+,
+"163.172.213.212 orport=10010 id=CB7DB13172E15D4AD7F9404667021DF7DF6E9A4A"
+/* nickname=noconname */
+/* extrainfo=0 */
+/* ===== */
+,
+"65.108.10.141 orport=9001 id=7B46F20449D6F25150E189428B62E1E3BA5848A9"
+" ipv6=[2a01:4f9:6a:4642::2]:9001"
+/* nickname=galtlandeu */
+/* extrainfo=0 */
+/* ===== */
+,
+"185.112.156.72 orport=443 id=089E8959D386C4DC283FFDA2B590A65FCE85C8B4"
+/* nickname=TheAdoptee */
+/* extrainfo=0 */
+/* ===== */
+,
+"15.235.29.234 orport=9001 id=1B3098A711D00ECD428576EB2E868ED4D22C96B1"
+/* nickname=BSSP08 */
+/* extrainfo=0 */
+/* ===== */
+,
+"135.125.202.252 orport=9001 id=720ABE4554C55EE6F6099491CA55D1F5550512C5"
+/* nickname=slotor02 */
+/* extrainfo=0 */
+/* ===== */
+,
+"135.148.52.231 orport=443 id=BA6E064596B86AF9F55F0603A82C90E958E86E7A"
+/* nickname=twoandtwoone */
+/* extrainfo=0 */
+/* ===== */
+,
+"92.222.79.186 orport=443 id=78AA54A1B4169242310499DD81C72EF519B070F3"
+/* nickname=CebolaServer */
+/* extrainfo=0 */
+/* ===== */
+,
+"213.144.135.21 orport=443 id=BB5FAE50BCE5B13C810CD17A931A0498E4681D41"
+" ipv6=[2a02:168:6a16:1130::32:101]:443"
+/* nickname=YoYuD1N01NoExit */
+/* extrainfo=0 */
+/* ===== */
+,
+"139.162.166.237 orport=443 id=8BEA5AB8F95DE1920775A1A8F4C34B947A7DE505"
+/* nickname=shadowdancer */
+/* extrainfo=0 */
+/* ===== */
+,
+"95.216.101.247 orport=443 id=38CC95A8CE92A591D4A5779359BEFFBA13FA1B88"
+" ipv6=[2a01:4f9:2b:151f:95:216:101:247]:443"
+/* nickname=TykRelay02 */
+/* extrainfo=0 */
+/* ===== */
+,
+"191.252.111.55 orport=443 id=1D1FA50D605FDC8F6DC39A0A60A7233DD35D0001"
+/* nickname=K4M1K4Z3 */
+/* extrainfo=0 */
+/* ===== */
+,
+"131.188.40.188 orport=11180 id=EBE718E1A49EE229071702964F8DB1F318075FF8"
+" ipv6=[2001:638:a000:4140::ffff:188]:11180"
+/* nickname=fluxe4 */
+/* extrainfo=0 */
+/* ===== */
+,
+"50.236.201.218 orport=9001 id=B5053ABFF845C96B1DD8F45DCF32E6BE1E63F127"
+/* nickname=Stephen304 */
+/* extrainfo=0 */
+/* ===== */
+,
+"5.182.211.119 orport=9001 id=DD7BD3E5BD0BA48A8C70B0CDF017FA5988B87E27"
+/* nickname=Assange014nl */
+/* extrainfo=0 */
+/* ===== */
+,
+"209.51.188.48 orport=443 id=CEE804FA03A87A65CAA6BCB3A250B5ED923C08BD"
+" ipv6=[2001:470:142:5::48]:443"
+/* nickname=FSF */
+/* extrainfo=0 */
+/* ===== */
+,
+"185.228.136.146 orport=9100 id=F0D01EB1FDC508279AB3412AF3FC950BB1DA2AD1"
+" ipv6=[2a03:4000:23:50:c8ed:bcff:fe13:ee61]:9100"
+/* nickname=Quetzalcoatl */
+/* extrainfo=0 */
+/* ===== */
+,
+"89.58.52.189 orport=9001 id=C3277FBEAB946672D468E90A16BB027B4CACC537"
+" ipv6=[2a03:4000:69:d5d::2]:9001"
+/* nickname=itrickl02 */
+/* extrainfo=0 */
+/* ===== */
+,
+"188.40.147.177 orport=443 id=1D702DA43D588FE9D308D3879A6F5E61BB2ECCFA"
+/* nickname=schumacher */
+/* extrainfo=0 */
+/* ===== */
+,
+"82.220.38.150 orport=443 id=A4DE3FC1ACEC3767F5C4049BCEF57317E7B0583C"
+/* nickname=eclipse12 */
+/* extrainfo=0 */
+/* ===== */
+,
+"65.39.97.13 orport=9001 id=0D1AD0392583CBC22B5F712165D9D752D35F0699"
+/* nickname=TorNodeAlpha */
+/* extrainfo=0 */
+/* ===== */
+,
+"23.137.251.61 orport=8443 id=B558F456FB410E6CDF3D33AC5EB5305D66DA8B19"
+" ipv6=[2602:fc24:13:e::fefe]:8443"
+/* nickname=bauruine */
+/* extrainfo=0 */
+/* ===== */
+,
+"81.6.40.139 orport=995 id=E36536404200A74930DB165858BD5BB554D2BEA2"
+" ipv6=[2a02:168:2000:5f:7b5c:fee4:84b2:30b]:995"
+/* nickname=naiveTorer */
+/* extrainfo=0 */
+/* ===== */
+,
+"185.194.141.178 orport=9100 id=1B6BCBCDB384364B6FB4F3576CA70AECFC083641"
+" ipv6=[2a03:4000:1c:7e3:e8aa:c8ff:fe36:66ee]:9100"
+/* nickname=Quetzalcoatl */
+/* extrainfo=0 */
+/* ===== */
+,
+"185.220.101.206 orport=8443 id=ADF0D51946DA3294C1F242B0ACADC91FF5F058EF"
+" ipv6=[2a0b:f4c2:2:1::206]:8443"
+/* nickname=ForPrivacyNET */
+/* extrainfo=0 */
+/* ===== */
+,
+"158.69.207.216 orport=9001 id=6565F31D9EC0C7DFFEA1920BE3BA4C73EF35B5C4"
+" ipv6=[2607:5300:201:3000::dfc]:9001"
+/* nickname=oscar */
+/* extrainfo=0 */
+/* ===== */
+,
+"199.249.230.141 orport=443 id=B7ECD9C6A910A170B55165742049CBCC777494F2"
+" ipv6=[2620:7:6001::141]:80"
+/* nickname=Quintex52 */
+/* extrainfo=0 */
+/* ===== */
+,
+"178.175.148.205 orport=9001 id=8769AF6DCC45DAB9C219CD9F464EAE3268550CFD"
+" ipv6=[2a00:1dc0:caff:ad::e746]:9001"
+/* nickname=yellowsun */
+/* extrainfo=0 */
+/* ===== */
+,
+"51.15.59.15 orport=444 id=B1F926DA3895A89AF288623F5A4F913979299C53"
+" ipv6=[2001:bc8:1860:1419::1]:444"
+/* nickname=artikel5ev13b */
+/* extrainfo=0 */
+/* ===== */
+,
+"192.42.116.17 orport=443 id=0485027A0A349D454D978F6C1CECDD29EA17769A"
+" ipv6=[2001:67c:6ec:203:218:33ff:fe44:5517]:443"
+/* nickname=hviv117 */
+/* extrainfo=0 */
+/* ===== */
+,
+"193.108.118.218 orport=443 id=79D9E66BB2FDBF25E846B635D8248FE1194CFD26"
+" ipv6=[2604:86c0:f001:2:9baf:37c2:e99e:babe]:443"
+/* nickname=BeastieJoy65 */
+/* extrainfo=0 */
+/* ===== */
+,
+"83.78.216.58 orport=43633 id=7ACCD860B360118F43275FCF215F6A4C9D5E016B"
+/* nickname=bauruine */
+/* extrainfo=0 */
+/* ===== */
+,
+"23.154.177.3 orport=443 id=76CA636C1D33E3E8630B7AC22A1D07420FCE8761"
+/* nickname=UnredactedManning */
+/* extrainfo=0 */
+/* ===== */
+,
+"213.149.82.60 orport=9001 id=45D8C1EEFF044043AA6806C4B9130F8F189EF316"
+" ipv6=[2a02:2488:4211:3400::3]:9001"
+/* nickname=Feuermagier */
+/* extrainfo=0 */
+/* ===== */
+,
+"49.12.225.94 orport=9001 id=EE2A9108A9EDBFCB5C44F2993267573188176AA4"
+/* nickname=myzwiebel03 */
+/* extrainfo=0 */
+/* ===== */
+,
+"172.107.202.142 orport=443 id=036E015AD84D3C605666E3A3306B1E3E18A89482"
+/* nickname=parsel */
+/* extrainfo=0 */
+/* ===== */
+,
+"94.199.217.225 orport=443 id=05D01D243BB76468B80670CCF8F07F5E9296736D"
+/* nickname=madblock */
+/* extrainfo=0 */
+/* ===== */
+,
+"185.233.100.23 orport=443 id=F47B13BFCE4EF48CDEF6C4D7C7A99208EBB972B5"
+" ipv6=[2a0c:e300::23]:443"
+/* nickname=Elenagb */
+/* extrainfo=0 */
+/* ===== */
+,
+"213.135.244.242 orport=24071 id=2D938F19EAF660D902C656B5E6002F39B45C4BE4"
+/* nickname=VoxBox */
+/* extrainfo=0 */
+/* ===== */
+,
+"45.132.246.38 orport=9001 id=431702B3A68A6015F9955DD4FD0129175B43EA0F"
+" ipv6=[2a03:4000:48:182:74dd:c1ff:fea8:d21e]:9001"
+/* nickname=bituman */
+/* extrainfo=0 */
+/* ===== */
+,
+"82.223.23.176 orport=443 id=C22A591FF4EE577C408DAFD26C75302615E5165E"
+" ipv6=[2001:ba0:1800:8188::1]:443"
+/* nickname=FrankyThePooper */
+/* extrainfo=0 */
+/* ===== */
+,
+"23.175.32.11 orport=443 id=56BAF2F6CAE76B1AC6C1F08C148D04C219E85E70"
+" ipv6=[2606:d680:cafe:80:23:175:32:11]:443"
+/* nickname=Wat1E1TorNodeIo */
+/* extrainfo=0 */
+/* ===== */
+,
+"91.203.5.165 orport=443 id=AF1F15819AC766D6508A2B05DA989E99AB511F9F"
+/* nickname=duchin */
+/* extrainfo=0 */
+/* ===== */
+,
+"96.126.105.219 orport=5353 id=60A5547B2203DD2E148EF9BDD6FF3891081C5DF4"
+" ipv6=[2600:3c03::f03c:91ff:fe93:5318]:5353"
+/* nickname=HotPotato */
+/* extrainfo=0 */
+/* ===== */
+,
+"212.32.240.165 orport=9001 id=CFDF99EE1923D870329F8DAE54398FD45409F01F"
+/* nickname=UlhasTor */
+/* extrainfo=0 */
+/* ===== */
+,
+"185.140.251.125 orport=9001 id=215CC28D7E273AE3308F041E45A8EE3BA6D85658"
+/* nickname=HoarseSupernova */
+/* extrainfo=0 */
+/* ===== */
+,
+"38.145.211.218 orport=9010 id=3FAFFA13BC88AB28EBA464A902FDED2EA453A581"
+" ipv6=[2605:f700:40:401::2efa:261a]:9010"
+/* nickname=edaks */
+/* extrainfo=0 */
+/* ===== */
+,
+"217.112.131.24 orport=443 id=7896A8075D51F60B950D8E63AAC2899731060843"
+/* nickname=licinius */
+/* extrainfo=0 */
+/* ===== */
+,
+"46.38.232.203 orport=443 id=8B3A07D9155E76BB4AE922C8F99AE3AB7D88DD23"
+/* nickname=ConcordiaConstanzia */
+/* extrainfo=0 */
+/* ===== */
+,
+"194.76.227.152 orport=9001 id=2F98E853A570AC7A79B4082364B781AD67705074"
+/* nickname=FireMateria */
+/* extrainfo=0 */
+/* ===== */
+,
+"192.99.43.171 orport=9001 id=C92ECAF73512E2CCB15827A192B7AF3E9DBC896E"
+/* nickname=Unnamed */
+/* extrainfo=0 */
+/* ===== */
+,
+"104.244.72.20 orport=9001 id=799E0B28F45548F545668A78DF04CD23490EC585"
+" ipv6=[2605:6400:30:efe6:1313:cafe:dead:beef]:9001"
+/* nickname=Hermes */
+/* extrainfo=0 */
+/* ===== */
+,
+"89.58.4.238 orport=9001 id=4745ACB16234385EF1694D530E109F7A573E30C6"
+" ipv6=[2a03:4000:5e:d48:acab::10]:9001"
+/* nickname=tweinode3 */
+/* extrainfo=0 */
+/* ===== */
+,
+"51.15.54.117 orport=443 id=547E6E68ADE1B6F492C44443588A939610401DFB"
+" ipv6=[2001:bc8:1820:c0d::1]:443"
+/* nickname=taki */
+/* extrainfo=0 */
+/* ===== */
+,
+"37.235.48.247 orport=7654 id=1F2077BF01CAF23F819D4892A89883196ABA842A"
+" ipv6=[2a03:f80:48:37:235:48:247:1]:7654"
+/* nickname=plutoa */
+/* extrainfo=0 */
+/* ===== */
+,
+"51.159.184.219 orport=9001 id=6BE5AA34A5C391724677A3FDC2AA77B3768F6E26"
+" ipv6=[2001:bc8:1200:4137::1]:9001"
+/* nickname=tormaumauhosting */
+/* extrainfo=0 */
+/* ===== */
+,
+"85.239.34.40 orport=9001 id=8E97A9FDBC262A2DB1C87B53F12CF1925866F355"
+" ipv6=[2001:678:6d4:7410::12e]:9001"
+/* nickname=itsnotjesse */
+/* extrainfo=0 */
+/* ===== */
+,
+"198.251.68.144 orport=9001 id=83AEDBDB4BE3AD0ED91850BF1A521B843077759E"
+/* nickname=focaltohr */
+/* extrainfo=0 */
+/* ===== */
+,
+"89.58.3.65 orport=443 id=FF8B7CAD5F508972509D79F933FB24D2F524AB75"
+" ipv6=[2a03:4000:5d:b8f:1478:68ff:fec4:27c7]:443"
+/* nickname=einNettesRelay */
+/* extrainfo=0 */
+/* ===== */
+,
+"172.106.112.50 orport=443 id=996F4CFD78130203B80E854A4EF6CA2355C6C72C"
+/* nickname=Reeses1 */
+/* extrainfo=0 */
+/* ===== */
+,
+"37.252.190.176 orport=443 id=81A59766272894D27FE8375C4F83A6BA453671EF"
+/* nickname=chutney */
+/* extrainfo=0 */
+/* ===== */
+,
+"185.25.51.138 orport=9001 id=D2380745937A994FE1E19859CBEBF181DBB80443"
+" ipv6=[2a04:2180:0:1::fb6f:72f2]:9001"
+/* nickname=IndigoMagick */
+/* extrainfo=0 */
+/* ===== */
+,
+"93.95.231.115 orport=9001 id=3417F1F24A7CA4033DB514610321A1A9F410CC31"
+/* nickname=MetalsAG */
+/* extrainfo=0 */
+/* ===== */
+,
+"104.192.3.74 orport=443 id=849626A2A3DD1364E8C51423D6F3213A3AE16FFD"
+" ipv6=[2605:aa80:0:9::3]:443"
+/* nickname=OnionsHaveLayers */
+/* extrainfo=0 */
+/* ===== */
+,
+"193.108.117.103 orport=9001 id=438F3EA4C9FB0DB63F5377A3271AB5435FAD7E04"
+/* nickname=dolemite */
+/* extrainfo=0 */
+/* ===== */
+,
+"68.67.32.33 orport=9001 id=4338C8026D468B811D3EB11AE9E421E2089B8239"
+/* nickname=kgXuCTCWVMALFMb74Ld */
+/* extrainfo=0 */
+/* ===== */
+,
+"199.249.230.118 orport=443 id=5F4CD12099AF20FAF9ADFDCEC65316A376D0201C"
+" ipv6=[2620:7:6001::118]:80"
+/* nickname=QuintexAirVPN7 */
+/* extrainfo=0 */
+/* ===== */
+,
+"140.78.100.42 orport=5443 id=9D970B7FBAC353D8F6049AD4E0CEE06BBDE4E17E"
+/* nickname=INSRelay42at5443 */
+/* extrainfo=0 */
+/* ===== */
+,
+"190.211.254.182 orport=9001 id=3B557E3F0C29D4339A904AD8C641F582151FEF71"
+/* nickname=ForestIsland */
+/* extrainfo=0 */
+/* ===== */
+,
+"74.82.47.194 orport=9001 id=80E23F24D5BE0195D2827557D260D1676DEA5451"
+" ipv6=[2001:470:1:908::9001]:9001"
+/* nickname=deadbabecafe */
+/* extrainfo=0 */
+/* ===== */
+,
+"130.61.174.201 orport=9001 id=519351E3D54202933F85E608D88484A5DC4E4EF0"
+/* nickname=bigman */
+/* extrainfo=0 */
+/* ===== */
+,
+"185.220.101.203 orport=8443 id=8F11B2E253CEC4C5C463BF38AB1CA645B7294D52"
+" ipv6=[2a0b:f4c2:2:1::203]:8443"
+/* nickname=ForPrivacyNET */
+/* extrainfo=0 */
+/* ===== */
+,
+"185.241.208.204 orport=9000 id=9A8902B985E2F58BC740671040E7165AC904DD40"
+/* nickname=Aramis */
+/* extrainfo=0 */
+/* ===== */
+,
+"85.208.144.164 orport=443 id=B13C2C569F3FD0C530B7D96E5FF7933DF7A0E834"
+" ipv6=[2a09:8740:0:3::13:4008]:443"
+/* nickname=porte */
+/* extrainfo=0 */
+/* ===== */
+,
+"185.14.30.57 orport=9001 id=58FC2AAB3792AC37897D34331F4F4E00341DEC0C"
+" ipv6=[2a02:27ab:0:2::22]:9001"
+/* nickname=zwewwlNL1 */
+/* extrainfo=0 */
+/* ===== */
+,
+"193.56.240.157 orport=443 id=8E6225BC8A770DF63B20A2FDAC1ABCD795A18987"
+/* nickname=skaalz */
+/* extrainfo=0 */
+/* ===== */
+,
+"185.244.195.103 orport=9100 id=DFA97DED4CE79FF6F31DAF917C2810CCE8729E9D"
+" ipv6=[2a03:4000:27:713:4489:4cff:feab:96fc]:9100"
+/* nickname=Quetzalcoatl */
+/* extrainfo=0 */
+/* ===== */
+,
+"155.248.212.228 orport=443 id=E5E553F51D82035A2CE555DBC7D883FAA32ED0B5"
+" ipv6=[2603:c024:c000:e400:5d76:a308:5c3a:b70]:443"
+/* nickname=yyzz1 */
+/* extrainfo=0 */
+/* ===== */
+,
+"95.216.27.105 orport=9001 id=0B45375A2CE8065E8A649D52CC35F39D128745A8"
+" ipv6=[2a01:4f9:2a:1b96:2::2]:443"
+/* nickname=Ranlvor */
+/* extrainfo=0 */
+/* ===== */
+,
+"185.220.101.68 orport=9000 id=42438E63C78CF0624BD7D212524FFE292D8FE3D5"
+/* nickname=CCCStuttgartBer */
+/* extrainfo=0 */
+/* ===== */
+,
+"103.28.52.93 orport=443 id=C5A6FEE5BC3BE19F5B9EB086CA95DAD393D8A4F6"
+/* nickname=jivin */
+/* extrainfo=0 */
+/* ===== */
+,
+"82.66.10.17 orport=19001 id=8F7521CEDA9AB705A42254E1E829268DEF57C70E"
+/* nickname=bigbrother23470 */
+/* extrainfo=0 */
+/* ===== */
+,
+"185.247.226.98 orport=443 id=9AB4B4F5B279DC611BEB62E4528EB91F59A6BB14"
+" ipv6=[2a06:1700:2:17:0:5232:2d:4432]:443"
+/* nickname=R2D2 */
+/* extrainfo=0 */
+/* ===== */
+,
+"178.27.106.248 orport=9031 id=9C305BC09852C7CB62E9A41F9ECA108BBFB23521"
+/* nickname=Feidhlim04 */
+/* extrainfo=0 */
+/* ===== */
+,
+"140.78.100.28 orport=5443 id=0DBA891A70AE95D4AD77593A936E6C04ABF2E7CE"
+/* nickname=INSRelay28at5443 */
+/* extrainfo=0 */
+/* ===== */
+,
+"65.108.136.183 orport=443 id=C3F7F3E1E32A64B22B2F0734E7C7A312F993D102"
+" ipv6=[2a01:4f9:6b:3408::4]:443"
+/* nickname=arbitraryKenzie4 */
+/* extrainfo=0 */
+/* ===== */
+,
+"51.15.185.201 orport=443 id=1079E628FC6B0025656AC024F2D9975C441498CD"
+/* nickname=PoochySloochy */
+/* extrainfo=0 */
+/* ===== */
+,
+"171.25.193.234 orport=80 id=CF1C1804C33CD69D8A75587FABC63D5D0E2980FA"
+" ipv6=[2001:67c:289c:2::234]:80"
+/* nickname=DFRI10 */
+/* extrainfo=0 */
+/* ===== */
+,
+"199.249.230.105 orport=443 id=9F2856F6D2B89AD4EF6D5723FAB167DB5A53519A"
+" ipv6=[2620:7:6001::105]:80"
+/* nickname=Quintex15 */
+/* extrainfo=0 */
+/* ===== */
+,
+"89.236.112.100 orport=443 id=00CCE6A84E6D63A1A42E105839BC8ED5D4B16669"
+/* nickname=effiorg1984 */
+/* extrainfo=0 */
+/* ===== */
+,
+"209.209.11.184 orport=31289 id=21FFF594CFE691A4A03B828E9597A9F74F878053"
+" ipv6=[2602:ffd5:1:112::1]:31288"
+/* nickname=Woodman */
+/* extrainfo=0 */
+/* ===== */
+,
+"93.95.230.85 orport=443 id=3B20B5D120AB8CC1780F43216DC9C6051ED5C387"
+/* nickname=Unnamed */
+/* extrainfo=0 */
+/* ===== */
+,
+"84.158.138.52 orport=10222 id=C4C462506D54EDC8EFC8E88E32F4AE014755035A"
+/* nickname=s0yb3an */
+/* extrainfo=0 */
+/* ===== */
+,
+"89.147.108.62 orport=443 id=F3A9588FB45F76DA4DE5B350C425C130F6FFA983"
+/* nickname=Reichsfunkmast2 */
+/* extrainfo=0 */
+/* ===== */
+,
+"46.4.103.29 orport=9001 id=46DB04323499DD535956531DF2BF7B03EB2AB15D"
+" ipv6=[2a01:4f8:140:94d6::2]:9001"
+/* nickname=mickymouse */
+/* extrainfo=0 */
+/* ===== */
+,
+"199.249.230.168 orport=443 id=F664E5E50B4D216E5940DA7E9CF653F5F9DC561B"
+" ipv6=[2620:7:6001::168]:80"
+/* nickname=Quintex79 */
+/* extrainfo=0 */
+/* ===== */
+,
+"185.239.222.245 orport=443 id=3DCECAF7089B1C2CE3EA9504EE05CE754F4CF9A8"
+" ipv6=[2a09:2681:101:9001::6]:443"
+/* nickname=BM06 */
+/* extrainfo=0 */
+/* ===== */
+,
+"94.16.117.97 orport=9000 id=13FBC97516DC854399E70BC7CA9A4513FFD4F08C"
+" ipv6=[2a03:4000:29:1:d4b9:91ff:fe6e:e48a]:9000"
+/* nickname=Quetzalcoatl */
+/* extrainfo=0 */
+/* ===== */
+,
+"46.163.76.170 orport=9001 id=3E2C933BE54585C760FD6D2F3FA7A33E373A7145"
+" ipv6=[2a01:488:66:1000:2ea3:4caa:0:1]:9001"
+/* nickname=judas */
+/* extrainfo=0 */
+/* ===== */
+,
+"204.85.191.8 orport=443 id=7C0AA4E3B73E407E9F5FEB1912F8BE26D8AA124D"
+/* nickname=ibibUNC0 */
+/* extrainfo=0 */
+/* ===== */
+,
+"109.190.177.33 orport=9999 id=A8874E2C45F445DBA462A914ED8D3AF045734FFB"
+/* nickname=computel */
+/* extrainfo=0 */
+/* ===== */
+,
+"176.10.119.69 orport=9001 id=FFC3F4BE4D5C392246DC7A37B256A6158C3D8562"
+/* nickname=euler */
+/* extrainfo=0 */
+/* ===== */
+,
+"162.251.116.106 orport=443 id=24A818D9F1E09F1845FC1589DE5AAF15C8E0867E"
+/* nickname=goingin */
+/* extrainfo=0 */
+/* ===== */
+,
+"198.50.238.128 orport=443 id=C7946D9A192BBE44C1C8A926A8B135AC495D6636"
+/* nickname=t0adwarri0r */
+/* extrainfo=0 */
+/* ===== */
+,
+"157.90.92.115 orport=9001 id=AC633C90E126E0BCA96F14ECE5D222B586FA0D56"
+" ipv6=[2a01:4f8:252:3df0::2]:9001"
+/* nickname=SODrelay */
+/* extrainfo=0 */
+/* ===== */
+,
+"132.145.22.208 orport=443 id=A8FB73D917B7C2B851A358729359E13EBA5978FA"
+" ipv6=[2603:c020:c007:cab:5235:2d:534b:31]:443"
+/* nickname=R5SK1 */
+/* extrainfo=0 */
+/* ===== */
+,
+"91.203.144.194 orport=443 id=B1B687C3C4EF46249D638DCE77DDC7AAA39F2996"
+/* nickname=yetiready */
+/* extrainfo=0 */
+/* ===== */
+,
+"204.85.191.9 orport=443 id=C59E079437340E3AD14E6785C0A91A5B6F328566"
+/* nickname=ibibUNC1 */
+/* extrainfo=0 */
+/* ===== */
+,
+"46.232.250.51 orport=443 id=D1C60F9BCF2DBA07A775978F66C9927D3A9490BB"
+" ipv6=[2a03:4000:2b:673:24da:28ff:feb5:e5c5]:443"
+/* nickname=juggernautrelay */
+/* extrainfo=0 */
+/* ===== */
+,
+"45.155.168.206 orport=443 id=6F9B7403B4CD1F48269730D1F9ED0E7A75C1F049"
+/* nickname=WizardFair */
+/* extrainfo=0 */
+/* ===== */
+,
+"185.243.218.27 orport=443 id=A5E42F1A3AFA948A7F2FDB1954A4CF6C6489D418"
+" ipv6=[2a03:94e0:ffff:185:243:218:0:27]:443"
+/* nickname=bauruine */
+/* extrainfo=0 */
+/* ===== */
+,
+"51.75.129.204 orport=443 id=2CE9BE1FC88B9D0FA03F387C9E4F000B5D4B2AE9"
+/* nickname=ridin */
+/* extrainfo=0 */
+/* ===== */
+,
+"188.68.36.28 orport=9001 id=7260949459C2D4D4A5ABC3AC10C1AD6939E6525C"
+" ipv6=[2a03:4000:13:7c3:a401:f1ff:fe45:7ffc]:9001"
+/* nickname=Piratenpartei05 */
+/* extrainfo=0 */
+/* ===== */
+,
+"82.149.227.124 orport=8080 id=7A6EC43FD4CD5990230FCE48EC37AFC578E36CE6"
+" ipv6=[2a01:440:108:11:82:149:227:124]:8080"
+/* nickname=cryzrelay03 */
+/* extrainfo=0 */
+/* ===== */
+,
+"135.148.100.90 orport=443 id=728F97D5BCB131698814D8C713C2220C6E7267DE"
+/* nickname=CanisLatrans */
+/* extrainfo=0 */
+/* ===== */
+,
+"185.183.157.127 orport=9100 id=34B80D703F4D6350146B684E66D962A23A830117"
+" ipv6=[2a03:4000:1d:b56:5850:c5ff:feca:c5b0]:9100"
+/* nickname=Quetzalcoatl */
+/* extrainfo=0 */
+/* ===== */
+,
+"179.43.159.194 orport=9001 id=617C95FCF5F00E98E73E35A71C066ED20614F26D"
+/* nickname=FreeExit */
+/* extrainfo=0 */
+/* ===== */
+,
+"185.244.195.157 orport=9100 id=69042D6B301F080105D11478A5BC848EB0B5D5DB"
+" ipv6=[2a03:4000:27:747:4804:22ff:fe7a:e606]:9100"
+/* nickname=Quetzalcoatl */
+/* extrainfo=0 */
+/* ===== */
+,
+"178.254.6.130 orport=443 id=7A2D44E76934D709CBF0E0AE8FEB132B61B6F35D"
+" ipv6=[2a00:6800:3:35b::2]:443"
+/* nickname=zensursula */
+/* extrainfo=0 */
+/* ===== */
+,
+"142.132.204.165 orport=4080 id=6CBA90E31188A65E2B6AC1EE8412E8DE571ECAD6"
+" ipv6=[2a01:4f8:261:50da::2]:4080"
+/* nickname=spicyavocado */
+/* extrainfo=0 */
+/* ===== */
+,
+"66.183.173.29 orport=22 id=E8FA2C9B690F3BC16ADE9A4803CE2C51132F10A6"
+/* nickname=tengu */
+/* extrainfo=0 */
+/* ===== */
+,
+"31.171.154.165 orport=9001 id=A1177D4BF4698A74B926AE1E0FC533578FA55667"
+/* nickname=Tirana */
+/* extrainfo=0 */
+/* ===== */
+,
+"91.134.147.134 orport=9001 id=2AD82F3964D325B3FE2FF74E980FB006374EF190"
+/* nickname=Unnamed */
+/* extrainfo=0 */
+/* ===== */
+,
+"193.105.73.80 orport=9001 id=9DC8B0282A8D3C45212167C454B503243BC93957"
+/* nickname=akira */
+/* extrainfo=0 */
+/* ===== */
+,
+"140.78.100.22 orport=5443 id=69D7FEF9B0026393C2FD73E897C71C102ABACA5C"
+/* nickname=INSRelay22at5443 */
+/* extrainfo=0 */
+/* ===== */
+,
+"23.129.64.217 orport=443 id=41C80C52AC82295A4D4308D30DCCD3D4ABC4F66C"
+" ipv6=[2620:18c:0:192::217]:443"
+/* nickname=EO */
+/* extrainfo=0 */
+/* ===== */
+,
+"65.108.253.128 orport=443 id=0B53BF919B9A01ED62ED10E51292ACA50DDB10EB"
+" ipv6=[2a01:4f9:c011:9e93::1]:443"
+/* nickname=l0z3rzb3d4m3d */
+/* extrainfo=0 */
+/* ===== */
+,
+"198.98.60.107 orport=443 id=23C64D7C96C8390013C9E583230E81E70F81F756"
+/* nickname=BingusBongus */
+/* extrainfo=0 */
+/* ===== */
+,
+"188.68.40.46 orport=9100 id=2EFC2B8BC724CF435C14066087936BE7CA3C57A3"
+" ipv6=[2a03:4000:17:5c:24df:84ff:fe54:82aa]:9100"
+/* nickname=Quetzalcoatl */
+/* extrainfo=0 */
+/* ===== */
+,
+"138.201.35.6 orport=9001 id=94B710C41B9E8FB592D0916E71B5A89FF4995996"
+" ipv6=[2a01:4f8:171:369a::2]:9001"
+/* nickname=torrelay */
+/* extrainfo=0 */
+/* ===== */
+,
+"51.81.208.164 orport=443 id=756576CF5DA387CDD7EABCCA3F8EAEF933CB4486"
+/* nickname=screenslaver */
+/* extrainfo=0 */
+/* ===== */
+,
+"89.147.109.91 orport=443 id=7155DE90C1C3C9BF4D637580C7F027E57227BD30"
+/* nickname=Unnamed */
+/* extrainfo=0 */
+/* ===== */
+,
+"152.70.197.164 orport=443 id=F882E4A4B73447C561617005C8E692B0A7080802"
+/* nickname=screwTheNSA */
+/* extrainfo=0 */
+/* ===== */
+,
+"163.172.211.16 orport=443 id=05F5062943054646CC7A65532CE52598052628FA"
+/* nickname=FreeUyghur */
+/* extrainfo=0 */
+/* ===== */
+,
+"174.128.250.164 orport=80 id=5197FC89F7A1623CA90D6E0254ABCCBC6D85A86E"
+/* nickname=ready2 */
+/* extrainfo=0 */
+/* ===== */
+,
+"13.211.32.165 orport=9001 id=956CD533B7C331C675D30A50900A2E9777D42782"
+/* nickname=BlueMonkey */
+/* extrainfo=0 */
+/* ===== */
+,
+"116.12.180.237 orport=7443 id=10805A3833774B812D07EB7D1D75A54021590F56"
+/* nickname=tommyboy */
+/* extrainfo=0 */
+/* ===== */
+,
+"212.192.246.55 orport=443 id=7B24FA67347BEAEC6923D884EACF1C123180DE32"
+/* nickname=0x616e6f6e */
+/* extrainfo=0 */
+/* ===== */
+,
+"150.246.240.221 orport=443 id=E3F7DA7CC7D5B6CC085799AE0404AA4D2D503E50"
+/* nickname=90377Sedna */
+/* extrainfo=0 */
+/* ===== */
+,
+"5.189.155.39 orport=9001 id=00962D2DD0B9BF3A6AF1D5EB201132388ACA1424"
+/* nickname=BlackBeluga */
+/* extrainfo=0 */
+/* ===== */
+,
+"104.244.72.91 orport=9001 id=BD140758135A15605996CCEE3BBFA4127F97B233"
+/* nickname=Hydra44 */
+/* extrainfo=0 */
+/* ===== */
+,
+"103.109.100.207 orport=9001 id=D08C694485A0031692FAFE8C3205FBBBDBCD9402"
+/* nickname=thelastvampire */
+/* extrainfo=0 */
+/* ===== */
+,
+"171.25.193.80 orport=80 id=491B4E55B4FD4FDEC63B229B0A3E59868FCA1F1F"
+" ipv6=[2001:67c:289c:4::79]:80"
+/* nickname=DFRI22 */
+/* extrainfo=0 */
+/* ===== */
+,
+"199.249.230.152 orport=443 id=4EE488AC0742BC6B747BB637A5635CE14E877F39"
+" ipv6=[2620:7:6001::152]:80"
+/* nickname=Quintex63 */
+/* extrainfo=0 */
+/* ===== */
+,
+"188.68.224.83 orport=443 id=A6BBD33695A4E3C4545BA370605A4DCD87D98BEE"
+/* nickname=ATLANTIS */
+/* extrainfo=0 */
+/* ===== */
+,
+"37.133.161.205 orport=9001 id=96C36107664B4406C38CD8676AEA85E4D8BCE825"
+/* nickname=tor4win */
+/* extrainfo=0 */
+/* ===== */
+,
+"198.180.150.9 orport=9001 id=60E4C5E306D2DB22890EE24A09F9B6C30AF396A8"
+" ipv6=[2001:418:8006::9]:9001"
+/* nickname=rgiad */
+/* extrainfo=0 */
+/* ===== */
+,
+"129.151.246.99 orport=9001 id=439CD12F87CEB496D2601B5DC1FF5186BD9AC2D1"
+/* nickname=tallinn21 */
+/* extrainfo=0 */
+/* ===== */
+,
+"130.162.218.212 orport=9001 id=944E947CA80D1CF8B2EBE3C3247B2C11931438B8"
+/* nickname=Perch */
+/* extrainfo=0 */
+/* ===== */
+,
+"86.115.2.103 orport=9001 id=E816C4D4CDA9810D2CB1371257462FF6BCE4DF91"
+/* nickname=homebox57 */
+/* extrainfo=0 */
+/* ===== */
+,
diff --git a/README.md b/README.md
index d4463f7c..d425d13b 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,15 @@
+
 NOnion
 -------------------------------
-[![Build Status](https://github.com/nblockchain/NOnion/actions/workflows/CI.yml/badge.svg?branch=master&event=push)](https://github.com/nblockchain/NOnion/actions/workflows/CI.yml)
+[![Build Status](https://github.com/aarani/NOnion/actions/workflows/CI.yml/badge.svg?branch=master&event=push)](https://github.com/aarani/NOnion/actions/workflows/CI.yml)
 
 _Unofficial_ work in progress .NET TOR client library (implemented in F#)
 
 - [How do I add this to my project?](#how-do-i-add-this-to-my-project)
+- [Getting Started](#getting-started)
 - [Contributing](#contributing)
-- [Contributors](https://github.com/nblockchain/NOnion/graphs/contributors)
-- [License](https://github.com/nblockchain/NOnion/blob/master/LICENSE)
+- [Contributors](https://github.com/aarani/NOnion/graphs/contributors)
+- [License](https://github.com/aarani/NOnion/blob/master/LICENSE)
 
 # How do I add this to my project?
 
@@ -23,6 +25,76 @@ Install via NuGet:
 > Install-Package NOnion
 ```
 
+# Getting Started (F#)
+
+## Bootstrapping
+
+To utilize the Tor network, start by bootstrapping your directory. This step involves downloading the most recent information about available routers and their identity keys.
+
+To initiate the bootstrapping process, you require a list of initial nodes for communication. In NOnion, there are two methods to obtain this list:
+1- [Download from GitLab](https://gitlab.torproject.org/tpo/core/tor/-/raw/main/src/app/config/fallback_dirs.inc)
+2- Utilize the embedded list in the NOnion binary (Note: this list could potentially be outdated)
+
+Based on what option you choose use one of the following commands to bootstrap a TorClient object:
+```
+let! torClient = TorClient.AsyncBootstrapWithEmbeddedList None
+```
+### OR
+```
+let! torClient = TorClient.AsyncBootstrapWithGitlab None
+```
+## Browsing the web
+
+To route your traffic through Tor, you require a circuit, which consists of multiple hops or routers. NOnion simplifies this process for you. Just specify the desired length of your circuit and use the following command:
+```
+let hopCount = 3 // any length you want
+let! circuit = torClient.AsyncCreateCircuit hopCount Exit None
+```
+Once the circuit is established, generate a stream to channel your traffic and establish a connection with your intended destination.
+```
+let address = "google.com" // Hostname you want to connect to
+let port = 80 // Port you want to connect it
+
+do!
+	stream.ConnectToOutside address port
+	|> Async.Ignore
+```
+Now, utilize the stream just like any other `System.IO.Stream`:
+```
+do! stream.AsyncWrite [|1uy; 2uy; 3uy; 4uy|]
+```
+
+## Connecting to hidden services
+
+To connect to a hidden service using the Tor network, use the folowing command:
+```
+let onionAddress = "facebookwkhpilnemxj7asaniu7vnjjbiltxjqhye3mhbshg7kx5tfyd.onion"
+
+let! serviceClient = TorServiceClient.Connect torClient onionAddress
+let! stream = serviceClient.GetStream()
+```
+Now, utilize the stream just like any other `System.IO.Stream`:
+```
+do!
+    sprintf "GET / HTTP/1.0\r\n\r\n"
+    |> System.Text.Encoding.ASCII.GetBytes
+    |> stream.AsyncWrite
+```
+## Hosting a hidden service
+Start a hidden service, use the following command:
+```
+let serviceHost = new TorServiceHost(torClient, None)
+do! serviceHost.Start()
+```
+*Note: use TorServiceHost's `maybeMasterPrivateKey` parameter to supply your existing bouncy castle private key*
+
+Now use the following command to wait for incoming streams/clients:
+```
+let! incomingStream = serviceHost.AcceptClient()
+```
+Now, utilize the stream just like any other `System.IO.Stream`.
+
+#### Everything mentioned above can be accomplished in C#. Our test project is also written in C#, so feel free to examine it.
 # Contributing
 
 Don't underestimate the power of your contribution - even a small pull request can make a big difference in a small project, so submit yours today!