diff --git a/garnet/lib/rust/fuchsia_uri/src/pkg_uri.rs b/garnet/lib/rust/fuchsia_uri/src/pkg_uri.rs
index e98bcf00fe327d2ba5b3c06aaad29d9c4415d0f1..49183a28584cf72925b92f7a8651458cd103df71 100644
--- a/garnet/lib/rust/fuchsia_uri/src/pkg_uri.rs
+++ b/garnet/lib/rust/fuchsia_uri/src/pkg_uri.rs
@@ -5,12 +5,11 @@
 pub use crate::errors::ParseError;
 pub use crate::parse::{check_resource, HASH_RE, NAME_RE};
 use serde::{Deserialize, Deserializer, Serialize, Serializer};
-use serde_derive::Serialize;
 use std::convert::TryFrom;
 use std::fmt;
 use std::str;
 use url::percent_encoding::percent_decode;
-use url::Url;
+use url::{Host, Url};
 
 /// Decoded representation of a fuchsia-pkg URI.
 ///
@@ -34,7 +33,7 @@ use url::Url;
 /// - fuchsia-pkg://example.com/some-package/some-variant/<some-hash>#path/to/resource (obsolete)
 #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
 pub struct PkgUri {
-    host: String,
+    repo: RepoUri,
     path: String,
     hash: Option<String>,
     resource: Option<String>,
@@ -94,11 +93,11 @@ impl PkgUri {
             None
         };
 
-        Ok(PkgUri { host, path, hash, resource })
+        Ok(PkgUri { repo: RepoUri { host }, path, hash, resource })
     }
 
     pub fn host(&self) -> &str {
-        &self.host
+        &self.repo.host()
     }
 
     pub fn name(&self) -> Option<&str> {
@@ -130,10 +129,15 @@ impl PkgUri {
         self.path == "/" && self.hash.is_none() && self.resource.is_none()
     }
 
+    /// Returns the [RepoUri] that corresponds to this package URI.
+    pub fn repo(&self) -> &RepoUri {
+        &self.repo
+    }
+
     /// Produce a new [PkgUri] with any resource fragment stripped off.
     pub fn root_uri(&self) -> PkgUri {
         PkgUri {
-            host: self.host.clone(),
+            repo: self.repo.clone(),
             path: self.path.clone(),
             hash: self.hash.clone(),
             resource: None,
@@ -141,11 +145,7 @@ impl PkgUri {
     }
 
     pub fn new_repository(host: String) -> Result<PkgUri, ParseError> {
-        if host.is_empty() {
-            return Err(ParseError::InvalidHost);
-        }
-
-        Ok(PkgUri { host: host.to_string(), path: "/".to_string(), hash: None, resource: None })
+        Ok(PkgUri { repo: RepoUri::new(host)?, path: "/".to_string(), hash: None, resource: None })
     }
 
     pub fn new_package(
@@ -153,7 +153,7 @@ impl PkgUri {
         path: String,
         hash: Option<String>,
     ) -> Result<PkgUri, ParseError> {
-        let mut uri = PkgUri::new_repository(host)?;
+        let repo = RepoUri::new(host)?;
 
         let (name, variant) = parse_path(path.as_str())?;
 
@@ -169,9 +169,8 @@ impl PkgUri {
                 return Err(ParseError::InvalidHash);
             }
         }
-        uri.path = path;
-        uri.hash = hash;
-        Ok(uri)
+
+        Ok(PkgUri { repo, path, hash, resource: None })
     }
 
     pub fn new_resource(
@@ -191,7 +190,7 @@ impl PkgUri {
 
 impl fmt::Display for PkgUri {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "fuchsia-pkg://{}", self.host)?;
+        write!(f, "{}", self.repo)?;
         if self.path != "/" {
             write!(f, "{}", self.path)?;
         }
@@ -246,15 +245,20 @@ impl<'de> Deserialize<'de> for PkgUri {
     }
 }
 
-#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
-#[serde(transparent)]
+#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
 pub struct RepoUri {
-    uri: PkgUri,
+    host: String,
 }
 
 impl RepoUri {
     pub fn new(host: String) -> Result<Self, ParseError> {
-        Ok(RepoUri { uri: PkgUri::new_repository(host)? })
+        if host.is_empty() {
+            return Err(ParseError::InvalidHost);
+        }
+
+        Host::parse(&host)?;
+
+        Ok(RepoUri { host })
     }
 
     pub fn parse(input: &str) -> Result<Self, ParseError> {
@@ -263,7 +267,7 @@ impl RepoUri {
     }
 
     pub fn host(&self) -> &str {
-        self.uri.host()
+        &self.host
     }
 }
 
@@ -288,7 +292,7 @@ impl TryFrom<PkgUri> for RepoUri {
 
     fn try_from(uri: PkgUri) -> Result<Self, Self::Error> {
         if uri.is_repository() {
-            Ok(RepoUri { uri })
+            Ok(uri.repo)
         } else {
             Err(ParseError::InvalidRepository)
         }
@@ -297,13 +301,19 @@ impl TryFrom<PkgUri> for RepoUri {
 
 impl From<RepoUri> for PkgUri {
     fn from(uri: RepoUri) -> Self {
-        uri.uri
+        PkgUri { repo: uri, path: "/".to_string(), hash: None, resource: None }
     }
 }
 
 impl fmt::Display for RepoUri {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        self.uri.fmt(f)
+        write!(f, "fuchsia-pkg://{}", self.host)
+    }
+}
+
+impl Serialize for RepoUri {
+    fn serialize<S: Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
+        self.to_string().serialize(ser)
     }
 }
 
@@ -399,7 +409,9 @@ mod tests {
                         assert_eq!(
                             uri,
                             Ok(PkgUri {
-                                host: $pkg_host.to_string(),
+                                repo: RepoUri {
+                                    host: $pkg_host.to_string(),
+                                },
                                 path: $pkg_path.to_string(),
                                 hash: $pkg_hash.map(|s: &str| s.to_string()),
                                 resource: $pkg_resource.map(|s: &str| s.to_string()),