diff --git a/pb/.gitignore b/pb/.gitignore
new file mode 100644
index 000000000..1caad971e
--- /dev/null
+++ b/pb/.gitignore
@@ -0,0 +1,5 @@
+target
+*.swp
+*~
+bin
+.bundle
diff --git a/pb/.ruby-gemset b/pb/.ruby-gemset
new file mode 100644
index 000000000..f6191d354
--- /dev/null
+++ b/pb/.ruby-gemset
@@ -0,0 +1 @@
+jam-pb
diff --git a/pb/.ruby-version b/pb/.ruby-version
new file mode 100644
index 000000000..abf2ccea0
--- /dev/null
+++ b/pb/.ruby-version
@@ -0,0 +1 @@
+ruby-2.0.0-p247
diff --git a/pb/Gemfile b/pb/Gemfile
new file mode 100644
index 000000000..7f7d5888b
--- /dev/null
+++ b/pb/Gemfile
@@ -0,0 +1,5 @@
+source 'https://rubygems.org'
+
+workspace = ENV["WORKSPACE"] || "~/workspace"
+
+gem 'ruby-protocol-buffers', '1.2.2'
diff --git a/pb/Gemfile.lock b/pb/Gemfile.lock
new file mode 100644
index 000000000..5151acc4d
--- /dev/null
+++ b/pb/Gemfile.lock
@@ -0,0 +1,10 @@
+GEM
+ remote: https://rubygems.org/
+ specs:
+ ruby-protocol-buffers (1.2.2)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ ruby-protocol-buffers (= 1.2.2)
diff --git a/pb/README.md b/pb/README.md
new file mode 100644
index 000000000..d4bfb340f
--- /dev/null
+++ b/pb/README.md
@@ -0,0 +1,29 @@
+Jam-Pb (Protocol Buffers)
+=========================
+
+Environment
+-----------
+
+* Download and install protoc 2.4.1 on your PATH: [Protocol Buffers Download](http://code.google.com/p/protobuf/downloads/list)
+* Bash (on windows, [Git Bash[(http://code.google.com/p/msysgit/) was used successfully)]
+* Optional: if you have 'bundle' available (ruby dependency management tool), protocol buffers for ruby will also build.
+* Optional: if you have protoc-c on your PATH, protocol buffers for c will also build.
+
+Building
+--------
+
+./build
+
+Using
+-----
+After a ./build:
+
+* ./target/cpp has cpp output
+* ./target/ruby has ruby output
+* ./target/c has c output
+* ./target/java has java output
+
+Contributing
+------------
+
+Edit src/client-container.proto to add or change existing messages. That file has more contextual information in it.
diff --git a/pb/build b/pb/build
new file mode 100755
index 000000000..47f91d514
--- /dev/null
+++ b/pb/build
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+TARGET=target
+SRC=src
+
+# clean
+rm -rf $TARGET
+# prep output
+mkdir $TARGET
+
+# find all protocol buffer files in src directory
+#PROTO_FILES=`find src -name *.proto`
+PROTO_FILES=src/client_container.proto
+
+# make variables available to sub-scripts
+export SRC TARGET PROTO_FILES
+
+set -e
+
+# cpp/java/python supported by protoc
+./build_protoc
+# ruby supported by rprotoc
+./build_rprotoc
+# c supported by protoc-c
+./build_protoc-c
diff --git a/pb/build_protoc b/pb/build_protoc
new file mode 100755
index 000000000..248406f32
--- /dev/null
+++ b/pb/build_protoc
@@ -0,0 +1,27 @@
+#!/bin/bash
+
+# this build file uses protoc to build all protocol buffers
+# http://code.google.com/p/protobuf/downloads/list
+
+
+TARGET_CPP="$TARGET/cpp"
+TARGET_JAVA="$TARGET/java"
+
+mkdir -p "$TARGET_CPP"
+mkdir -p "$TARGET_JAVA"
+
+# if you don't want to put protoc on the command line,
+# then set a PROTOC environment variable
+if [ -z $PROTOC] ; then
+ PROTOC="protoc"
+fi
+
+command -v $PROTOC >/dev/null 2>&1 || { echo >&2 "protoc is required but not installed. Aborting"; exit 1; }
+
+# die on error at this point
+set -e
+
+echo "building cpp protocol buffers"
+$PROTOC $PROTO_FILES --cpp_out=$TARGET_CPP --proto_path=$SRC
+echo "building java protocol buffers"
+$PROTOC $PROTO_FILES --java_out=$TARGET_JAVA --proto_path=$SRC
diff --git a/pb/build_protoc-c b/pb/build_protoc-c
new file mode 100755
index 000000000..05171eaea
--- /dev/null
+++ b/pb/build_protoc-c
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+# this build file uses protoc to build all protocol buffers
+# http://code.google.com/p/protobuf/downloads/list
+
+TARGET_C="$TARGET/c"
+
+mkdir -p "$TARGET_C"
+
+# if you don't want to put protoc on the command line,
+# then set a PROTOC environment variable
+if [ -z $PROTOCC] ; then
+ PROTOCC="protoc-c"
+fi
+
+# running protoc-c is currently optional
+command -v $PROTOCC >/dev/null 2>&1 || { echo >&2 "protoc-c is required but not installed. Skipping c protocol buffers."; exit 0; }
+
+# die on error at this point
+set -e
+
+echo "building c protocol buffers"
+$PROTOCC $PROTO_FILES --c_out=$TARGET_C --proto_path=$SRC
diff --git a/pb/build_rprotoc b/pb/build_rprotoc
new file mode 100755
index 000000000..e019cc519
--- /dev/null
+++ b/pb/build_rprotoc
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+# ruby protocol buffers
+# http://code.google.com/p/ruby-protobuf/
+RUBY_OUT=$TARGET/ruby
+
+# we exit with 0; treat ruby as optional at the moment
+command -v "bundle" >/dev/null 2>&1 || { echo >&2 "bundle is required but not installed. Skipping ruby protocol buffers."; exit 0; }
+
+# creates a bin folder with 'rprotoc' command inside
+bundle install --binstubs > /dev/null
+
+# die on error at this point
+set -e
+
+echo "building ruby protocol buffers"
+bin/ruby-protoc $PROTO_FILES --proto_path $SRC --ruby_out $RUBY_OUT
+
+./package_ruby
+
diff --git a/pb/ivy.xml b/pb/ivy.xml
new file mode 100644
index 000000000..3e6df90b2
--- /dev/null
+++ b/pb/ivy.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/pb/ivysettings.xml b/pb/ivysettings.xml
new file mode 100644
index 000000000..ba74af988
--- /dev/null
+++ b/pb/ivysettings.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/pb/jenkins b/pb/jenkins
new file mode 100755
index 000000000..2d3bb662f
--- /dev/null
+++ b/pb/jenkins
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+GEM_SERVER=http://localhost:9000/gems
+
+echo "starting build..."
+./build
+
+if [ "$?" = "0" ]; then
+ echo "build succeeded"
+ echo "publishing gem"
+ pushd "target/ruby/jampb"
+ find . -name *.gem -exec curl -f -T {} $GEM_SERVER/{} \;
+
+ if [ "$?" != "0" ]; then
+ echo "publish failed"
+ exit 1
+ fi
+ popd
+ echo "done publishing gems"
+else
+ echo "build failed"
+ exit 1
+fi
+
+
diff --git a/pb/package_java b/pb/package_java
new file mode 100755
index 000000000..83bb0e8da
--- /dev/null
+++ b/pb/package_java
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+# not yet integrated into any formal build step.
+# usage: after you've run ./build, run ./package_java to generate a jar
+set -e
+
+echo "packaging java"
+# retrieve protocol buffer dependency
+java -jar $IVY -sync -retrieve "target/lib/[conf]/[artifact]-[type].[ext]"
+# compile java file generated by protocol buffrs
+javac -cp target/lib/default/protobuf-java-jar.jar target/java/jampb/ClientContainer.java
+pushd target/java/jampb
+# create jar with all classes generated by javac
+jar cvf jampb-clientcontainer.jar `find . -name \*.class`
+popd
+
diff --git a/pb/package_ruby b/pb/package_ruby
new file mode 100755
index 000000000..65731589b
--- /dev/null
+++ b/pb/package_ruby
@@ -0,0 +1,64 @@
+#!/bin/bash
+
+echo "packaging ruby protocol buffers"
+pushd target/ruby > /dev/null
+
+rm -rf jampb
+
+bundle gem jampb > /dev/null
+
+# copy over built ruby code
+cp src/*.rb jampb/lib/jampb
+
+if [ -z $BUILD_NUMBER ]; then
+ BUILD_NUMBER="1"
+fi
+
+VERSION="0.0.${BUILD_NUMBER}"
+
+pushd jampb > /dev/null
+
+cat > lib/jampb/version.rb << EOF
+module Jampb
+ VERSION = "$VERSION"
+end
+EOF
+
+
+# define gemspec
+cat >> jampb.gemspec << EOF
+
+# -*- encoding: utf-8 -*-
+require File.expand_path('../lib/jampb/version', __FILE__)
+
+Gem::Specification.new do |gem|
+ gem.authors = ["Seth Call"]
+ gem.email = ["seth@jamkazam.com"]
+ gem.description = %q{protocol buffers for jamkazam}
+ gem.summary = %q{protocol buffers for jamkazam.}
+ gem.homepage = "http://www.jamkazam.com"
+
+ gem.files = ['Gemfile', 'Rakefile', 'README.md', 'jampb.gemspec', 'lib/jampb.rb', 'lib/jampb/client_container.pb.rb', 'lib/jampb/version.rb' ]
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
+ gem.name = "jampb"
+ gem.require_paths = ["lib"]
+ gem.version = Jampb::VERSION
+end
+EOF
+
+# make sure library manifest includes the generated ruby file
+cat >> lib/jampb.rb << EOF
+require "jampb/version"
+require "jampb/client_container.pb"
+
+module Jampb
+end
+
+EOF
+
+gem build jampb.gemspec > /dev/null
+
+popd > /dev/null
+popd > /dev/null
+
diff --git a/pb/src/client_container.proto b/pb/src/client_container.proto
new file mode 100644
index 000000000..31c0fcad4
--- /dev/null
+++ b/pb/src/client_container.proto
@@ -0,0 +1,322 @@
+
+// adding a new message:
+// within ClientMessage block,
+// create a new field in the enum Type section (100s are meant to be normal messages; 1000s are meant to be errors). It will have new field number X
+// create a optional message of the same name (but lower-case! the json code requires this), and use the same field number X.
+// then at bottom of the file (or wherever; just outside any existing blocks), add your message definition.
+
+package jampb;
+
+message ClientMessage {
+ enum Type {
+ LOGIN = 100;
+ LOGIN_ACK = 101;
+
+ LOGIN_MUSIC_SESSION = 102;
+ LOGIN_MUSIC_SESSION_ACK = 103;
+ FRIEND_SESSION_JOIN = 104;
+ LEAVE_MUSIC_SESSION = 105;
+ LEAVE_MUSIC_SESSION_ACK = 106;
+ HEARTBEAT = 107;
+ FRIEND_UPDATE = 108;
+ SESSION_INVITATION = 109;
+ MUSICIAN_SESSION_DEPART = 110;
+ JOIN_REQUEST = 111;
+ FRIEND_REQUEST = 112;
+ FRIEND_REQUEST_ACCEPTED = 113;
+ MUSICIAN_SESSION_JOIN = 114;
+ MUSICIAN_SESSION_FRESH = 115;
+ MUSICIAN_SESSION_STALE = 116;
+ HEARTBEAT_ACK = 117;
+
+ TEST_SESSION_MESSAGE = 200;
+
+ PING_REQUEST = 300;
+ PING_ACK = 301;
+ PEER_MESSAGE = 302;
+ TEST_CLIENT_MESSAGE = 303;
+
+ SERVER_BAD_STATE_RECOVERED = 900;
+
+ SERVER_GENERIC_ERROR = 1000;
+ SERVER_REJECTION_ERROR = 1001;
+ SERVER_PERMISSION_ERROR = 1002;
+ SERVER_BAD_STATE_ERROR = 1003;
+ }
+
+ // Identifies which inner message is filled in
+ required Type type = 1;
+ required string route_to = 2;
+ optional string from = 3;
+ optional string message_id = 4;
+ optional string in_reply_to = 5;
+
+ // One of the following messages can be populated:
+
+ // Client-Server messages (to/from)
+ optional Login login = 100; // to server
+ optional LoginAck login_ack = 101; // from server
+ optional LoginMusicSession login_music_session = 102; // to server
+ optional LoginMusicSessionAck login_music_session_ack = 103; // from server
+ optional FriendSessionJoin friend_session_join = 104; // from server to all members
+ optional LeaveMusicSession leave_music_session = 105;
+ optional LeaveMusicSessionAck leave_music_session_ack = 106;
+ optional Heartbeat heartbeat = 107;
+ optional FriendUpdate friend_update = 108; // from server to all friends of user
+ optional SessionInvitation session_invitation = 109; // from server to user
+ optional MusicianSessionDepart musician_session_depart = 110;
+ optional JoinRequest join_request = 111;
+ optional FriendRequest friend_request = 112;
+ optional FriendRequestAccepted friend_request_accepted = 113;
+ optional MusicianSessionJoin musician_session_join = 114;
+ optional MusicianSessionFresh musician_session_fresh = 115;
+ optional MusicianSessionStale musician_session_stale = 116;
+ optional HeartbeatAck heartbeat_ack = 117;
+
+ // Client-Session messages (to/from)
+ optional TestSessionMessage test_session_message = 200;
+
+ // Client-Client messages (to/from)
+ optional PingRequest ping_request = 300;
+ optional PingAck ping_ack = 301;
+ optional PeerMessage peer_message = 302;
+ optional TestClientMessage test_client_message = 303;
+
+ // Server-to-Client special messages
+ optional ServerBadStateRecovered server_bad_state_recovered = 900;
+
+ // Server-to-Client errors
+ optional ServerGenericError server_generic_error = 1000;
+ optional ServerRejectionError server_rejection_error = 1001;
+ optional ServerPermissionError server_permission_error = 1002;
+ optional ServerBadStateError server_bad_state_error = 1003;
+}
+
+// route_to: server
+// sent from client to server to associate the connection with a user
+// either supply a username/password, or just supply token
+// if successful, a LoginAck is sent.
+// if errored, a GenericServerError is sent and the connection is closed.
+message Login {
+ optional string username = 1; // username... could be email. need to wait for prod requirements
+ optional string password = 2; // a password
+ optional string token = 3; // a token/cookie from previous successful login attempt or from 'token' field in .music file
+ optional string client_id = 4; // if supplied, the server will accept this client_id as the unique Id of this client instance
+ optional string reconnect_music_session_id = 5; // if supplied, the server will attempt to log the client into this session (designed for reconnect scenarios while in-session)
+
+}
+
+// route_to: client
+// sent from server to client to let the client know the login was successful,
+// and to also show the IP address of the client as seen by the server.
+message LoginAck {
+ optional string public_ip = 1;
+ optional string client_id = 2; // a new client_id if none is supplied in Login, or just the original client_id echoed back
+ optional string token = 3; // the remember me token. This is useful if you logged in with username/password (otherwise it's just 'token' echoed back)
+ optional int32 heartbeat_interval = 4; // set your heartbeat interval to this value
+ optional string music_session_id = 5; // the music session that the user was in very recently (likely due to dropped connection)
+ optional bool reconnected = 6; // if reconnect_music_session_id is specified, and the server could log the user into that session, then true is returned.
+}
+
+// route_to: server
+// send from client to server to log in to a music session and be 'present'.
+// if successful, a LoginMusicSessionAck is sent with error = false, and Client-Session messages will be passed
+// directly from client to all other client. Also, the server will generate a 'UserJoinedMusicSession' message
+// and send it to all other users to let the others know the user is here.
+// if errored, a LoginMusicSessionAck is sent with error = true.
+message LoginMusicSession {
+ optional string music_session = 1;
+}
+
+// route_to: client
+// error = true if the LoginMusicSession command failed.
+message LoginMusicSessionAck {
+ optional bool error = 1;
+ optional string error_reason = 2;
+}
+
+// route_to: server
+// send from client to server to leave a previously joined music session
+message LeaveMusicSession {
+ optional string music_session = 1;
+}
+
+// route_to: client
+// error = true if the session didn't exist, but otherwise this is intended to always work barring server unrecoverable errors
+message LeaveMusicSessionAck {
+ optional bool error = 1;
+ optional string error_reason = 2;
+}
+
+// route_to: client:
+// sent by server to let the rest of the participants know a user has joined.
+message FriendSessionJoin {
+ optional string session_id = 1; // the session ID
+ optional string user_id = 2; // this is the user_id and can be used for user unicast messages
+ optional string username = 3; // meant to be a display name
+ optional string photo_url = 4;
+}
+
+// route_to: client:
+// sent by server to let the rest of the participants know a user has joined.
+message MusicianSessionJoin {
+ optional string session_id = 1; // the session ID
+ optional string user_id = 2; // this is the user_id and can be used for user unicast messages
+ optional string username = 3; // meant to be a display name
+ optional string photo_url = 4;
+}
+
+// route_to: client:
+// sent by server to let the rest of the participants know a user has left.
+message MusicianSessionDepart {
+ optional string session_id = 1; // the session ID
+ optional string user_id = 2; // this is the user_id and can be used for user unicast messages
+ optional string username = 3; // meant to be a display name
+ optional string photo_url = 4;
+}
+
+// route_to: client:
+// sent by server to let the rest of the participants know a client has become active again after going stale
+message MusicianSessionFresh {
+ optional string session_id = 1; // the session ID
+ optional string user_id = 2; // this is the user_id and can be used for user unicast messages
+ optional string username = 3; // meant to be a display name
+ optional string photo_url = 4;
+}
+
+// route_to: client:
+// sent by server to let the rest of the participants know a user has gone stale (websocket connection dropped)
+message MusicianSessionStale {
+ optional string session_id = 1; // the session ID
+ optional string user_id = 2; // this is the user_id and can be used for user unicast messages
+ optional string username = 3; // meant to be a display name
+ optional string photo_url = 4;
+}
+
+message JoinRequest {
+ optional string join_request_id = 1;
+ optional string username = 2;
+ optional string text = 3;
+}
+
+// route_to: session
+// a test message used by ruby-client currently. just gives way to send out to rest of session
+message TestSessionMessage {
+ optional string msg = 1;
+}
+
+// route_to: client:[CLIENT_ID]
+// asks a client to begin a ping session
+message PingRequest {
+
+}
+
+// route_to: client:[CLIENT_ID]
+// tells a client to begin pinging a session
+message PingAck {
+
+}
+
+// route_to: client:[CLIENT_ID]
+// generic message between clients
+message PeerMessage {
+ optional string message = 1;
+}
+
+// route_to: client:[CLIENT_ID]
+// a test message used by ruby-client currently. just gives a way to send directly to another client
+message TestClientMessage {
+ optional string msg = 1;
+}
+
+// route_to: server
+// sent from client to server periodically to let server track if the client is truly alive and avoid TCP timeout scenarios
+// the server will send a HeartbeatAck in response to this
+message Heartbeat {
+
+}
+
+// target: client
+// sent from server to client in response to a Heartbeat
+message HeartbeatAck {
+}
+
+// target: client
+// send from server to client when user sends a friend request
+message FriendRequest {
+ optional string friend_request_id = 1;
+ optional string user_id = 2;
+ optional string name = 3;
+ optional string photo_url = 4;
+ optional string friend_id = 5;
+ optional string msg = 6;
+ optional string notification_id = 7;
+ optional string created_at = 8;
+}
+
+// target: client
+message FriendRequestAccepted {
+ optional string friend_id = 1; // accepter
+ optional string name = 2;
+ optional string photo_url = 3;
+ optional string user_id = 4; // original requester
+ optional string msg = 5;
+ optional string notification_id = 6;
+ optional string created_at = 7;
+}
+
+// target: client
+// send from server to client when a user logs in
+message FriendUpdate {
+ optional string user_id = 1;
+ optional string name = 2;
+ optional string photo_url = 3;
+ optional bool online = 4;
+ optional string msg = 5;
+}
+
+// route_to: user:[USER_ID]
+// let a user know they've been invited to a session
+message SessionInvitation {
+ optional string invitation = 1;
+}
+
+// route_to: client
+// this should follow a ServerBadStateError in the case that the
+// websocket gateway recovers from whatever ailed it
+message ServerBadStateRecovered {
+}
+
+
+
+// route_to: client
+// this indicates unhandled error on server
+// if you receive this, your connection will close after.
+// but gives the client a chance to know why.
+message ServerGenericError {
+ optional string error_msg = 1;
+}
+
+// route_to: client
+// this indicates the client did something wrong, and the server is mad enough to close connection.
+// if you receive this, your connection will close after.
+// but gives the client a chance to know why.
+message ServerRejectionError {
+ optional string error_msg = 1;
+}
+
+// route_to: client
+// this indicates that the client doesn't have permission to ask the command
+// your connection is not closed if this message occurs
+message ServerPermissionError {
+ optional string error_msg = 1;
+}
+
+// route_to: client
+// this indicates that the server is in a bad state, but could recover later, and is unable to process the request
+// your connection is not closed if this message occurs
+message ServerBadStateError {
+ optional string error_msg = 1;
+}
+
+