Index: trunk/cmd/jounce/main.go
===================================================================
--- trunk/cmd/jounce/main.go	(revision 36)
+++ trunk/cmd/jounce/main.go	(revision 37)
@@ -16,19 +16,17 @@
 	}
 
+	srv := jounce.NewServer()
 	// TODO: load from config/DB
-	s := jounce.Server{
-		Hostname: "localhost",
-		Logger:   log.New(log.Writer(), "", log.LstdFlags),
-		Upstreams: []jounce.Upstream{{
-			Addr:     "chat.freenode.net:6697",
-			Nick:     "jounce",
-			Username: "jounce",
-			Realname: "jounce",
-			Channels: []string{"#jounce"},
-		}},
-	}
+	srv.Hostname = "localhost"
+	srv.Upstreams = []jounce.Upstream{{
+		Addr:     "chat.freenode.net:6697",
+		Nick:     "jounce",
+		Username: "jounce",
+		Realname: "jounce",
+		Channels: []string{"#jounce"},
+	}}
 
 	log.Printf("Server listening on %v", addr)
-	go s.Run()
-	log.Fatal(s.Serve(ln))
+	go srv.Run()
+	log.Fatal(srv.Serve(ln))
 }
Index: trunk/downstream.go
===================================================================
--- trunk/downstream.go	(revision 36)
+++ trunk/downstream.go	(revision 37)
@@ -47,4 +47,5 @@
 
 	registered bool
+	user       *user
 	closed     bool
 	nick       string
@@ -167,5 +168,19 @@
 
 func (c *downstreamConn) register() error {
+	c.srv.lock.Lock()
+	u, ok := c.srv.users[c.username]
+	c.srv.lock.Unlock()
+
+	if !ok {
+		c.messages <- &irc.Message{
+			Prefix:  c.srv.prefix(),
+			Command: irc.ERR_PASSWDMISMATCH,
+			Params:  []string{"*", "Invalid username or password"},
+		}
+		return nil
+	}
+
 	c.registered = true
+	c.user = u
 
 	c.messages <- &irc.Message{
@@ -174,5 +189,4 @@
 		Params:  []string{c.nick, "Welcome to jounce, " + c.nick},
 	}
-
 	c.messages <- &irc.Message{
 		Prefix:  c.srv.prefix(),
@@ -180,5 +194,4 @@
 		Params:  []string{c.nick, "Your host is " + c.srv.Hostname},
 	}
-
 	c.messages <- &irc.Message{
 		Prefix:  c.srv.prefix(),
@@ -186,5 +199,4 @@
 		Params:  []string{c.nick, "This server was created <datetime>"}, // TODO
 	}
-
 	c.messages <- &irc.Message{
 		Prefix:  c.srv.prefix(),
@@ -192,5 +204,4 @@
 		Params:  []string{c.nick, c.srv.Hostname, "jounce", "aiwroO", "OovaimnqpsrtklbeI"},
 	}
-
 	c.messages <- &irc.Message{
 		Prefix:  c.srv.prefix(),
@@ -199,6 +210,6 @@
 	}
 
-	c.srv.lock.Lock()
-	for _, uc := range c.srv.upstreamConns {
+	u.lock.Lock()
+	for _, uc := range u.upstreamConns {
 		// TODO: fix races accessing upstream connection data
 		if !uc.registered {
@@ -211,5 +222,5 @@
 		}
 	}
-	c.srv.lock.Unlock()
+	u.lock.Unlock()
 
 	return nil
Index: trunk/server.go
===================================================================
--- trunk/server.go	(revision 36)
+++ trunk/server.go	(revision 37)
@@ -3,4 +3,5 @@
 import (
 	"fmt"
+	"log"
 	"net"
 	"sync"
@@ -31,4 +32,12 @@
 }
 
+type user struct {
+	username string
+	srv      *Server
+
+	lock          sync.Mutex
+	upstreamConns []*upstreamConn
+}
+
 type Upstream struct {
 	Addr     string
@@ -45,6 +54,13 @@
 
 	lock            sync.Mutex
+	users           map[string]*user
 	downstreamConns []*downstreamConn
-	upstreamConns   []*upstreamConn
+}
+
+func NewServer() *Server {
+	return &Server{
+		Logger: log.New(log.Writer(), "", log.LstdFlags),
+		users:  make(map[string]*user),
+	}
 }
 
@@ -54,9 +70,16 @@
 
 func (s *Server) Run() {
+	// TODO: multi-user
+	u := &user{username: "jounce", srv: s}
+
+	s.lock.Lock()
+	s.users[u.username] = u
+	s.lock.Unlock()
+
 	for i := range s.Upstreams {
 		upstream := &s.Upstreams[i]
 		// TODO: retry connecting
 		go func() {
-			conn, err := connectToUpstream(s, upstream)
+			conn, err := connectToUpstream(u, upstream)
 			if err != nil {
 				s.Logger.Printf("failed to connect to upstream server %q: %v", upstream.Addr, err)
@@ -64,7 +87,7 @@
 			}
 
-			s.lock.Lock()
-			s.upstreamConns = append(s.upstreamConns, conn)
-			s.lock.Unlock()
+			u.lock.Lock()
+			u.upstreamConns = append(u.upstreamConns, conn)
+			u.lock.Unlock()
 
 			if err := conn.readMessages(); err != nil {
@@ -72,12 +95,12 @@
 			}
 
-			s.lock.Lock()
-			for i, c := range s.upstreamConns {
+			u.lock.Lock()
+			for i, c := range u.upstreamConns {
 				if c == conn {
-					s.upstreamConns = append(s.upstreamConns[:i], s.upstreamConns[i+1:]...)
+					u.upstreamConns = append(u.upstreamConns[:i], u.upstreamConns[i+1:]...)
 					break
 				}
 			}
-			s.lock.Unlock()
+			u.lock.Unlock()
 		}()
 	}
Index: trunk/upstream.go
===================================================================
--- trunk/upstream.go	(revision 36)
+++ trunk/upstream.go	(revision 37)
@@ -30,4 +30,5 @@
 	irc      *irc.Conn
 	srv      *Server
+	user     *user
 	messages chan<- *irc.Message
 
@@ -43,6 +44,6 @@
 }
 
-func connectToUpstream(s *Server, upstream *Upstream) (*upstreamConn, error) {
-	logger := &prefixLogger{s.Logger, fmt.Sprintf("upstream %q: ", upstream.Addr)}
+func connectToUpstream(u *user, upstream *Upstream) (*upstreamConn, error) {
+	logger := &prefixLogger{u.srv.Logger, fmt.Sprintf("upstream %q: ", upstream.Addr)}
 	logger.Printf("connecting to server")
 
@@ -58,5 +59,6 @@
 		net:      netConn,
 		irc:      irc.NewConn(netConn),
-		srv:      s,
+		srv:      u.srv,
+		user:     u,
 		messages: msgs,
 		channels: make(map[string]*upstreamChannel),
