diff --git a/repos/ealanos/run/kuori.run b/repos/ealanos/run/kuori.run
new file mode 100644
index 0000000000..e56bef2153
--- /dev/null
+++ b/repos/ealanos/run/kuori.run
@@ -0,0 +1,105 @@
+create_boot_directory
+import_from_depot [depot_user]/pkg/[drivers_nic_pkg] \
+
+append config {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}
+
+append qemu_args " -nographic "
+append_qemu_nic_args "host=10.0.2.1,dhcpstart=10.0.2.55,hostfwd=tcp::10080-:80,hostfwd=tcp::18080-:8080,hostfwd=udp::10007-:7,hostfwd=udp::17070-:7070"
+
+build { core init hoitaja timer lib/ld lib/vfs lib/libm lib/libc lib/stdcxx server/nic_router server/nic_uplink app/kuori app/allocating_cell }
+
+install_config $config
+build_boot_image [build_artifacts]
+run_genode_until forever
\ No newline at end of file
diff --git a/repos/ealanos/src/app/kuori/main.cc b/repos/ealanos/src/app/kuori/main.cc
new file mode 100644
index 0000000000..641f39d80b
--- /dev/null
+++ b/repos/ealanos/src/app/kuori/main.cc
@@ -0,0 +1,352 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+namespace Lwip {
+ extern "C" {
+ #include
+ #include
+ #include
+ #include
+ #include
+ }
+}
+
+namespace Ealan::Kuori {
+ using namespace Genode;
+ class Main;
+}
+
+class Ealan::Kuori::Main
+{
+ private:
+ Env &_env;
+ Heap &_heap;
+
+ Ealan::Launcher_connection &_hoitaja;
+
+ struct Wakeup_scheduler : Lwip::Nic_netif::Wakeup_scheduler
+ {
+ Lwip::Nic_netif *nic{nullptr};
+
+ void schedule_nic_server_wakeup() override
+ {
+ nic->wakeup_nic_server();
+ }
+
+ void set_nic(Lwip::Nic_netif *nic)
+ {
+ this->nic = nic;
+ }
+
+ Wakeup_scheduler() = default;
+ } _wakeup_scheduler;
+
+ enum states
+ {
+ NONE = 0,
+ ACCEPTED,
+ RECEIVED,
+ CLOSING
+ };
+
+ struct state
+ {
+ uint8_t state;
+ uint8_t retries;
+ struct Lwip::tcp_pcb *pcb;
+ struct Lwip::pbuf *p;
+ };
+
+ Lwip::tcp_pcb *listen_socket;
+ uint16_t _port{8080};
+
+ struct Tcp_connection
+ {
+ static Main *kuori;
+ /**
+ * @brief Free state and lwip objects
+ *
+ * @param s
+ */
+ static void free(struct state *s)
+ {
+ if (s != nullptr) {
+ if (s->p) {
+ Lwip::pbuf_free(s->p);
+ }
+ Lwip::mem_free(s);
+ }
+ }
+
+ static void close(struct Lwip::tcp_pcb *tpcb, struct state *s)
+ {
+ Lwip::tcp_arg(tpcb, nullptr);
+ Lwip::tcp_sent(tpcb, nullptr);
+ Lwip::tcp_recv(tpcb, nullptr);
+ Lwip::tcp_err(tpcb, nullptr);
+ Lwip::tcp_poll(tpcb, nullptr, 0);
+
+ free(s);
+ Lwip::tcp_close(tpcb);
+ }
+
+ static void send(struct Lwip::tcp_pcb *tpcb, struct state *s)
+ {
+ struct Lwip::pbuf *ptr;
+ Lwip::err_t wr_err = Lwip::ERR_OK;
+
+ while ((wr_err == Lwip::ERR_OK) && (s->p != nullptr))
+ {
+ ptr = s->p;
+
+ wr_err = Lwip::tcp_write(tpcb, ptr->payload, ptr->len, 3);
+ if (wr_err == Lwip::ERR_OK) {
+ uint16_t plen;
+
+ plen = ptr->len;
+ s->p = ptr->next;
+
+ if (s->p) {
+ Lwip::pbuf_ref(s->p);
+ }
+ Lwip::pbuf_free(ptr);
+ Lwip::tcp_recved(tpcb, plen);
+ } else if (wr_err == Lwip::ERR_MEM) {
+ s->p = ptr;
+ } else {
+ Genode::error("Encountered LwIP error code ", static_cast(wr_err));
+ }
+ }
+ }
+
+ static void error(void *arg, Lwip::err_t error)
+ {
+ struct state *s;
+
+ LWIP_UNUSED_ARG(error);
+
+ s = static_cast(arg);
+
+ free(s);
+ }
+
+ static Lwip::err_t poll(void *arg, struct Lwip::tcp_pcb *tpcb)
+ {
+ Lwip::err_t rc;
+ struct state *s;
+
+ s = static_cast(arg);
+ if (s) {
+ if (s->p) {
+ send(tpcb, s);
+ } else {
+ if (s->state == CLOSING) {
+ close(tpcb, s);
+ }
+ }
+ rc = Lwip::ERR_OK;
+ } else {
+ Lwip::tcp_abort(tpcb);
+ rc = Lwip::ERR_ABRT;
+ }
+ return rc;
+ }
+
+ static Lwip::err_t sent(void *arg, struct Lwip::tcp_pcb *tpcb, [[maybe_unused]] Lwip::u16_t len)
+ {
+ struct state *s;
+
+ s = static_cast(arg);
+ s->retries = 0;
+
+ if (s->p) {
+ Lwip::tcp_sent(tpcb, sent);
+ send(tpcb, s);
+ } else {
+ if (s->state == CLOSING) {
+ close(tpcb, s);
+ }
+ }
+ return Lwip::ERR_OK;
+ }
+
+ static Lwip::err_t receive(void *arg, struct Lwip::tcp_pcb *tpcb, struct Lwip::pbuf *p, Lwip::err_t err)
+ {
+ struct state *s;
+ Lwip::err_t rc = Lwip::ERR_OK;
+
+ s = static_cast(arg);
+
+ if (!p) {
+ s->state = CLOSING;
+ if (!s->p) {
+ close(tpcb, s);
+ } else {
+ send(tpcb, s);
+ }
+ rc = Lwip::ERR_OK;
+ } else if (err != Lwip::ERR_OK) {
+ rc = err;
+ } else if (s->state == ACCEPTED) {
+ s->state = RECEIVED;
+ kuori->parse_and_launch(tpcb, p, s);
+ }
+ else if (s->state == RECEIVED)
+ {
+ if (!s->p) {
+ s->p = p;
+ kuori->parse_and_launch(tpcb, p, s);
+ }
+ else
+ {
+ struct Lwip::pbuf *ptr;
+ ptr = s->p;
+ Lwip::pbuf_cat(ptr, p);
+ }
+ rc = Lwip::ERR_OK;
+ }
+ else
+ {
+ Lwip::tcp_recved(tpcb, p->tot_len);
+ Lwip::pbuf_free(p);
+ rc = Lwip::ERR_OK;
+ }
+
+ return rc;
+ }
+
+ static Lwip::err_t accept([[maybe_unused]] void *arg, struct Lwip::tcp_pcb *newpcb, Lwip::err_t err)
+ {
+ Lwip::err_t rc;
+ struct state *s;
+
+ LWIP_UNUSED_ARG(arg);
+
+ if ((err != Lwip::ERR_OK) || (newpcb == nullptr)) {
+ return Lwip::ERR_VAL;
+ }
+
+ s = static_cast(Lwip::mem_malloc(sizeof(struct state)));
+ if (s) {
+ s->state = ACCEPTED;
+ s->pcb = newpcb;
+ s->retries = 0;
+ s->p = nullptr;
+
+ Lwip::tcp_arg(newpcb, s);
+ Lwip::tcp_recv(newpcb, receive);
+ Lwip::tcp_err(newpcb, error);
+ Lwip::tcp_poll(newpcb, poll, 0);
+ Lwip::tcp_sent(newpcb, sent);
+
+ rc = Lwip::ERR_OK;
+
+ Genode::log("Accepted new connection from ", const_cast(Lwip::ipaddr_ntoa(&newpcb->remote_ip)));
+ }
+ else
+ {
+ rc = Lwip::ERR_MEM;
+ }
+ return rc;
+ }
+
+ };
+
+ void init()
+ {
+ listen_socket = Lwip::tcp_new_ip_type(Lwip::IPADDR_TYPE_ANY);
+
+ if (!listen_socket) {
+ Genode::error("Failed to create server socket.");
+ }
+
+ Lwip::err_t err;
+ err = Lwip::tcp_bind(listen_socket, &Lwip::ip_addr_any, _port);
+
+ if (err == Lwip::ERR_OK) {
+ listen_socket = Lwip::tcp_listen_with_backlog(listen_socket, 255);
+ Lwip::tcp_accept(listen_socket, Tcp_connection::accept);
+ } else {
+ Genode::error("Failed to bind socket.");
+ }
+
+ Tcp_connection::kuori = this;
+
+ Genode::log("Kuori v0.1 - Remote shell for EalánOS");
+ }
+
+ Lwip::Nic_netif _netif;
+
+ public:
+ Main(Env &env, Genode::Heap &heap, Xml_node config, Ealan::Launcher_connection &hoitaja) : _env(env), _heap(heap), _hoitaja(hoitaja), _wakeup_scheduler(), _netif(env, _heap, config, _wakeup_scheduler) {
+
+ _wakeup_scheduler.set_nic(&_netif);
+ init();
+ }
+
+ void parse_and_launch(Lwip::tcp_pcb *tpcb, Lwip::pbuf *p, state *state)
+ {
+ try {
+ Genode::log("Parsing input of length ", p->len);
+ Genode::log(static_cast(p->payload));
+ Xml_node start_node(static_cast(p->payload));
+ //Genode::log(start_node);
+
+ Genode::String<90> name = start_node.attribute_value("name", Genode::String<90>());
+ char msg[120];
+
+ std::sprintf(msg, "Lauching %s\n", name.string());
+
+ Genode::String<640> cell_cfg(static_cast(p->payload));
+ Genode::log(cell_cfg);
+ _hoitaja.launch(cell_cfg);
+
+ Lwip::pbuf *response_buffer = Lwip::pbuf_alloc(Lwip::PBUF_TRANSPORT, Genode::strlen(msg), Lwip::PBUF_RAM);
+
+ Lwip::pbuf_take(response_buffer, msg, Genode::strlen(msg));
+
+ state->p = response_buffer;
+ Tcp_connection::send(tpcb, state);
+ }
+ catch (Genode::Xml_attribute::Invalid_syntax)
+ {
+ Genode::error("Invalid config supplied.");
+ Genode::String<100> response("Invalid cell configuration.\n");
+
+ Lwip::pbuf *response_buffer = Lwip::pbuf_alloc(Lwip::PBUF_TRANSPORT, response.length(), Lwip::PBUF_RAM);
+ Lwip::pbuf_take(response_buffer, response.string(), response.length());
+
+ state->p = response_buffer;
+ Tcp_connection::send(tpcb, state);
+ }
+ }
+};
+
+Ealan::Kuori::Main *Ealan::Kuori::Main::Tcp_connection::kuori;
+
+void Libc::Component::construct(Libc::Env &env)
+{
+ Genode::Attached_rom_dataspace _config{env, "config"};
+ Genode::Xml_node config = _config.xml();
+
+ static Genode::Heap heap{env.ram(), env.rm()};
+ static Timer::Connection timer{env};
+
+ Lwip::genode_init(heap, timer);
+
+ Genode::Thread *myself = Genode::Thread::myself();
+ Genode::Affinity::Location loc = myself->affinity();
+ Genode::Affinity affinity(env.cpu().affinity_space(), loc);
+
+ static Ealan::Launcher_connection hoitaja{env, affinity};
+
+ static Ealan::Kuori::Main kuori(env, heap, config, hoitaja);
+}
\ No newline at end of file
diff --git a/repos/ealanos/src/app/kuori/target.mk b/repos/ealanos/src/app/kuori/target.mk
new file mode 100644
index 0000000000..c1d013067e
--- /dev/null
+++ b/repos/ealanos/src/app/kuori/target.mk
@@ -0,0 +1,22 @@
+SRC_CC = main.cc
+SRC_CC += vfs.cc printf.cc rand.cc sys_arch.cc
+TARGET = kuori
+
+LIBPORTS_DIR = $(REP_DIR)/../libports
+VFS_DIR = $(LIBPORTS_DIR)/src/lib/vfs/lwip
+vpath %.cc $(VFS_DIR)
+
+
+LWIP_PORT_DIR := $(call select_from_ports,lwip)
+LWIPDIR := $(LWIP_PORT_DIR)/src/lib/lwip/src
+INC_DIR += $(LWIP_PORT_DIR)/include/lwip \
+ $(LWIPDIR)/include \
+ $(LWIPDIR)/include/ipv4 \
+ $(LWIPDIR)/include/api \
+ $(LWIPDIR)/include/netif \
+ $(LIBPORTS_DIR)/src/lib/lwip/include \
+ $(LIBPORTS_DIR)/src/lib/vfs \
+ $(LIBPORTS_DIR)/src/lib/vfs/lwip
+
+CC_OPT += -Wno-error=conversion -Wno-error=effc++
+LIBS += base libm libc lwip stdcxx
\ No newline at end of file