Synchronized state machines for Rust over WebSockets.

Related tags

WebSocket aper
Overview

Aper

crates.io docs.rs wokflow state

Aper is a framework for real-time sharing of application state over WebSockets. Its use cases include browser-based collaboration tools and multiplayer in-browser games.

Specifically, Aper provides scaffolding to represent your program as a state machine, as well as the infrastructure to keep that state machine synchronized across multiple instances running in your users' browsers.

Aper integrates with Yew on the client side, and Actix for the server. Although the focus is on browser-based apps running in WebAssembly and communicating over WebSocket, the core state machine scaffolding can be used independent of the client/server architecture, and even with non-WebSocket protocols.

Aper is rapidly evolving. Consider this a technology preview.

Roadmap

Before the first non-preview release, the following need to be sorted out:

  • State transitions that can occur with no user input (e.g. for a timer in a game).
  • Optimistic state updates on the client, with rollback if necessary.
  • Implement graceful reconnection in the client. (e.g. iOS seems to drop websocket connections of background tabs, need to auto-reconnect)
  • Allow the state machine to handle disconnection.
  • Allow the state machine to “reject” a transition instead of just treating it as a no-op, in order to avoid propagating it.
  • Use a factory pattern to produce state machines rather than a no-argument new function, for flexibility.
  • Add turn-key “channel creation” UI.
  • The server should allow binary or text connections, and the client should switch between json and bincode depending on whether it has the development flag.

The immediate roadmap has a strong emphasis on figuring out the right interface between Aper and application code. Once that's sorted out, longer-term tasks can focus on scaling Aper up to a production environment:

  • Add a separate concept of “player state” in addition to game state. Player state includes things like name or cursor position, but cannot be used for state updates, and as a result can be sent out-of-order.
  • Make state machines more composable.
  • Integrating with authentication/permissions. I don't plan for Aper to ever be opinionated about an auth framework, but it needs to provide hooks to allow it to integrate with other systems.
  • Journaled state storage, both indefinitely for state persistence and short-term to enable graceful node failure.
  • Load balancing rooms between multiple servers.
  • “Agents” that are spun up by the server that have the same interface to the state machine that users do, but do not have a human attached. These could allow a way to access external resources, non-deterministic computation, etc. in a way that does not break the restriction that state updates are deterministic.
  • Pre-built data structures like lists, trees, and sets, and a derive macro to turn any struct built with them into a state machine.
  • Pre-built string data structure implementing Operational Transform.
Comments
  • make generated transforms always public

    make generated transforms always public

    This makes the macro usable with public structs. #22

    Probably it would be better to make the generated transform public only when the struct is public? I don't know how to cleanly put a conditional pub into the metaprogramming. I will learn how to if you agree that that is how it should be.

    opened by joonazan 3
  • How to run the examples

    How to run the examples

    I'm currently trying to run the drop-four example but its not working.

    I ran the following command

    cargo build
    

    It built successfully then I ran

    cargo run -p drop-four-client
    

    This return an error:

    error: a bin target must be available for `cargo run`
    

    I'm currently stuck and I would really love to try out the examples.

    opened by EteimZ 2
  • Cannot derive StateMachine for public struct

    Cannot derive StateMachine for public struct

    error[E0446]: private type `CharacterTransform` in public interface
     --> state/src/character.rs:4:10
      |
    4 | #[derive(StateMachine, Clone, Debug, Serialize, Deserialize, PartialEq)]
      |          ^^^^^^^^^^^^
      |          |
      |          can't leak private type
      |          `CharacterTransform` declared as private
      |
      = note: this error originates in the derive macro `StateMachine` (in Nightly builds, run with -Z macro-backtrace for more info)
    
    opened by joonazan 2
  • Better List serialization

    Better List serialization

    Serialize List as (ZenoIndex, Uuid, T)-tuple instead of serializing each map. This reduces its size and gets rid of the problem that Lists couldn't be serialized into JSON.

    opened by joonazan 1
  • Allow server to serve multiple channels easily

    Allow server to serve multiple channels easily

    The lower-level axtix implementation allows a concept of “channels” each with independent state, but the ServerBuilder does not expose a way to do this. There should be an easy way to allow channel creation from the server.

    v1-milestone aper-actix 
    opened by paulgb 1
  • Conflicts

    Conflicts

    Aper's data structures attempt to handle conflicts appropriately, but there are cases where that isn't possible. For example:

    • Alice modifies a list item after Bob has deleted it.
    • Alice and Bob both modify an atomic at the same time.
    • Alice makes a move in a game that is now invalid because of a move Bob made.

    In these cases, an object representing the conflict should be raised. This would probably be an associated type of StateMachine with a reasonable default (maybe ()).

    Design questions:

    • Should this be a Rust Error? In this case we could use Result<(), Conflict> as a signature for apply
    • Should apply guarantee that the state has not been modified if a conflict arises, or should it be the responsibility of the state manager to roll back if a conflict comes up?
      • The latter would simplify the implementation of transactions (#7) because apply could simply raise the first conflict that arose resulting in a rollback of the entire transaction.
    aper-core data-structures 
    opened by paulgb 1
  • Transactions

    Transactions

    For built-in data structures, it might sometimes be useful to be able to atomically perform multiple operations. I'd need to give some thought to the best way to do this, but one idea is to have a Transactioned pseudo-data structure that wraps a state machine and accepts a Transaction update. Something like (not tested):

    pub struct Transaction<T: Transition>(Vec<T>);
    
    pub struct Transactioned<T: StateMachine>(T);
    
    impl<T: StateMachine> StateMachine for Transactioned<T> {
        type Transition = Transactioned<<T as StateMachine>::Transition>;
    
        fn apply(&mut self, transition: Self::Transition) {
            for t in transition.0 {
                self.0.apply(t);
            }
        }
    }
    
    aper-core data-structures 
    opened by paulgb 1
  • Generalize relative/absolute `List` positions

    Generalize relative/absolute `List` positions

    There are a number of ways to refer to a position in a List, which determines the behavior when the list has changed between when the reference is created and when it is used:

    • At the beginning/end of the list
    • Between two adjacent items
      • Two types of anchor behavior: anchor to the first, anchor to the second (only matters if one is moved or something is inserted between them)
      • A possible third: anchor to whichever has not been moved
    • Immediately after an item
    • Immediately before an item
    • At a specific fractional index

    When adding an item in reference to another item, we usually want to fall back on a fractional index. That way, if the item it is in reference to is removed, we can fall back on the index.

    For simplicity the right approach here is probably something like:

    pub enum ListPosition {
        Beginning,
        End,
        AbsolutePosition(ZenoIndex),
        Before(Uuid, ZenoIndex),
        After(Uuid, ZenoIndex),
    }
    

    This would accomplish the most common use cases, but not the more complex ones.

    v1-milestone aper-core data-structures 
    opened by paulgb 0
  • Support local optimistic transitions

    Support local optimistic transitions

    Currently, the client waits to receive a transition back from the server, even if it is waiting for a transition that originated on that client. This is necessary because applying that transition immediately risks a case where another user has issued a conflicting transition at the same time that arrives at the server first, which would cause the local state to be inconsistent.

    To get around this, we can keep two copies of the local state: one with only transitions from the server applied to it, and one with local transitions as well. We use the one with local transitions to display the state, but if we ever receive an unexpected transition from the server, we roll back to the local server version.

    v1-milestone aper-core 
    opened by paulgb 0
  • Demonstration environment

    Demonstration environment

    It would make it easier to explain/teach the principles behind Aper if I had a demonstration environment that could run locally in the browser. It would be implemented in the form of a Yew component. It would render two (or more?) copies of a component, with state synchronized between them. Messages between them would appear in the UI.

    examples 
    opened by paulgb 0
  • Data structures with shared object pools

    Data structures with shared object pools

    In order to allow something like a Kanban board, we would want to have multiple data structures that items could be moved between. The best way to accomplish this would be to allow multiple lists to share a pool of objects.

    In order to avoid a state where multiple data structures contain the same item, we would also need a way to make multiple state updates atomic (e.g. #7).

    aper-core data-structures 
    opened by paulgb 0
  • Tree data structure

    Tree data structure

    Aper should have a Tree data structure that would represent nestable data structures, such as:

    • Elements in a vector graphic that supports nested grouping (e.g. SVG, geojson)
    • Comments in a threaded comment format

    It should be possible to move elements from one section of the tree to another. In order to handle this nicely, it probably makes sense to have one "pool" for the Tree, mapping uuids to interior state machines, and then have a nested structure of TreeNodes that refer to those uuids. This means that we need to make sure loops are broken and (equivalently) that every node has a parent in the tree.

    v1-milestone aper-core data-structures 
    opened by paulgb 0
Lightweight, event-driven WebSockets for Rust.

WS-RS Lightweight, event-driven WebSockets for Rust. /// A WebSocket echo server listen("127.0.0.1:3012", |out| { move |msg| { out.send(ms

Jason Housley 1.3k Jan 8, 2023
Rust + wasm + websockets

This is a template repo for eframe, a framework for writing apps using egui.

Emil Ernerfeldt 12 Oct 3, 2022
Composable WebSockets made easy, for Rust 🦀

ezsockets Have you ever struggle with creating a WebSocket server or a client in Rust? This crate is for you. High level abstraction of WebSocket, han

Grzegorz Baranski 55 Dec 30, 2022
Rust API connector for Bybit's WebSockets APIs.

rust-bybit English | 简体中文 Unofficial Rust API connector for Bybit's WebSockets APIs. Disclaimer This is an unofficial Rust API connector for Bybit's A

yufuquant 12 Nov 12, 2022
A MITM Proxy Written in Rust 🦀! Toolkit for HTTP/1, HTTP/2, and WebSockets with SSL/TLS Capabilities. Learning Project.

Man In The Middle Proxy Description Rust-based Man in the Middle proxy, an early-stage project aimed at providing visibility into network traffic. Cur

null 158 Mar 9, 2023
Command-line client for WebSockets, like netcat (or curl) for ws:// with advanced socat-like functions

websocat Netcat, curl and socat for WebSockets. Examples Connect to public echo server $ websocat ws://echo.websocket.org 123 123 ABC ABC Serve and c

Vitaly Shukela 5k Jan 4, 2023
Clearly a repo about websockets and their comparison...

Tags Go vs Typescript: Video 1 of the series is tag go-vs-ts-video-1 Current Project Video 2 and 3 are going to be likely Go vs Rust vs Typescript, bu

ThePrimeagen 344 Dec 31, 2022
😎 A custom invoke system for Tauri that leverages WebSockets

?? tauri-awesome-rpc This is a crate provides a custom invoke system for Tauri using a localhost JSON RPC WebSocket. Each message is delivered through

Victor Aremu 20 Dec 2, 2022
A command-line tool for exposing a wrapped program's standard IO using WebSockets/SSE

cmdpiped cmdpiped is a command-line tool for exposing a wrapped cli program's standard IO to WebSockets/SSE Installation Ready to use Binaries are ava

Geoffrey Mureithi 10 Nov 11, 2022
SockJS server for rust language

SockJS server SockJS server for Actix framework. API Documentation Cargo package: sockjs SockJS is built with Actix web Minimum supported Rust version

Actix 63 Oct 7, 2022
A WebSocket (RFC6455) library written in Rust

Rust-WebSocket Note: Maintainership of this project is slugglish. You may want to use tungstenite or tokio-tungstenite instead. Rust-WebSocket is a We

Rust Websockets 1.3k Jan 6, 2023
Lightweight stream-based WebSocket implementation for Rust.

Tungstenite Lightweight stream-based WebSocket implementation for Rust. use std::net::TcpListener; use std::thread::spawn; use tungstenite::server::ac

Snapview GmbH 1.3k Jan 2, 2023
A very-very simple url shortener for Rust

urlshortener-rs A very simple urlshortener for Rust. This library aims to implement as much URL shortener services as possible and to provide an inter

Victor Polevoy 39 Nov 20, 2022
A WebSocket (RFC6455) library written in Rust

Rust-WebSocket Rust-WebSocket is a WebSocket (RFC6455) library written in Rust. Rust-WebSocket provides a framework for dealing with WebSocket connect

Jason N 19 Aug 22, 2022
An aria2 websocket jsonrpc in Rust.

aria2-ws An aria2 websocket jsonrpc in Rust. Built with tokio. Docs.rs aria2 RPC docs Features Almost all methods and structed responses Auto reconnec

null 8 Sep 7, 2022
A simple toy websocket client to connect to Bitstamp.net and print the live order book written in Rust.

A simple toy websocket client to connect to Bitstamp.net and print the live order book written in Rust.

Nate Houk 1 Feb 14, 2022
RuTTY - Rust TTY Server

RuTTY - Rust TTY Server Demo RuTTY (aka Ruthie) is a CLI-powered websocket server written in Rust that allows you to expose your commands via browser.

Gershon Papi 4 Jun 4, 2023
Synchronized shadow state of Solana programs available for off-chain processing.

Solana Shadow The Solana Shadow crate adds shadows to solana on-chain accounts for off-chain processing. This create synchronises all accounts and the

null 40 Oct 30, 2022
notiflux - subscribe over WebSockets, publish over REST

notiflux notiflux is a pub/sub server where clients subscribe over a WebSocket and messages are broadcast over a POST request How does it work? Client

Axel Örn Sigurðsson 3 Apr 9, 2024
Easy c̵̰͠r̵̛̠ö̴̪s̶̩̒s̵̭̀-t̶̲͝h̶̯̚r̵̺͐e̷̖̽ḁ̴̍d̶̖̔ ȓ̵͙ė̶͎ḟ̴͙e̸̖͛r̶̖͗ë̶̱́ṉ̵̒ĉ̷̥e̷͚̍ s̷̹͌h̷̲̉a̵̭͋r̷̫̊ḭ̵̊n̷̬͂g̵̦̃ f̶̻̊ơ̵̜ṟ̸̈́ R̵̞̋ù̵̺s̷̖̅ţ̸͗!̸̼͋

Rust S̵̓i̸̓n̵̉ I̴n̴f̶e̸r̵n̷a̴l mutability! Howdy, friendly Rust developer! Ever had a value get m̵̯̅ð̶͊v̴̮̾ê̴̼͘d away right under your nose just when

null 294 Dec 23, 2022