diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..85a1d6e6bc15a401ef725091b2c16a49b6e72f6b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+.idea
+.vscode
+build/
diff --git a/CMakeLists.txt b/CMakeLists.txt
index daf67c900972d0b2f9eaf027a1c2a524f0135c37..0dc4a147a0ab62880a78f47ad00021b3994f4a20 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,8 +1,10 @@
 cmake_minimum_required(VERSION 3.5)
 project(UDPForwarder)
-
 set(CMAKE_CXX_STANDARD 17)
 
+find_package(rlib)
+find_package(Threads)
+
 set(CMAKE_CXX_FLAGS_DEBUG "-g -DMALLOC_CHECK_=2")
 set(CMAKE_CXX_FLAGS_RELEASE "-O3")
 
@@ -28,12 +30,13 @@ if(APPLE)
 endif()
 
 include_directories(./src)
-include_directories(./src/lib)
 
 # TODO
-set(SRC )
+set(SRC src/main.cc)
 
 add_executable(udp-forwarder ${SRC})
+target_link_libraries(udp-forwarder r)
+target_link_libraries(udp-forwarder Threads::Threads)
 
 # target_link_libraries(udp-forwarder -static-libgcc -static-libstdc++)
 #if(FOR_M32)
diff --git a/src/common.hpp b/src/common.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..5596d2eaadc886a457983cbc3c0fde3f4dafda4f
--- /dev/null
+++ b/src/common.hpp
@@ -0,0 +1,9 @@
+#ifndef UDP_FORWARDER_DYN_COMMON_HPP_
+#define UDP_FORWARDER_DYN_COMMON_HPP_ 1
+
+#include <rlib/log.hpp>
+
+extern rlib::logger rlog;
+
+#endif
+
diff --git a/src/example.conf b/src/example.conf
new file mode 100644
index 0000000000000000000000000000000000000000..f681aa984c47c2ba7b9ba4fb30af379b35c8480c
--- /dev/null
+++ b/src/example.conf
@@ -0,0 +1,20 @@
+# Maybe we don't need a config file. 
+# just use command line
+# ./udp-forwarder -i "plain:0.0.0.0:9988" -o "misc:base.jp3.recolic.net:9989:P@ssw0rd" [--log=error/info/verbose/debug]
+
+
+# Do you want conf or json? 
+[inbound]
+protocol = plain
+addr = 0.0.0.0
+port = 9988
+
+[outbound]
+protocol = misc
+addr = base.jp3.recolic.net
+port = 9989
+password = P@ssw0rd
+
+[main]
+log_level = info
+
diff --git a/src/main.cc b/src/main.cc
new file mode 100644
index 0000000000000000000000000000000000000000..bced5e9a3872f1894b95b65ac18e6c0f4989be37
--- /dev/null
+++ b/src/main.cc
@@ -0,0 +1,36 @@
+#include <rlib/stdio.hpp>
+#include <rlib/opt.hpp>
+#include "common.hpp"
+
+rlib::logger rlog(std::cerr);
+
+using namespace rlib::literals;
+
+int main(int argc, char **argv) {
+    rlib::opt_parser args(argc, argv);
+    if(args.getBoolArg("--help", "-h")) {
+        rlib::println("Usage: {} -i $InboundConfig -o $OutboundConfig [--log=error/info/verbose/debug]"_rs.format(args.getSelf()));
+        rlib::println("  InboundConfig and OutboundConfig are in this format: ");
+        rlib::println("  '$method:$params', available methods: ");
+        rlib::println("  'plain:$addr:$port', 'misc:$addr:$portRange:$psk'");
+        return 0;
+    }
+    auto inboundConfig = args.getValueArg("-i");
+    auto outboundConfig = args.getValueArg("-o");
+    auto log_level = args.getValueArg("--log", false, "info");
+
+    if(log_level == "error")
+        rlog.set_log_level(rlib::log_level_t::ERROR);
+    else if(log_level == "info")
+        rlog.set_log_level(rlib::log_level_t::INFO);
+    else if(log_level == "verbose")
+        rlog.set_log_level(rlib::log_level_t::VERBOSE);
+    else if(log_level == "debug")
+        rlog.set_log_level(rlib::log_level_t::DEBUG);
+    else
+        throw std::runtime_error("Unknown log level: " + log_level);
+
+    // Forwarder(inboundConfig, outboundConfig).run_forever();
+
+    return 0;
+}