Skip to content

Instantly share code, notes, and snippets.

@guybrush
Last active April 11, 2022 18:00
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save guybrush/11fcb03b8b61b23ae822 to your computer and use it in GitHub Desktop.
Save guybrush/11fcb03b8b61b23ae822 to your computer and use it in GitHub Desktop.
p2p-lobby

p2p-lobby

this is only proof-of-concept for now, code and readme is just a temporary braindump and may change a lot - stay tuned :D

the goal is to provide a simple way to distribute and discover p2p-app-lobbies. a lobby is basically a "room" for people (peers) that are connected to each other via p2p (webrtc). in every lobby the connected peers run a specific application (javascript-program) that utilizes the connections between the peers in some way (video/audio, reliable and unreliable transport). apps can be created by loading the bundled source-code into the browser (drag&drop → indexedDB). there can be lobbies for all sorts of applications. like a video-chat or a p2p-game or file-sharing-app or a wiki or a package-manager-registry or ..

this excellent talk describes well what i want to have: 22C3 - Peer to peer under the hood (32:11). using the terminiology of the talk, p2p-lobby would be an "application subnet" and each lobby would be a "service subnet". so we have a p2p-network with lots of peers that are not all connected to each other but only to N randomly choosen peers. once peers join a lobby they make sure to be connected to each other so the application can utilize the fully connected network between all participating peers. at this point no server is needed for the introduction of peers that join the lobby, it can be done through the network the peers are already part of.

every user/peer can start/create a lobby that "hosts" an app and other users can join the lobby. the information about which lobbies are currently "hosting" which application and which peers are connected to it is distributed with throttled balanced slotted broadcasts. users who dont have the app's source-code will get it from the other peers - so there are no servers needed to host the applications.

p2p-lobby provides a user-interface to create apps and to create/join lobbies but it can also be used programatically in any website/js-application. or you can just download dist/p2plobby.html and browse it via file:///...

anyway, this is just an idea for now and lots of things are unclear at the moment and need to be proof-of-concept'ed (i am currently writing an offline simulation with visualization to better understand how it would work). but basically the hard work has already been done by feross, mafintosh, substack and other amazing open-source-enthusiasts (see resources)! so if you have any suggestions or questions, please talk in #stackvm on freenode or create an issue!

usage

p2p-lobby can be used in any website/js-app

var p2pl = require('p2p-lobby')()

either use the user-interface:

document.body.appendChild(p2pl.el)

or use the api programmatically:

// handle all the provided events
p2pl.on('error',onError)
p2pl.on('lobby',onLobby)

// connect to signaling servers
p2pl.connect(['https://example.com','https://signalhub.p2plobby.org','https://localhost:1337'])

// list lobbies
p2pl.lobbiesByHash['<hash-of-app-source>'] // array of lobbies hosting a specific app
p2pl.lobbies // array of lobbies

// join a lobby
var lobby = p2pl.lobbiesByHash['<hash-of-app-source>'][0]
// or: lobby = p2pl.lobbies[0]
lobby.join()
lobby.on('peer',onLobbyPeer) // not sure..
lobby.send('hello guys!')
lobby.leave()

// create a lobby
var lobby = p2pl.createLobby('<hash-of-app-source>')
lobby.on('peer',onLobbyPeer)
lobby.send('hello there! lets wait for 3 more and then start the game!')
lobby.leave()

// create an app
var app = p2pl.createApp(
  { name: 'name of the application'
  , info: 'short information about this'
  , logo: '<url-encoded-logo-picture>'
  , source: App.toString()
  } )

// a simple p2p-application
function App(lobby) {
  console.log('the game will start immediately')
  for (var i=0; i<lobby.peers.length; i++) addPeerToGame(lobby.peers[i])
  tick()
  function tick() {
    logic()
    render()
    lobby.send(JSON.stringify({type:'control',data:'<user-input>'})
    requestAnimationFrame(tick)
  }
  function addPeerToGame(p) {
    p.on('data', function(d){
      if (d.type == 'control') applyControl(p, d.data)
    })
  }
  // ...
}

signaling

  • users (peers) connect to a one or multiple (for redundancy and robustness) signalhub's (provided by the community or self-hosted) to introduce them to each other
  • make it super easy (!) to host a server
    • i would prefer to write the server in golang, so i can provide a single binary one can just start
      • i.e. port mafintosh/signalhub to golang
    • a usecase would be where a group of people is on a lan without internet-connection. someone can just start a signaling-server in the lan and tell the others to connect to it for peer-introduction.
  • make it easy to introduce available servers to users
    • users can store servers in indexedDB
  • also provide a way to do server-less signaling (in the user-interface)

authentication

  • users/peers store an asymmetric keypair in the browser via keyboot
  • users can publish a signed message somewhere on the web (e.g. homepage, gist, anything with CORS enabled) to prove their identity
  • what happens when the user loses the key-pair or is on another browser and/or machine?
    • make it possible to make backups
    • make it possible to move/copy keypair to multiple browsers/devices
    • make it possible to revoke keys?
    • use multiple keypairs?
  • currently keyboot only works properly in chrome (for me)
    • the load event of the iframe is not fireing in firefox
    • SharedWorker and thus page-bus is only supported by chrome and firefox
    • this will hopefully get better and more robust over time (also ServiceWorker looks promising)
  • also make it possible to use p2p-lobby without having to use keyboot (anonymous)

distribution

  • apps are stored locally in the browser via casboot. either the user uploads the app into the browser via drag&drop or gets the app via p2p from other users. an app consists of meta-data and the bundle which contains the source-code. we separate meta-data and bundle because otherwise the Merkle-DAG would contain too much data (i think..). so once a peer is connected it will receive

    // <app-hash> points to the metadata in the Merkel Dag
    <app-hash> = sha1(JSON.stringify(
      { name   : <app-name>        // 1-200 characters ?
      , info   : <app-info>        // 1-400 characters ?
      , logo   : <app-logo>        // max 10kb base64(image) ?
      , source : <app-source-hash> // points to <app-source> in the content-addressable-store
      , author : <app-author>      // fingerprint of public key
      , sign   : <app-author-sign> // sign(<app-name><app-info><app-logo><app-source-hash><app-author>)
      } ))
    
    • the json can be generated with p2p-lobby-cli or by drag&drop into the browser (then it will use the keyboot-keypair)
    • <app-source> (the content of the app-bundle) is not directly embedded into the <app-source-hash> points to
    • maybe this could be distributed with webtorrent (instead of casboot)?
    • make it possible to use p2p-lobby without having to use casboot
    • throttled balanced slotted broadcasts
      • put a counter on every message that gets increased everytime a peer forwards a message. peers only forward messages with a counter < N.
  • information about which lobbies are currently open/hosted is gossiped via hyperlog

    • a lobby consists of

      • the lobby-id
      • the app-hash of the app it is "hosting"
      • the hash of public-keys of users that are in the lobby
    • the merkle dag would looks like this:

      top-hash
      |-- <hash-0> lobby-for-appA
      |   |-- <hash-0-0-0> lobby-for-appA-<lobbyId0>
      |   |   `-- <hash-0-0-1> lobby-for-appA-<lobbyId0>
      |   |       `-- <hash-0-0-2> lobby-for-appA-<lobbyId0>
      |   |-- <hash-0-0> lobby-for-appA-<lobbyId1>
      |   :
      |-- <hash-1> lobby-for-appB
      |
      :
      
  • how to join a lobby

    • user-interface
      • click join on a lobby in the list of lobbies
    • link
      • https://p2plobby.org#join-<lobbyId>
      • file:///Z:/path/to/p2plobby.html#join-<lobbyId>
  • how to create a lobby

    • user-interface
      • select an app in the list of available apps and click create
      • available apps are
        • locally stored apps
        • currently hosted apps (by other peers)
    • link
      • with app-hash
        • https://p2plobby.org#create-<app-hash>
        • file:///Z:/path/to/p2plobby.html#create-<app-hash>
      • maybe also compressed source in the link? (see mrdoob/htmleditor)
        • https://p2plobby.org#createFromSource-<rawinflate(source-code)>
        • file:///Z:/path/to/p2plobby.html#createFromSource-<rawinflate(source-code)>
        • this would automatically store the source-code in the content-addressable-store
  • how is an app exactly defined? (see distribution)

    • should an app be signed by the keyboot-pair or some other keypair?

    • im currently separating app-metadata and app-bundle (the source-code) to avoid having to distribute too much data through the Merkel-DAG. so the peers can see name/info/logo of apps without having to download the app-bundle. im not sure about this. (see distribution)

    • how about putting the app-metadata right into the bundle?

      -----BEGIN P2PLOBBY SIGNATURE-----
      { "name":"app name"
      , "info":"some info"
      , "logo":"<base64-encoded-image>"
      , "source-hash":"<hash-of-source-without-metadata>"
      , "author":"<author-fingerprint>"
      , "signature":"<sign>"
      }
      -----END P2PLOBBY SIGNATURE-----
      
  • when users join a lobby should the application take over immediately?

    • how to get back to lobbies?
    • put application in a sandboxed iframe so it cant change things outside?
    • load application in a new tab?
  • should apps be distributed via casboot or just some sort of api for webtorrent?

  • should p2plobby itself be hosted with hyperboot?

    • i will host it with hyperboot anyway i think, but should it be advertised to be loaded from dist/p2p-lobby.html? (because version/api/break?)
  • security

    • what are attack-vectors?
      • malicious apps
        • the current approach (all the apps in one domain) make iframes useless to protect anything since the iframe can just access window.parent (though i need to checkout CSP)
        • if we host every app on a subdomain (exactly like https://htmlb.in) every app would require permissions explicitly
          • proof (you have to grant permission explicitly per subdomain):
          • what are the implications of hosting apps on subdomains?
            • one has to host a p2p-lobby-server (beside signaling-servers), you cant just drop the p2p-lobby.html somewhere (file:///, github, ..)
              • this server would not have to do much at all, just serve / on every subdomain. but still you have to deploy it
          • because of the implications i want to make really sure that this would be the only way to be secure
        • implement some permission-schema?
      • ddos
    • implement a "paranoid-mode" where you only connect to trusted peers

resources

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment