diff --git a/.gitignore b/.gitignore
index 923beebfed0097c0091ee940635d5c8418a1b683..c209462d2be82075286bde9a7c86ffd0003f8544 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,4 +5,7 @@ build
 **/config.h
 _CPack_Packages
 .env
-docs/html
\ No newline at end of file
+docs/html
+/build*
+/.vs
+/CMakeSettings.json
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index bb8c541adc4a3c6ac60cb9f554eff4ce788a3708..1f20fc83f1785a4fb4798fc3a514781a1083beea 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -195,6 +195,33 @@ windows:build_debug:
       - cmake -DCMAKE_GENERATOR_PLATFORM=x64 "-DFTL_VERSION=$CI_COMMIT_TAG" -DWITH_GNUTLS=TRUE -DGNUTLS_INCLUDE_DIR="C:/Build/bin/gnutls/lib/includes/" -DGNUTLS_LIBRARY="C:/Build/bin/gnutls/lib/libgnutls.dll.a" ..
       - '& MSBuild.exe beyond-protocol.sln -property:Configuration=RelWithDebInfo -nr:false -maxCpuCount'
 
+
+windows:build_arm64:
+  only:
+    - main
+    - merge_requests
+    - tags
+
+  stage: build
+  tags:
+    - windows
+
+  needs: []
+  dependencies: []
+  
+  cache: # use artifacts instead if multiple runners available
+    key: "$CI_COMMIT_SHORT_SHA arm64"
+    paths:
+      - build_arm64/
+    
+  script:
+      - cd $CI_PROJECT_DIR
+      - if (Test-Path build_arm64) { Remove-Item build_arm64/ -Recurse }
+      - mkdir build_arm64
+      - cd build_arm64
+      - cmake -DCMAKE_GENERATOR_PLATFORM=arm64 -DWITH_GNUTLS=FALSE -DBUILD_TESTS=FALSE -DURIPARSER_LIBRARY="C:/Build/bin_arm64/uriparser/Release/uriparser.lib" -DBUILD_TESTING=FALSE  ..
+      - cmake --build . --config Release
+
 windows:test:
   only:
     - main
@@ -278,6 +305,35 @@ windows:pack_debug:
       - ./*.zip
     expire_in: 1 week
 
+
+windows:pack_arm64:
+  only:
+    - tags
+  
+  stage: pack
+
+  tags:
+    - windows
+  dependencies: ["windows:build_arm64"]
+  needs: ["windows:test", "windows:build_arm64"]
+
+  cache: # use artifacts instead if multiple runners available
+    key: "$CI_COMMIT_SHORT_SHA arm64"
+    paths:
+      - build_arm64/
+
+  script:
+    - $env:PATH+=";C:/Shared/Deploy"
+    - cd build_debug
+    - cpack -C RelWithDebInfo
+    - Invoke-RestMethod -Headers @{ "JOB-TOKEN"="$CI_JOB_TOKEN" } -InFile "../libftl-protocol-${CI_COMMIT_TAG}-win64.zip" -uri "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/libftl-protocol/${CI_COMMIT_TAG}/libftl-protocol-${CI_COMMIT_TAG}-win64-debug.zip" -Method put
+
+  artifacts:
+    when: always
+    paths:
+      - ./*.zip
+    expire_in: 1 week
+
 # Documentation
 
 pages:
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 67542ca4c5c26761b6d3ab355418d266af7dc0d3..c871546af90c5f38ef7074df827a1595ed8a7b01 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -48,7 +48,6 @@ if (WITH_GNUTLS)
 	message(STATUS "Gnutls found: ${GNUTLS_FOUND}")
 	set(HAVE_GNUTLS true)
 else()
-	add_library(gnutls INTERFACE)
 endif()
 
 # ==============================================================================
@@ -82,11 +81,11 @@ endif()
 
 # Only need libuuid on Linux, Windows has its own
 if (NOT WIN32)
-check_include_file("uuid/uuid.h" UUID_FOUND)
-if (NOT UUID_FOUND)
-	message(ERROR "UUID library is required")
-endif()
-find_library(UUID_LIBRARIES NAMES uuid libuuid)
+	check_include_file("uuid/uuid.h" UUID_FOUND)
+	if (NOT UUID_FOUND)
+		message(ERROR "UUID library is required")
+	endif()
+	find_library(UUID_LIBRARIES NAMES uuid libuuid)
 else()
 endif()
 
@@ -104,12 +103,17 @@ include(ftl_paths)
 
 if (WIN32) # TODO(nick) Should do based upon compiler (VS)
 	add_definitions(-DWIN32)
-	#set(CMAKE_GENERATOR_TOOLSET "host=x64")
-	set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /arch:AVX2 /MP /std:c++17 /wd4996 /Zc:__cplusplus")
+	set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /std:c++17 /wd4996 /Zc:__cplusplus")
 	set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /DFTL_DEBUG /Wall")
 	set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /O2 /W3")
     set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /O2 /W3 /Z7")
 	set(OS_LIBS "")
+	
+	if ("${CMAKE_GENERATOR_PLATFORM}" STREQUAL "x64")
+		set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /arch:AVX2")
+	elseif ("${CMAKE_GENERATOR_PLATFORM}" STREQUAL "ARM64")	
+	endif()
+	
 else()
 	add_definitions(-DUNIX)
 	# -fdiagnostics-color
@@ -202,7 +206,11 @@ add_library(beyond-protocol STATIC
 target_include_directories(beyond-protocol PUBLIC
 	$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
 	$<INSTALL_INTERFACE:include>)
-target_link_libraries(beyond-protocol Threads::Threads ${UUID_LIBRARIES} GnuTLS::GnuTLS)
+target_link_libraries(beyond-protocol Threads::Threads ${UUID_LIBRARIES})
+
+if (WITH_GNUTLS)
+	target_link_libraries(beyond-protocol GnuTLS::GnuTLS)
+endif()
 
 install(TARGETS beyond-protocol
 	ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
diff --git a/src/protocol/websocket.cpp b/src/protocol/websocket.cpp
index ed6ae71ca333a04eefc7043e3c7856ff06ce4fb7..696b474feea8c9a1cf20749ff824c5522c41ef17 100644
--- a/src/protocol/websocket.cpp
+++ b/src/protocol/websocket.cpp
@@ -23,12 +23,16 @@ inline uint32_t secure_rnd() {
     return rnd;
 }
 #else
+// TODO(Seb): Use cryptographically strong RNG. RFC 6455 (WebSicket protocol)
+//            requires new masking key for every frame and the key must not be
+//            predictable (use system random number source such as /dev/random
+//            or another library if GnuTLS is not enabled).
+
 #include <random>
 static std::random_device rd_;
 static std::uniform_int_distribution<uint32_t> dist_(0);
 
 inline uint32_t secure_rnd() {
-    // TODO(Seb)
     return dist_(rd_);
 }
 #endif
@@ -36,7 +40,9 @@ inline uint32_t secure_rnd() {
 using ftl::URI;
 using ftl::net::internal::WebSocketBase;
 using ftl::net::internal::Connection_TCP;
+#ifdef HAVE_GNUTLS
 using ftl::net::internal::Connection_TLS;
+#endif
 
 /* Taken from easywsclient */
 struct wsheader_type {