Index: /trunk/db.go
===================================================================
--- /trunk/db.go	(revision 567)
+++ /trunk/db.go	(revision 568)
@@ -31,4 +31,5 @@
 	Username string
 	Password string // hashed
+	Realname string
 	Admin    bool
 }
@@ -93,7 +94,10 @@
 }
 
-func (net *Network) GetRealname() string {
+func GetRealname(user *User, net *Network) string {
 	if net.Realname != "" {
 		return net.Realname
+	}
+	if user.Realname != "" {
+		return user.Realname
 	}
 	return net.Nick
Index: /trunk/db_sqlite.go
===================================================================
--- /trunk/db_sqlite.go	(revision 567)
+++ /trunk/db_sqlite.go	(revision 568)
@@ -17,5 +17,6 @@
 	username VARCHAR(255) NOT NULL UNIQUE,
 	password VARCHAR(255),
-	admin INTEGER NOT NULL DEFAULT 0
+	admin INTEGER NOT NULL DEFAULT 0,
+	realname VARCHAR(255)
 );
 
@@ -134,4 +135,5 @@
 	"ALTER TABLE Channel ADD COLUMN detached_internal_msgid VARCHAR(255)",
 	"ALTER TABLE Network ADD COLUMN enabled INTEGER NOT NULL DEFAULT 1",
+	"ALTER TABLE User ADD COLUMN realname VARCHAR(255)",
 }
 
@@ -243,10 +245,11 @@
 	user := &User{Username: username}
 
-	var password sql.NullString
-	row := db.db.QueryRow("SELECT id, password, admin FROM User WHERE username = ?", username)
-	if err := row.Scan(&user.ID, &password, &user.Admin); err != nil {
+	var password, realname sql.NullString
+	row := db.db.QueryRow("SELECT id, password, admin, realname FROM User WHERE username = ?", username)
+	if err := row.Scan(&user.ID, &password, &user.Admin, &realname); err != nil {
 		return nil, err
 	}
 	user.Password = password.String
+	user.Realname = realname.String
 	return user, nil
 }
@@ -257,13 +260,14 @@
 
 	password := toNullString(user.Password)
+	realname := toNullString(user.Realname)
 
 	var err error
 	if user.ID != 0 {
-		_, err = db.db.Exec("UPDATE User SET password = ?, admin = ? WHERE username = ?",
-			password, user.Admin, user.Username)
+		_, err = db.db.Exec("UPDATE User SET password = ?, admin = ?, realname = ? WHERE username = ?",
+			password, user.Admin, realname, user.Username)
 	} else {
 		var res sql.Result
-		res, err = db.db.Exec("INSERT INTO User(username, password, admin) VALUES (?, ?, ?)",
-			user.Username, password, user.Admin)
+		res, err = db.db.Exec("INSERT INTO User(username, password, admin, realname) VALUES (?, ?, ?, ?)",
+			user.Username, password, user.Admin, realname)
 		if err != nil {
 			return err
Index: /trunk/doc/soju.1.scd
===================================================================
--- /trunk/doc/soju.1.scd	(revision 567)
+++ /trunk/doc/soju.1.scd	(revision 568)
@@ -161,5 +161,6 @@
 
 	*-realname* <realname>
-		Connect with the specified real name. By default, the nickname is used.
+		Connect with the specified real name. By default, the account's realname
+		is used if set, otherwise the network's nickname is used.
 
 	*-nick* <nickname>
@@ -297,6 +298,18 @@
 	Disable SASL authentication and remove stored credentials.
 
-*user create* -username <username> -password <password> [-admin]
+*user create* -username <username> -password <password> [options...]
 	Create a new soju user. Only admin users can create new accounts.
+
+	Options:
+
+	*-admin*
+		Make the new user an administrator.
+
+	*-realname* <realname>
+		Set the user's realname. This is used as a fallback if there is no
+		realname set for a network.
+
+*user update* [-realname <realname>]
+	Update the current user.
 
 *user delete* <username>
Index: /trunk/downstream.go
===================================================================
--- /trunk/downstream.go	(revision 567)
+++ /trunk/downstream.go	(revision 568)
@@ -84,6 +84,6 @@
 		attrs["username"] = irc.TagValue(network.Username)
 	}
-	if network.Realname != "" {
-		attrs["realname"] = irc.TagValue(network.Realname)
+	if realname := GetRealname(&network.user.User, &network.Network); realname != "" {
+		attrs["realname"] = irc.TagValue(realname)
 	}
 
@@ -1388,4 +1388,11 @@
 		}
 
+		// If the client just resets to the default, just wipe the per-network
+		// preference
+		storeRealname := realname
+		if realname == dc.user.Realname {
+			storeRealname = ""
+		}
+
 		var storeErr error
 		var needUpdate []Network
@@ -1399,5 +1406,5 @@
 				})
 
-				n.Realname = realname
+				n.Realname = storeRealname
 				if err := dc.srv.db.StoreNetwork(dc.user.ID, &n.Network); err != nil {
 					dc.logger.Printf("failed to store network realname: %v", err)
@@ -1408,5 +1415,5 @@
 
 			record := n.Network // copy network record because we'll mutate it
-			record.Realname = realname
+			record.Realname = storeRealname
 			needUpdate = append(needUpdate, record)
 		})
@@ -2224,4 +2231,8 @@
 			pass, _ := attrs.GetTag("pass")
 
+			if realname == dc.user.Realname {
+				realname = ""
+			}
+
 			// TODO: reject unknown attributes
 
Index: /trunk/service.go
===================================================================
--- /trunk/service.go	(revision 567)
+++ /trunk/service.go	(revision 568)
@@ -255,8 +255,13 @@
 			children: serviceCommandSet{
 				"create": {
-					usage:  "-username <username> -password <password> [-admin]",
+					usage:  "-username <username> -password <password> [-realname <realname>] [-admin]",
 					desc:   "create a new soju user",
 					handle: handleUserCreate,
 					admin:  true,
+				},
+				"update": {
+					usage:  "[-realname <realname>]",
+					desc:   "update the current user",
+					handle: handleUserUpdate,
 				},
 				"delete": {
@@ -267,5 +272,4 @@
 				},
 			},
-			admin: true,
 		},
 		"change-password": {
@@ -752,4 +756,5 @@
 	username := fs.String("username", "", "")
 	password := fs.String("password", "", "")
+	realname := fs.String("realname", "", "")
 	admin := fs.Bool("admin", false, "")
 
@@ -772,4 +777,5 @@
 		Username: *username,
 		Password: string(hashed),
+		Realname: *realname,
 		Admin:    *admin,
 	}
@@ -779,4 +785,20 @@
 
 	sendServicePRIVMSG(dc, fmt.Sprintf("created user %q", *username))
+	return nil
+}
+
+func handleUserUpdate(dc *downstreamConn, params []string) error {
+	fs := newFlagSet()
+	realname := fs.String("realname", "", "")
+
+	if err := fs.Parse(params); err != nil {
+		return err
+	}
+
+	if err := dc.user.updateRealname(*realname); err != nil {
+		return err
+	}
+
+	sendServicePRIVMSG(dc, fmt.Sprintf("updated user %q", dc.user.Username))
 	return nil
 }
Index: /trunk/upstream.go
===================================================================
--- /trunk/upstream.go	(revision 567)
+++ /trunk/upstream.go	(revision 568)
@@ -1672,5 +1672,5 @@
 	uc.nickCM = uc.network.casemap(uc.nick)
 	uc.username = uc.network.GetUsername()
-	uc.realname = uc.network.GetRealname()
+	uc.realname = GetRealname(&uc.user.User, &uc.network.Network)
 
 	uc.SendMessage(&irc.Message{
Index: /trunk/user.go
===================================================================
--- /trunk/user.go	(revision 567)
+++ /trunk/user.go	(revision 568)
@@ -764,4 +764,10 @@
 	}
 
+	// If the realname is reset to the default, just wipe the per-network
+	// setting
+	if record.Realname == u.Realname {
+		record.Realname = ""
+	}
+
 	if err := u.checkNetwork(record); err != nil {
 		return nil, err
@@ -856,4 +862,28 @@
 }
 
+func (u *user) updateRealname(realname string) error {
+	u.User.Realname = realname
+	if err := u.srv.db.StoreUser(&u.User); err != nil {
+		return fmt.Errorf("failed to update user %q: %v", u.Username, err)
+	}
+
+	// Re-connect to networks which use the default realname
+	var needUpdate []Network
+	u.forEachNetwork(func(net *network) {
+		if net.Realname == "" {
+			needUpdate = append(needUpdate, net.Network)
+		}
+	})
+
+	var netErr error
+	for _, net := range needUpdate {
+		if _, err := u.updateNetwork(&net); err != nil {
+			netErr = err
+		}
+	}
+
+	return netErr
+}
+
 func (u *user) stop() {
 	u.events <- eventStop{}
