diff --git a/node/node.go b/node/node.go
index 7805fd52aadd67f15e25ad41cefee48c2614fc0b..44f48d71721087bf71c33fdcfc5fa72038abf2d7 100644
--- a/node/node.go
+++ b/node/node.go
@@ -48,6 +48,26 @@ type Node struct {
 // New returns a new Node with a controlling Context, scoped to an etcd
 // Session.
 func New(parentCtx context.Context, session *concurrency.Session, ice Icecast, nodeID string, publicAddrs []net.IP, peerAddr net.IP, gossipPort int, lbSpec string, maxBandwidth, maxListeners int) (*Node, error) {
+	// Creating a Node starts a number of background goroutines and etcd
+	// watchers. Here's how those parts interact:
+	//
+	// The main task of the Node is to manage the Icecast daemon by
+	// ensuring its configuration is in sync with the contents of our
+	// configuration (in etcd). The Node controls Icecast by generating
+	// its configuration and sending it a SIGHUP to reload it.
+	//
+	// Changes in the Icecast configuration originate from two sources:
+	// changes in the mount configuration, or state transitions of the
+	// Icecast leader. So we listen on both the configuration and the
+	// election notification channels, and use a third channel to send the
+	// new configuration to Icecast: in both cases, the other half of the
+	// configuration is cached and looked up asynchronously.
+	//
+	// The second part of the Node functionality has to do with
+	// maintaining a view of the state of all nodes so that we can use it
+	// for load balancing decisions. This is delegated to the loadBalancer
+	// type.
+
 	// Create a sub-Context that can be canceled when Stop is called. This
 	// Context controls the lifetime of the Node itself, and all the
 	// associated background goroutines: canceling it immediately