diff --git a/bindings/channels.go b/bindings/channels.go
index 98e746041e1c88ec29cbf84a2148eb5f46d58614..a38faa188128663cd8ac4972085482c725738b47 100644
--- a/bindings/channels.go
+++ b/bindings/channels.go
@@ -331,6 +331,15 @@ func LoadChannelsManager(cmixID int, storageTag string,
 	return channelManagerTrackerSingleton.make(m), nil
 }
 
+// ChannelGeneration contains information about a newly generated channel. It
+// contains the public channel info formatted in pretty print and the private
+// key for the channel in PEM format.
+//
+// Example JSON:
+//  {
+//    "Channel": "\u003cSpeakeasy-v1:My_Channel,description:Here is information about my channel.,level:Public,secrets:8AS3SczFvAYZftWuj4ZkOM9muFPIwq/0HuVCUJgTK8w=,GpPl1510/G07J4RfdYX9J5plTX3WNLVm+uuGmCwgFeU=,5,1,mRfdUGM6WxWjjCuLzO+2+zc3BQh2zMT2CHD8ZnBwpVI=\u003e",
+//    "PrivateKey": "-----BEGIN RSA PRIVATE KEY-----\nMDECAQACBgDMIU9LpQIDAQABAgYAvCd9ewECAw0tzQIDD305AgMFu4UCAwd+kQID\nAQxc\n-----END RSA PRIVATE KEY-----"
+//  }
 type ChannelGeneration struct {
 	Channel    string
 	PrivateKey string
@@ -341,24 +350,35 @@ type ChannelGeneration struct {
 //
 // It returns a pretty print of the channel and the private key.
 //
-// The name cannot be longer that __ characters. The description cannot be
-// longer than __ and can only use ______ characters.
-//
 // Parameters:
 //  - cmixID - The tracked cmix object ID. This can be retrieved using
 //    [Cmix.GetID].
-//  - name - The name of the new channel. The name cannot be longer than __
-//    characters and must contain only _____ characters. It cannot be changed
+//  - name - The name of the new channel. The name must be between 3 and 24
+//    characters inclusive. It can only include upper and lowercase unicode
+//    letters, digits 0 through 9, and underscores (_). It cannot be changed
 //    once a channel is created.
-//  - description - The description of a channel. The description cannot be
-//    longer than __ characters and must contain only _____ characters. It
-//    cannot be changed once a channel is created.
+//  - description - The description of a channel. The description is optional
+//    but cannot be longer than 144 characters and can include all unicode
+//    characters. It cannot be changed once a channel is created.
+//  - privacyLevel - The broadcast.PrivacyLevel of the channel. 0 = public,
+//    1 = private, and 2 = secret. Refer to the comment below for more
+//    information.
 //
 // Returns:
-//  - []byte - ChannelGeneration describes a generated channel. It contains both
-//    the public channel info and the private key for the channel in PEM format.
-//    fixme: document json
-func GenerateChannel(cmixID int, name, description string) ([]byte, error) {
+//  - []byte - [ChannelGeneration] describes a generated channel. It contains
+//    both the public channel info and the private key for the channel in PEM
+//    format.
+//
+// The [broadcast.PrivacyLevel] of a channel indicates the level of channel
+// information revealed when sharing it via URL. For any channel besides public
+// channels, the secret information is encrypted and a password is required to
+// share and join a channel.
+//  - A privacy level of [broadcast.Public] reveals all the information
+//    including the name, description, privacy level, public key and salt.
+//  - A privacy level of [broadcast.Private] reveals only the name and
+//    description.
+//  - A privacy level of [broadcast.Secret] reveals nothing.
+func GenerateChannel(cmixID int, name, description string, privacyLevel int) ([]byte, error) {
 	// Get cmix from singleton so its rng can be used
 	cmix, err := cmixTrackerSingleton.get(cmixID)
 	if err != nil {
@@ -367,8 +387,9 @@ func GenerateChannel(cmixID int, name, description string) ([]byte, error) {
 
 	stream := cmix.api.GetRng().GetStream()
 	defer stream.Close()
-	c, pk, err := cryptoBroadcast.NewChannel(
-		name, description, cmix.api.GetCmix().GetMaxMessageLength(), stream)
+	level := cryptoBroadcast.PrivacyLevel(privacyLevel)
+	c, pk, err := cryptoBroadcast.NewChannel(name, description, level,
+		cmix.api.GetCmix().GetMaxMessageLength(), stream)
 	if err != nil {
 		return nil, err
 	}
@@ -415,6 +436,14 @@ func makeChannelPrivateKeyStoreKey(channelID *id.ID) string {
 	return channelPrivateKeyStoreKey + "/" + channelID.String()
 }
 
+// ChannelInfo contains information about a channel.
+//
+// Example of ChannelInfo JSON:
+//  {
+//    "Name": "Test Channel",
+//    "Description": "This is a test channel",
+//    "ChannelID": "RRnpRhmvXtW9ugS1nILJ3WfttdctDvC2jeuH43E0g/0D",
+//  }
 type ChannelInfo struct {
 	Name        string
 	Description string
@@ -427,11 +456,10 @@ type ChannelInfo struct {
 //  - prettyPrint - The pretty print of the channel.
 //
 // The pretty print will be of the format:
-//  <XXChannel-v1:Test Channel,description:This is a test channel,secrets:pn0kIs6P1pHvAe7u8kUyf33GYVKmkoCX9LhCtvKJZQI=,3A5eB5pzSHyxN09w1kOVrTIEr5UyBbzmmd9Ga5Dx0XA=,0,0,/zChIlLr2p3Vsm2X4+3TiFapoapaTi8EJIisJSqwfGc=>
+//  <Speakeasy-v1:Test Channel,description:This is a test channel,secrets:YxHhRAKy2D4XU2oW5xnW/3yaqOeh8nO+ZSd3nUmiQ3c=,6pXN2H9FXcOj7pjJIZoq6nMi4tGX2s53fWH5ze2dU1g=,493,1,MVjkHlm0JuPxQNAn6WHsPdOw9M/BUF39p7XB/QEkQyc=>
 //
 // Returns:
-//  - []byte - ChannelInfo describes all relevant channel info.
-//    fixme: document json
+//  - []byte - JSON of [ChannelInfo], which describes all relevant channel info.
 func GetChannelInfo(prettyPrint string) ([]byte, error) {
 	_, bytes, err := getChannelInfo(prettyPrint)
 	return bytes, err
@@ -462,11 +490,10 @@ func getChannelInfo(prettyPrint string) (*cryptoBroadcast.Channel, []byte, error
 //    another user or generated via GenerateChannel.
 //
 // The pretty print will be of the format:
-//  <XXChannel-v1:Test Channel,description:This is a test channel,secrets:pn0kIs6P1pHvAe7u8kUyf33GYVKmkoCX9LhCtvKJZQI=,3A5eB5pzSHyxN09w1kOVrTIEr5UyBbzmmd9Ga5Dx0XA=,0,0,/zChIlLr2p3Vsm2X4+3TiFapoapaTi8EJIisJSqwfGc=>"
+//  <Speakeasy-v1:Test Channel,description:This is a test channel,secrets:YxHhRAKy2D4XU2oW5xnW/3yaqOeh8nO+ZSd3nUmiQ3c=,6pXN2H9FXcOj7pjJIZoq6nMi4tGX2s53fWH5ze2dU1g=,493,1,MVjkHlm0JuPxQNAn6WHsPdOw9M/BUF39p7XB/QEkQyc=>
 //
 // Returns:
-//  - []byte - ChannelInfo describes all relevant channel info.
-//    fixme: document json
+//  - []byte - JSON of [ChannelInfo], which describes all relevant channel info.
 func (cm *ChannelsManager) JoinChannel(channelPretty string) ([]byte, error) {
 	c, info, err := getChannelInfo(channelPretty)
 	if err != nil {
@@ -479,6 +506,40 @@ func (cm *ChannelsManager) JoinChannel(channelPretty string) ([]byte, error) {
 	return info, err
 }
 
+// JoinChannelFromURL joins the given channel from a URL. It will fail if the
+// channel has already been joined. A password is required unless it is of the
+// privacy level [broadcast.Public], in which case it can be left empty. To get
+// the privacy level of a channel URL, use [GetShareUrlType].
+//
+// Parameters:
+//  - url - The channel's share URL. Should be received from another user or
+//    generated via [GetShareURL].
+//  - password - The password needed to decrypt the secret data in the URL. Only
+//    required for private or secret channels.
+//
+// Returns:
+//  - []byte - [ChannelInfo] describes all relevant channel info.
+func (cm *ChannelsManager) JoinChannelFromURL(url, password string) ([]byte, error) {
+	c, err := cryptoBroadcast.DecodeShareURL(url, password)
+	if err != nil {
+		return nil, err
+	}
+
+	info, err := json.Marshal(&ChannelInfo{
+		Name:        c.Name,
+		Description: c.Description,
+		ChannelID:   c.ReceptionID.String(),
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	// Join the channel using the API
+	err = cm.api.JoinChannel(c)
+
+	return info, err
+}
+
 // GetChannels returns the IDs of all channels that have been joined.
 //
 // Returns:
@@ -527,6 +588,116 @@ func (cm *ChannelsManager) ReplayChannel(marshalledChanId []byte) error {
 	return cm.api.ReplayChannel(chanId)
 }
 
+////////////////////////////////////////////////////////////////////////////////
+// Channel Share URL                                                          //
+////////////////////////////////////////////////////////////////////////////////
+
+// ShareURL is returned from ChannelsManager.GetShareURL. It includes the
+// channel's share URL and password, if it needs one.
+//
+// JSON example for a public channel:
+//  {
+//    "url": "https://internet.speakeasy.tech/?0Name=My_Channel&Description=Here+is+information+about+my+channel.&2Level=Public&e=3CCvzK8diF%2B6vUZetyZkcyemoiI8uFLGSh%2B%2F9%2Bh5YQE%3D&k=zBakjn1Snay7AMr2CZ%2BCoWCHbe9TrtQqAVftIDi9Fjs%3D&l=5&m=0&p=1&s=Seyvx%2F5%2FOVTj5LClUG42AuLamDnqrbtMOoLymyIpFqY%3D&v=0",
+//    "password": ""
+//  }
+//
+// JSON example for a private channel:
+//  {
+//    "url": "https://internet.speakeasy.tech/?0Name=My_Channel&1Description=Here+is+information+about+my+channel.&d=i%2FwBAK6i89YT3LjPYb5%2BMmog5Gjk2unYzYt25y%2BmZH3%2Bo08oUIHHEoC7JYjk50Q2%2BMcSj6fQh%2BW3LBvWv02f1g60PLXZ1H8OS2rqoxBhwHvTpNgXRdUIErbk6q3ljIdjtSqJtWIzAx5no%2F96jaIBsob0U9jDE1jgsU8XNGxDz3TeKcdTOFiUpnh4R%2BALcys%3D&m=1&v=0",
+//    "password": "easter boaster musket catalyze unproven vendetta plated grinning"
+//  }
+//
+// JSON example for a secret channel:
+//  {
+//    "url": "https://internet.speakeasy.tech/?d=xRORDN8lt%2BI2SAn%2F21ZpOzj50J3HOV1GkMsPkhtgoYyQUqpPBZhhKpewzuDI%2B3wTQlpANLDMtFVL4J7y2lBpvIz9LQ5%2F6CoRdVkoXbG7uRqv6wscYdwWPYZBARC2cJSyeVad6RbxnoZ65Z0dtEVEff328ri3ZpaMBlP%2BpUH928pcVHibALW7Bw04Rkmh%2FWx6wJGw%2FU0gTHo02UlYFHh4G9CC%2BIU1x13BmEuW6Hyk6Ty9BlHt29QbsQ7uU30RwzQOyg8%3D&m=2&v=0",
+//    "password": "florist angled valid snarl discharge endearing harbor hazy"
+//  }
+type ShareURL struct {
+	URL      string `json:"url"`
+	Password string `json:"password"`
+}
+
+// GetShareURL generates a URL that can be used to share this channel with
+// others on the given host.
+//
+// A URL comes in one of three forms based on the privacy level set when
+// generating the channel. Each privacy level hides more information than the
+// last with the lowest level revealing everything and the highest level
+// revealing nothing. For any level above the lowest, a password is returned,
+// which will be required when decoding the URL.
+//
+// The maxUses is the maximum number of times this URL can be used to join a
+// channel. If it is set to 0, then it can be shared unlimited times. The max
+// uses is set as a URL parameter using the key [broadcast.MaxUsesKey]. Note
+// that this number is also encoded in the secret data for private and secret
+// URLs, so if the number is changed in the URL, is will be verified when
+// calling [ChannelsManager.JoinChannelFromURL]. There is no enforcement for
+// public URLs.
+//
+// Parameters:
+//  - cmixID - The tracked Cmix object ID.
+//  - host - The URL to append the channel info to.
+//  - maxUses - The maximum number of uses the link can be used (0 for
+//    unlimited).
+//  - marshalledChanId - A marshalled channel ID ([id.ID]).
+//
+// Returns:
+//  - JSON of ShareURL.
+func (cm *ChannelsManager) GetShareURL(cmixID int, host string, maxUses int,
+	marshalledChanId []byte) ([]byte, error) {
+
+	// Unmarshal channel ID
+	chanId, err := id.Unmarshal(marshalledChanId)
+	if err != nil {
+		return nil, err
+	}
+
+	// Get the channel from the ID
+	ch, err := cm.api.GetChannel(chanId)
+	if err != nil {
+		return nil, err
+	}
+
+	// Get user from singleton
+	user, err := cmixTrackerSingleton.get(cmixID)
+	if err != nil {
+		return nil, err
+	}
+
+	// Generate share URL and password
+	rng := user.api.GetRng().GetStream()
+	url, password, err := ch.ShareURL(host, maxUses, rng)
+	rng.Close()
+	if err != nil {
+		return nil, err
+	}
+
+	su := ShareURL{
+		URL:      url,
+		Password: password,
+	}
+
+	return json.Marshal(su)
+}
+
+// GetShareUrlType determines the [broadcast.PrivacyLevel] of the channel URL.
+// If the URL is an invalid channel URL, an error is returned.
+//
+// Parameters:
+//  - url - The channel share URL.
+//
+// Returns:
+//  - An int that corresponds to the [broadcast.PrivacyLevel] as outlined below.
+//
+// Possible returns:
+//  0 = public channel
+//  1 = private channel
+//  2 = secret channel
+func GetShareUrlType(url string) (int, error) {
+	level, err := cryptoBroadcast.GetShareUrlType(url)
+	return int(level), err
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // Channel Sending Methods & Reports                                          //
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/broadcast/rsaToPublic_test.go b/broadcast/rsaToPublic_test.go
index 159f9d1016ead09866c8db1bd3f15faedfc94404..9be17453b96db68ca8a735fdcd9234ccd7ad2e3e 100644
--- a/broadcast/rsaToPublic_test.go
+++ b/broadcast/rsaToPublic_test.go
@@ -59,7 +59,7 @@ func Test_asymmetricClient_Smoke(t *testing.T) {
 	packetPayloadLength := newMockCmix(cMixHandler).GetMaxMessageLength()
 
 	channel, pk, _ := crypto.NewChannel(
-		cName, cDesc, packetPayloadLength, rngGen.GetStream())
+		cName, cDesc, crypto.Public, packetPayloadLength, rngGen.GetStream())
 	cid := channel.ReceptionID
 
 	// Must mutate cMixHandler such that it's processorMap contains a
diff --git a/channels/joinedChannel_test.go b/channels/joinedChannel_test.go
index e3bee24de606711cc0a0cffcd24e6a6c9b42001d..f020561e040861e540bac72810a4849087d65a0e 100644
--- a/channels/joinedChannel_test.go
+++ b/channels/joinedChannel_test.go
@@ -51,8 +51,9 @@ func Test_manager_store(t *testing.T) {
 	m := mFace.(*manager)
 
 	for i := 0; i < 10; i++ {
-		ch, _, err := newTestChannel("name_"+strconv.Itoa(i),
-			"description_"+strconv.Itoa(i), m.rng.GetStream())
+		ch, _, err := newTestChannel(
+			"name_"+strconv.Itoa(i), "description_"+strconv.Itoa(i),
+			m.rng.GetStream(), cryptoBroadcast.Public)
 		if err != nil {
 			t.Errorf("Failed to create new channel %d: %+v", i, err)
 		}
@@ -99,8 +100,8 @@ func Test_manager_loadChannels(t *testing.T) {
 	expected := make([]*joinedChannel, 10)
 
 	for i := range expected {
-		ch, _, err := newTestChannel("name_"+strconv.Itoa(i),
-			"description_"+strconv.Itoa(i), m.rng.GetStream())
+		ch, _, err := newTestChannel(
+			"name_"+strconv.Itoa(i), "description_"+strconv.Itoa(i), m.rng.GetStream(), cryptoBroadcast.Public)
 		if err != nil {
 			t.Errorf("Failed to create new channel %d: %+v", i, err)
 		}
@@ -168,7 +169,8 @@ func Test_manager_addChannel(t *testing.T) {
 
 	m := mFace.(*manager)
 
-	ch, _, err := newTestChannel("name", "description", m.rng.GetStream())
+	ch, _, err := newTestChannel(
+		"name", "description", m.rng.GetStream(), cryptoBroadcast.Public)
 	if err != nil {
 		t.Errorf("Failed to create new channel: %+v", err)
 	}
@@ -213,7 +215,8 @@ func Test_manager_addChannel_ChannelAlreadyExistsErr(t *testing.T) {
 
 	m := mFace.(*manager)
 
-	ch, _, err := newTestChannel("name", "description", m.rng.GetStream())
+	ch, _, err := newTestChannel(
+		"name", "description", m.rng.GetStream(), cryptoBroadcast.Public)
 	if err != nil {
 		t.Errorf("Failed to create new channel: %+v", err)
 	}
@@ -249,7 +252,8 @@ func Test_manager_removeChannel(t *testing.T) {
 
 	m := mFace.(*manager)
 
-	ch, _, err := newTestChannel("name", "description", m.rng.GetStream())
+	ch, _, err := newTestChannel(
+		"name", "description", m.rng.GetStream(), cryptoBroadcast.Public)
 	if err != nil {
 		t.Errorf("Failed to create new channel: %+v", err)
 	}
@@ -294,7 +298,8 @@ func Test_manager_removeChannel_ChannelDoesNotExistsErr(t *testing.T) {
 
 	m := mFace.(*manager)
 
-	ch, _, err := newTestChannel("name", "description", m.rng.GetStream())
+	ch, _, err := newTestChannel(
+		"name", "description", m.rng.GetStream(), cryptoBroadcast.Public)
 	if err != nil {
 		t.Errorf("Failed to create new channel: %+v", err)
 	}
@@ -326,7 +331,8 @@ func Test_manager_getChannel(t *testing.T) {
 
 	m := mFace.(*manager)
 
-	ch, _, err := newTestChannel("name", "description", m.rng.GetStream())
+	ch, _, err := newTestChannel(
+		"name", "description", m.rng.GetStream(), cryptoBroadcast.Public)
 	if err != nil {
 		t.Errorf("Failed to create new channel: %+v", err)
 	}
@@ -367,7 +373,8 @@ func Test_manager_getChannel_ChannelDoesNotExistsErr(t *testing.T) {
 
 	m := mFace.(*manager)
 
-	ch, _, err := newTestChannel("name", "description", m.rng.GetStream())
+	ch, _, err := newTestChannel(
+		"name", "description", m.rng.GetStream(), cryptoBroadcast.Public)
 	if err != nil {
 		t.Errorf("Failed to create new channel: %+v", err)
 	}
@@ -403,8 +410,8 @@ func Test_manager_getChannels(t *testing.T) {
 	expected := make([]*id.ID, 10)
 
 	for i := range expected {
-		ch, _, err := newTestChannel("name_"+strconv.Itoa(i),
-			"description_"+strconv.Itoa(i), m.rng.GetStream())
+		ch, _, err := newTestChannel(
+			"name_"+strconv.Itoa(i), "description_"+strconv.Itoa(i), m.rng.GetStream(), cryptoBroadcast.Public)
 		if err != nil {
 			t.Errorf("Failed to create new channel %d: %+v", i, err)
 		}
@@ -436,7 +443,8 @@ func Test_manager_getChannels(t *testing.T) {
 func Test_joinedChannel_Store(t *testing.T) {
 	kv := versioned.NewKV(ekv.MakeMemstore())
 	rng := fastRNG.NewStreamGenerator(1, 1, csprng.NewSystemRNG)
-	ch, _, err := newTestChannel("name", "description", rng.GetStream())
+	ch, _, err := newTestChannel(
+		"name", "description", rng.GetStream(), cryptoBroadcast.Public)
 	if err != nil {
 		t.Errorf("Failed to create new channel: %+v", err)
 	}
@@ -479,7 +487,8 @@ func Test_loadJoinedChannel(t *testing.T) {
 
 	m := mFace.(*manager)
 
-	ch, _, err := newTestChannel("name", "description", m.rng.GetStream())
+	ch, _, err := newTestChannel(
+		"name", "description", m.rng.GetStream(), cryptoBroadcast.Public)
 	if err != nil {
 		t.Errorf("Failed to create new channel: %+v", err)
 	}
@@ -508,7 +517,8 @@ func Test_loadJoinedChannel(t *testing.T) {
 func Test_joinedChannel_delete(t *testing.T) {
 	kv := versioned.NewKV(ekv.MakeMemstore())
 	rng := fastRNG.NewStreamGenerator(1, 1, csprng.NewSystemRNG)
-	ch, _, err := newTestChannel("name", "description", rng.GetStream())
+	ch, _, err := newTestChannel(
+		"name", "description", rng.GetStream(), cryptoBroadcast.Public)
 	if err != nil {
 		t.Errorf("Failed to create new channel: %+v", err)
 	}
@@ -565,9 +575,11 @@ func Test_makeJoinedChannelKey_Consistency(t *testing.T) {
 // newTestChannel creates a new cryptoBroadcast.Channel in the same way that
 // cryptoBroadcast.NewChannel does but with a smaller RSA key and salt to make
 // tests run quicker.
-func newTestChannel(name, description string, rng csprng.Source) (
+func newTestChannel(name, description string, rng csprng.Source,
+	level cryptoBroadcast.PrivacyLevel) (
 	*cryptoBroadcast.Channel, rsa.PrivateKey, error) {
-	c, pk, err := cryptoBroadcast.NewChannelVariableKeyUnsafe(name, description, 1000, 512, rng)
+	c, pk, err := cryptoBroadcast.NewChannelVariableKeyUnsafe(
+		name, description, level, 1000, 512, rng)
 	return c, pk, err
 }
 
diff --git a/channels/manager_test.go b/channels/manager_test.go
index 6f07e2013d222e10d8c93bce7beeabbfd59ef8b1..e4213bd7c369143fec41c60895150bf79e0ea3a6 100644
--- a/channels/manager_test.go
+++ b/channels/manager_test.go
@@ -11,6 +11,7 @@ import (
 	"fmt"
 	"gitlab.com/elixxir/client/broadcast"
 	"gitlab.com/elixxir/client/storage/versioned"
+	broadcast2 "gitlab.com/elixxir/crypto/broadcast"
 	cryptoChannel "gitlab.com/elixxir/crypto/channel"
 	"gitlab.com/elixxir/crypto/fastRNG"
 	"gitlab.com/elixxir/ekv"
@@ -51,7 +52,8 @@ func TestManager_JoinChannel(t *testing.T) {
 	m := mFace.(*manager)
 	mem := m.events.model.(*mockEventModel)
 
-	ch, _, err := newTestChannel("name", "description", m.rng.GetStream())
+	ch, _, err := newTestChannel(
+		"name", "description", m.rng.GetStream(), broadcast2.Public)
 	if err != nil {
 		t.Errorf("Failed to create new channel: %+v", err)
 	}
@@ -94,7 +96,8 @@ func TestManager_LeaveChannel(t *testing.T) {
 	m := mFace.(*manager)
 	mem := m.events.model.(*mockEventModel)
 
-	ch, _, err := newTestChannel("name", "description", m.rng.GetStream())
+	ch, _, err := newTestChannel(
+		"name", "description", m.rng.GetStream(), broadcast2.Public)
 	if err != nil {
 		t.Errorf("Failed to create new channel: %+v", err)
 	}
@@ -135,9 +138,9 @@ func TestManager_GetChannels(t *testing.T) {
 	chList := make(map[id.ID]interface{})
 
 	for i := 0; i < 10; i++ {
-		name := fmt.Sprintf("testChannel %d", numtests)
+		name := fmt.Sprintf("testChannel_%d", numtests)
 		s := rng.GetStream()
-		tc, _, err := newTestChannel(name, "blarg", s)
+		tc, _, err := newTestChannel(name, "blarg", s, broadcast2.Public)
 		s.Close()
 		if err != nil {
 			t.Fatalf("failed to generate channel %s", name)
@@ -172,9 +175,9 @@ func TestManager_GetChannel(t *testing.T) {
 	chList := make([]*id.ID, 0, numtests)
 
 	for i := 0; i < 10; i++ {
-		name := fmt.Sprintf("testChannel %d", numtests)
+		name := fmt.Sprintf("testChannel_%d", numtests)
 		s := rng.GetStream()
-		tc, _, err := newTestChannel(name, "blarg", s)
+		tc, _, err := newTestChannel(name, "blarg", s, broadcast2.Public)
 		s.Close()
 		if err != nil {
 			t.Fatalf("failed to generate channel %s", name)
diff --git a/channels/send_test.go b/channels/send_test.go
index c3c8114c85d69193216c04bf903991f69129917e..6886519a42dbd95431dcb927ada369c46bb7c94a 100644
--- a/channels/send_test.go
+++ b/channels/send_test.go
@@ -272,8 +272,8 @@ func TestAdminGeneric(t *testing.T) {
 	validUntil := time.Hour
 
 	rng := &csprng.SystemRNG{}
-	ch, priv, err := cryptoBroadcast.NewChannel("test", "test",
-		1000, rng)
+	ch, priv, err := cryptoBroadcast.NewChannel(
+		"test", "test", cryptoBroadcast.Public, 1000, rng)
 	if err != nil {
 		t.Fatalf("Failed to generate channel: %+v", err)
 	}
diff --git a/cmd/broadcast.go b/cmd/broadcast.go
index 7b0e7c9183ae1311bf7f2478640309723ca824e4..df52c82f13ae29ae780670a9a502d68be86e2142 100644
--- a/cmd/broadcast.go
+++ b/cmd/broadcast.go
@@ -81,7 +81,8 @@ var broadcastCmd = &cobra.Command{
 
 			if viper.GetBool(broadcastNewFlag) {
 				// Create a new broadcast channel
-				channel, pk, err = crypto.NewChannel(name, desc, user.GetCmix().GetMaxMessageLength(), user.GetRng().GetStream())
+				channel, pk, err = crypto.NewChannel(name, desc, crypto.Public,
+					user.GetCmix().GetMaxMessageLength(), user.GetRng().GetStream())
 				if err != nil {
 					jww.FATAL.Panicf("Failed to create new channel: %+v", err)
 				}
diff --git a/go.mod b/go.mod
index cf73253e0921d1501752e9ce93529b4d314d1710..2d20fdda7f3c19fa52b1a216fb741aee99a11ee1 100644
--- a/go.mod
+++ b/go.mod
@@ -15,7 +15,7 @@ require (
 	github.com/stretchr/testify v1.8.0
 	gitlab.com/elixxir/bloomfilter v0.0.0-20211222005329-7d931ceead6f
 	gitlab.com/elixxir/comms v0.0.4-0.20221011183106-8c4450ba3cfb
-	gitlab.com/elixxir/crypto v0.0.7-0.20221015023742-e1108df47b6b
+	gitlab.com/elixxir/crypto v0.0.7-0.20221014162002-64ef7c013cc9
 	gitlab.com/elixxir/ekv v0.2.1
 	gitlab.com/elixxir/primitives v0.0.3-0.20220901220638-1acc75fabdc6
 	gitlab.com/xx_network/comms v0.0.4-0.20221005205845-b34d538ffd85
@@ -50,6 +50,7 @@ require (
 	github.com/pelletier/go-toml/v2 v2.0.2 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/rs/cors v1.8.2 // indirect
+	github.com/sethvargo/go-diceware v0.3.0 // indirect
 	github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
 	github.com/soheilhy/cmux v0.1.5 // indirect
 	github.com/spf13/afero v1.9.2 // indirect
diff --git a/go.sum b/go.sum
index a7a1300530c0d37858b4f22a65cc05b9ac20ef22..9c400a6eed19a70c3b8cb50e12ee6d05adb954d4 100644
--- a/go.sum
+++ b/go.sum
@@ -547,6 +547,8 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb
 github.com/sagikazarmark/crypt v0.6.0/go.mod h1:U8+INwJo3nBv1m6A/8OBXAq7Jnpspk5AxSgDyEQcea8=
 github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
 github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
+github.com/sethvargo/go-diceware v0.3.0 h1:UVVEfmN/uF50JfWAN7nbY6CiAlp5xeSx+5U0lWKkMCQ=
+github.com/sethvargo/go-diceware v0.3.0/go.mod h1:lH5Q/oSPMivseNdhMERAC7Ti5oOPqsaVddU1BcN1CY0=
 github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
 github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
@@ -634,10 +636,8 @@ gitlab.com/elixxir/comms v0.0.4-0.20221011183106-8c4450ba3cfb/go.mod h1:oRteMH+R
 gitlab.com/elixxir/crypto v0.0.0-20200804182833-984246dea2c4/go.mod h1:ucm9SFKJo+K0N2GwRRpaNr+tKXMIOVWzmyUD0SbOu2c=
 gitlab.com/elixxir/crypto v0.0.3/go.mod h1:ZNgBOblhYToR4m8tj4cMvJ9UsJAUKq+p0gCp07WQmhA=
 gitlab.com/elixxir/crypto v0.0.7-0.20220913220142-ab0771bad0af/go.mod h1:QF8SzsrYh9Elip9EUYUDAhPjqO9DGrrrQxYHvn+VXok=
-gitlab.com/elixxir/crypto v0.0.7-0.20221014161526-ce04e14b9f80 h1:M9imzr2ZrD0GXsR+nfrzLnJ8Y9axj2Pvq8CTDE+IQb4=
-gitlab.com/elixxir/crypto v0.0.7-0.20221014161526-ce04e14b9f80/go.mod h1:QF8SzsrYh9Elip9EUYUDAhPjqO9DGrrrQxYHvn+VXok=
-gitlab.com/elixxir/crypto v0.0.7-0.20221015023742-e1108df47b6b h1:gWB95fteva1kMYR/qzdkxMMAs771wIvYd97D9nPy/xI=
-gitlab.com/elixxir/crypto v0.0.7-0.20221015023742-e1108df47b6b/go.mod h1:QF8SzsrYh9Elip9EUYUDAhPjqO9DGrrrQxYHvn+VXok=
+gitlab.com/elixxir/crypto v0.0.7-0.20221014162002-64ef7c013cc9 h1:T5zUNYXFtV9Cr/P5fUx1B4lkRi+c2qL0Esgp1/Zpd58=
+gitlab.com/elixxir/crypto v0.0.7-0.20221014162002-64ef7c013cc9/go.mod h1:IU//ZTbPrqnaUrPKP9l9cD29c+cJPo3E1yrlbcBaf2w=
 gitlab.com/elixxir/ekv v0.2.1 h1:dtwbt6KmAXG2Tik5d60iDz2fLhoFBgWwST03p7T+9Is=
 gitlab.com/elixxir/ekv v0.2.1/go.mod h1:USLD7xeDnuZEavygdrgzNEwZXeLQJK/w1a+htpN+JEU=
 gitlab.com/elixxir/primitives v0.0.0-20200731184040-494269b53b4d/go.mod h1:OQgUZq7SjnE0b+8+iIAT2eqQF+2IFHn73tOo+aV11mg=