diff --git a/garnet/go/src/amber/BUILD.gn b/garnet/go/src/amber/BUILD.gn
index 916a5f20aa7b785bd532b4bb42d0297d65b47689..bcce302f8a687c27c8fdeb644873bb2b81c650e8 100644
--- a/garnet/go/src/amber/BUILD.gn
+++ b/garnet/go/src/amber/BUILD.gn
@@ -36,6 +36,7 @@ go_library("lib") {
     "//garnet/public/lib/syslog/go/src/syslog",
     "//sdk/fidl/fuchsia.amber($go_toolchain)",
     "//sdk/fidl/fuchsia.pkg($go_toolchain)",
+    "//sdk/fidl/fuchsia.pkg.rewrite($go_toolchain)",
     "//sdk/fidl/fuchsia.sys($go_toolchain)",
     "//zircon/public/fidl/fuchsia-cobalt($go_toolchain)",
     "//zircon/public/fidl/fuchsia-mem($go_toolchain)",
diff --git a/garnet/go/src/amber/amberctl/amberctl.go b/garnet/go/src/amber/amberctl/amberctl.go
index 7b301e927f309bb825518713af426d1e1300cc7b..13981fd742caca3e3597e0c5bb3471559e66e094 100644
--- a/garnet/go/src/amber/amberctl/amberctl.go
+++ b/garnet/go/src/amber/amberctl/amberctl.go
@@ -28,6 +28,7 @@ import (
 	"fidl/fuchsia/amber"
 	fuchsiaio "fidl/fuchsia/io"
 	"fidl/fuchsia/pkg"
+	"fidl/fuchsia/pkg/rewrite"
 )
 
 const usage = `usage: %s <command> [opts]
@@ -101,27 +102,185 @@ func doTest(pxy *amber.ControlInterface) error {
 	return nil
 }
 
-func connectToAmber(ctx *context.Context) (*amber.ControlInterface, amber.ControlInterfaceRequest) {
+type Services struct {
+	amber         *amber.ControlInterface
+	resolver      *pkg.PackageResolverInterface
+	repoMgr       *pkg.RepositoryManagerInterface
+	rewriteEngine *rewrite.EngineInterface
+}
+
+func connectToAmber(ctx *context.Context) *amber.ControlInterface {
 	req, pxy, err := amber.NewControlInterfaceRequest()
 	if err != nil {
 		panic(err)
 	}
 	ctx.ConnectToEnvService(req)
-	return pxy, req
+	return pxy
 }
 
-func connectToPackageResolver(ctx *context.Context) (*pkg.PackageResolverInterface, pkg.PackageResolverInterfaceRequest) {
+func connectToPackageResolver(ctx *context.Context) *pkg.PackageResolverInterface {
 	req, pxy, err := pkg.NewPackageResolverInterfaceRequest()
 	if err != nil {
 		panic(err)
 	}
 	ctx.ConnectToEnvService(req)
-	return pxy, req
+	return pxy
 }
 
-func addSource(a *amber.ControlInterface) error {
-	var cfg amber.SourceConfig
+func connectToRepositoryManager(ctx *context.Context) *pkg.RepositoryManagerInterface {
+	req, pxy, err := pkg.NewRepositoryManagerInterfaceRequest()
+	if err != nil {
+		panic(err)
+	}
+	ctx.ConnectToEnvService(req)
+	return pxy
+}
+
+func connectToRewriteEngine(ctx *context.Context) *rewrite.EngineInterface {
+	req, pxy, err := rewrite.NewEngineInterfaceRequest()
+	if err != nil {
+		panic(err)
+	}
+	ctx.ConnectToEnvService(req)
+	return pxy
+}
+
+// upgradeSourceConfig attempts to upgrade an amber.SourceConfig into a pkg.RepositoryConfig
+//
+// The two config formats are incompatible in various ways:
+//
+// * repo configs cannot be disabled. amberctl will attempt to preserve a config's disabled bit by
+// not configuring a rewrite rule for the source.
+//
+// * repo configs do not support oauth, network client config options, or polling frequency
+// overrides. If present, these options are discarded.
+//
+// * repo config mirrors do not accept different URLs for the TUF repo and the blobs. Any custom
+// blob URL is discarded.
+func upgradeSourceConfig(cfg amber.SourceConfig) pkg.RepositoryConfig {
+	repoCfg := pkg.RepositoryConfig{
+		RepoUrl:        repoUrlForId(cfg.Id),
+		RepoUrlPresent: true,
+	}
+
+	mirror := pkg.MirrorConfig{
+		MirrorUrl:        cfg.RepoUrl,
+		MirrorUrlPresent: true,
+		Subscribe:        cfg.Auto,
+		SubscribePresent: true,
+	}
+	if cfg.BlobKey != nil {
+		var blobKey pkg.RepositoryBlobKey
+		blobKey.SetAesKey(cfg.BlobKey.Data[:])
+		mirror.SetBlobKey(blobKey)
+	}
+	repoCfg.SetMirrors([]pkg.MirrorConfig{mirror})
+
+	for _, key := range cfg.RootKeys {
+		if key.Type != "ed25519" {
+			continue
+		}
+
+		var rootKey pkg.RepositoryKeyConfig
+		bytes, err := hex.DecodeString(key.Value)
+		if err != nil {
+			continue
+		}
+		rootKey.SetEd25519Key(bytes)
+
+		repoCfg.RootKeys = append(repoCfg.RootKeys, rootKey)
+		repoCfg.RootKeysPresent = true
+	}
+
+	return repoCfg
+}
+
+func repoUrlForId(id string) string {
+	return fmt.Sprintf("fuchsia-pkg://%s", id)
+}
+
+func rewriteRuleForId(id string) rewrite.Rule {
+	var rule rewrite.Rule
+	rule.SetLiteral(rewrite.LiteralRule{
+		HostMatch:             "fuchsia.com",
+		HostReplacement:       id,
+		PathPrefixMatch:       "/",
+		PathPrefixReplacement: "/",
+	})
+	return rule
+}
+
+func replaceDynamicRewriteRules(rewriteEngine *rewrite.EngineInterface, rule rewrite.Rule) error {
+	return doRewriteRuleEditTransaction(rewriteEngine, func(transaction *rewrite.EditTransactionInterface) error {
+		if err := transaction.ResetAll(); err != nil {
+			return fmt.Errorf("fuchsia.pkg.rewrite.EditTransaction.ResetAll IPC encountered an error: %s", err)
+		}
+
+		s, err := transaction.Add(rule)
+		if err != nil {
+			return fmt.Errorf("fuchsia.pkg.rewrite.EditTransaction.Add IPC encountered an error: %s", err)
+		}
+		status := zx.Status(s)
+		if status != zx.ErrOk {
+			return fmt.Errorf("unable to add rewrite rule: %s", status)
+		}
+
+		return nil
+	})
+}
+
+func removeAllDynamicRewriteRules(rewriteEngine *rewrite.EngineInterface) error {
+	return doRewriteRuleEditTransaction(rewriteEngine, func(transaction *rewrite.EditTransactionInterface) error {
+		if err := transaction.ResetAll(); err != nil {
+			return fmt.Errorf("fuchsia.pkg.rewrite.EditTransaction.ResetAll IPC encountered an error: %s", err)
+		}
+
+		return nil
+	})
+}
 
+// doRewriteRuleEditTransaction executes a rewrite rule edit transaction using
+// the provided callback, retrying on data races a few times before giving up.
+func doRewriteRuleEditTransaction(rewriteEngine *rewrite.EngineInterface, cb func(*rewrite.EditTransactionInterface) error) error {
+	for i := 0; i < 10; i++ {
+		err, status := func() (error, zx.Status) {
+			var status zx.Status
+			req, transaction, err := rewrite.NewEditTransactionInterfaceRequest()
+			if err != nil {
+				return fmt.Errorf("creating edit transaction: %s", err), status
+			}
+			defer transaction.Close()
+			if err := rewriteEngine.StartEditTransaction(req); err != nil {
+				return fmt.Errorf("fuchsia.pkg.rewrite.Engine IPC encountered an error: %s", err), status
+			}
+
+			if err := cb(transaction); err != nil {
+				return err, status
+			}
+
+			s, err := transaction.Commit()
+			if err != nil {
+				return fmt.Errorf("fuchsia.pkg.rewrite.EditTransaction.Commit IPC encountered an error: %s", err), status
+			}
+			return nil, zx.Status(s)
+		}()
+		if err != nil {
+			return err
+		}
+		switch status {
+		case zx.ErrOk:
+			return nil
+		case zx.ErrUnavailable:
+			continue
+		default:
+			return fmt.Errorf("unexpected error while committing rewrite rule transaction: %s", status)
+		}
+	}
+
+	return fmt.Errorf("unable to commit rewrite rule changes")
+}
+
+func addSource(services Services) error {
 	if len(*pkgFile) == 0 {
 		return fmt.Errorf("a url or file path (via -f) are required")
 	}
@@ -179,6 +338,7 @@ func addSource(a *amber.ControlInterface) error {
 		source = f
 	}
 
+	var cfg amber.SourceConfig
 	if err := json.NewDecoder(source).Decode(&cfg); err != nil {
 		return fmt.Errorf("failed to parse source config: %v", err)
 	}
@@ -206,16 +366,38 @@ func addSource(a *amber.ControlInterface) error {
 		cfg.BlobRepoUrl = filepath.Join(cfg.RepoUrl, "blobs")
 	}
 
-	added, err := a.AddSrc(cfg)
+	added, err := services.amber.AddSrc(cfg)
 	if err != nil {
-		return fmt.Errorf("IPC encountered an error: %s", err)
+		return fmt.Errorf("fuchsia.amber.Control IPC encountered an error: %s", err)
 	}
 	if !added {
 		return fmt.Errorf("request arguments properly formatted, but possibly otherwise invalid")
 	}
 
 	if isSourceConfigEnabled(&cfg) && !*nonExclusive {
-		if err := disableAllSources(a, cfg.Id); err != nil {
+		if err := disableAllSources(services.amber, cfg.Id); err != nil {
+			return err
+		}
+	}
+
+	repoCfg := upgradeSourceConfig(cfg)
+	s, err := services.repoMgr.Add(repoCfg)
+	if err != nil {
+		return fmt.Errorf("fuchsia.pkg.RepositoryManager IPC encountered an error: %s", err)
+	}
+	status := zx.Status(s)
+	if !(status == zx.ErrOk || status == zx.ErrAlreadyExists) {
+		return fmt.Errorf("unable to register source with RepositoryManager: %s", status)
+	}
+
+	// Nothing currently registers sources in a disabled state, but make a best effort attempt
+	// to try to prevent the source from being used anyway by only configuring a mapping of
+	// fuchsia.com to this source if it is enabled. Note that this doesn't prevent resolving a
+	// package using this config's id explicitly or calling an amber source config
+	// "fuchsia.com".
+	if isSourceConfigEnabled(&cfg) {
+		rule := rewriteRuleForId(cfg.Id)
+		if err := replaceDynamicRewriteRules(services.rewriteEngine, rule); err != nil {
 			return err
 		}
 	}
@@ -223,19 +405,19 @@ func addSource(a *amber.ControlInterface) error {
 	return nil
 }
 
-func rmSource(a *amber.ControlInterface) error {
+func rmSource(services Services) error {
 	name := strings.TrimSpace(*name)
 	if name == "" {
 		return fmt.Errorf("no source id provided")
 	}
 
-	status, err := a.RemoveSrc(name)
+	status, err := services.amber.RemoveSrc(name)
 	if err != nil {
-		return fmt.Errorf("IPC encountered an error: %s", err)
+		return fmt.Errorf("fuchsia.amber.Control IPC encountered an error: %s", err)
 	}
 	switch status {
 	case amber.StatusOk:
-		return nil
+		break
 	case amber.StatusErrNotFound:
 		return fmt.Errorf("Source not found")
 	case amber.StatusErr:
@@ -243,6 +425,25 @@ func rmSource(a *amber.ControlInterface) error {
 	default:
 		return fmt.Errorf("Unexpected status: %v", status)
 	}
+
+	// Since modifications to amber.Control, RepositoryManager, and rewrite.Engine aren't
+	// atomic and amberctl could be interrupted or encounter an error during any step,
+	// unregister the rewrite rule before removing the repo config to prevent a dangling
+	// rewrite rule to a repo that no longer exists.
+	if err := removeAllDynamicRewriteRules(services.rewriteEngine); err != nil {
+		return err
+	}
+
+	s, err := services.repoMgr.Remove(repoUrlForId(name))
+	if err != nil {
+		return fmt.Errorf("fuchsia.pkg.RepositoryManager IPC encountered an error: %s", err)
+	}
+	zxStatus := zx.Status(s)
+	if !(zxStatus == zx.ErrOk || zxStatus == zx.ErrNotFound) {
+		return fmt.Errorf("unable to remove source from RepositoryManager: %s", zxStatus)
+	}
+
+	return nil
 }
 
 func getUp(r *pkg.PackageResolverInterface) error {
@@ -322,10 +523,10 @@ func disableAllSources(a *amber.ControlInterface, except string) error {
 	return nil
 }
 
-func do(amberProxy *amber.ControlInterface, resolverProxy *pkg.PackageResolverInterface) int {
+func do(services Services) int {
 	switch os.Args[1] {
 	case "get_up":
-		if err := getUp(resolverProxy); err != nil {
+		if err := getUp(services.resolver); err != nil {
 			log.Printf("error getting an update: %s", err)
 			return 1
 		}
@@ -334,12 +535,12 @@ func do(amberProxy *amber.ControlInterface, resolverProxy *pkg.PackageResolverIn
 			log.Printf("no blob id provided")
 			return 1
 		}
-		if err := amberProxy.GetBlob(*blobID); err != nil {
+		if err := services.amber.GetBlob(*blobID); err != nil {
 			log.Printf("error requesting blob fetch: %s", err)
 			return 1
 		}
 	case "add_src":
-		if err := addSource(amberProxy); err != nil {
+		if err := addSource(services); err != nil {
 			log.Printf("error adding source: %s", err)
 			if _, ok := err.(ErrGetFile); ok {
 				return 2
@@ -348,12 +549,12 @@ func do(amberProxy *amber.ControlInterface, resolverProxy *pkg.PackageResolverIn
 			}
 		}
 	case "rm_src":
-		if err := rmSource(amberProxy); err != nil {
+		if err := rmSource(services); err != nil {
 			log.Printf("error removing source: %s", err)
 			return 1
 		}
 	case "list_srcs":
-		if err := listSources(amberProxy); err != nil {
+		if err := listSources(services.amber); err != nil {
 			log.Printf("error listing sources: %s", err)
 			return 1
 		}
@@ -361,12 +562,12 @@ func do(amberProxy *amber.ControlInterface, resolverProxy *pkg.PackageResolverIn
 		log.Printf("%q not yet supported\n", os.Args[1])
 		return 1
 	case "test":
-		if err := doTest(amberProxy); err != nil {
+		if err := doTest(services.amber); err != nil {
 			log.Printf("error testing connection to amber: %s", err)
 			return 1
 		}
 	case "system_update":
-		configured, err := amberProxy.CheckForSystemUpdate()
+		configured, err := services.amber.CheckForSystemUpdate()
 		if err != nil {
 			log.Printf("error checking for system update: %s", err)
 			return 1
@@ -382,14 +583,19 @@ func do(amberProxy *amber.ControlInterface, resolverProxy *pkg.PackageResolverIn
 			log.Printf("Error enabling source: no source id provided")
 			return 1
 		}
-		err := setSourceEnablement(amberProxy, *name, true)
+		err := setSourceEnablement(services.amber, *name, true)
 		if err != nil {
 			log.Printf("Error enabling source: %s", err)
 			return 1
 		}
+		err = replaceDynamicRewriteRules(services.rewriteEngine, rewriteRuleForId(*name))
+		if err != nil {
+			log.Printf("Error configuring rewrite rules: %s", err)
+			return 1
+		}
 		fmt.Printf("Source %q enabled\n", *name)
 		if !*nonExclusive {
-			if err := disableAllSources(amberProxy, *name); err != nil {
+			if err := disableAllSources(services.amber, *name); err != nil {
 				log.Printf("Error disabling sources: %s", err)
 				return 1
 			}
@@ -399,14 +605,19 @@ func do(amberProxy *amber.ControlInterface, resolverProxy *pkg.PackageResolverIn
 			log.Printf("Error disabling source: no source id provided")
 			return 1
 		}
-		err := setSourceEnablement(amberProxy, *name, false)
+		err := setSourceEnablement(services.amber, *name, false)
 		if err != nil {
 			log.Printf("Error disabling source: %s", err)
 			return 1
 		}
+		err = removeAllDynamicRewriteRules(services.rewriteEngine)
+		if err != nil {
+			log.Printf("Error configuring rewrite rules: %s", err)
+			return 1
+		}
 		fmt.Printf("Source %q disabled\n", *name)
 	case "gc":
-		err := amberProxy.Gc()
+		err := services.amber.Gc()
 		if err != nil {
 			log.Printf("Error collecting garbage: %s", err)
 			return 1
@@ -462,13 +673,21 @@ func Main() {
 
 	ctx := context.CreateFromStartupInfo()
 
-	amberProxy, _ := connectToAmber(ctx)
-	defer amberProxy.Close()
+	var services Services
+
+	services.amber = connectToAmber(ctx)
+	defer services.amber.Close()
+
+	services.resolver = connectToPackageResolver(ctx)
+	defer services.resolver.Close()
+
+	services.repoMgr = connectToRepositoryManager(ctx)
+	defer services.repoMgr.Close()
 
-	resolverProxy, _ := connectToPackageResolver(ctx)
-	defer resolverProxy.Close()
+	services.rewriteEngine = connectToRewriteEngine(ctx)
+	defer services.rewriteEngine.Close()
 
-	os.Exit(do(amberProxy, resolverProxy))
+	os.Exit(do(services))
 }
 
 type ErrDaemon string
diff --git a/garnet/go/src/amber/meta/amberctl.cmx b/garnet/go/src/amber/meta/amberctl.cmx
index ba9393cf941b54c078e1476667fdb1c328077b15..4bc953cfb13b0cc15bc30e6b571cc0c7e8b2905e 100644
--- a/garnet/go/src/amber/meta/amberctl.cmx
+++ b/garnet/go/src/amber/meta/amberctl.cmx
@@ -7,7 +7,9 @@
             "fuchsia.amber.Control",
             "fuchsia.logger.LogSink",
             "fuchsia.net.SocketProvider",
-            "fuchsia.pkg.PackageResolver"
+            "fuchsia.pkg.PackageResolver",
+            "fuchsia.pkg.RepositoryManager",
+            "fuchsia.pkg.rewrite.Engine"
         ]
     }
 }
diff --git a/garnet/lib/rust/fidl_fuchsia_pkg_ext/src/repo.rs b/garnet/lib/rust/fidl_fuchsia_pkg_ext/src/repo.rs
index 83b783284f13806dd820f13c56c114c5ad04178a..59ce8884268776969173f4379ee39eb73b023694 100644
--- a/garnet/lib/rust/fidl_fuchsia_pkg_ext/src/repo.rs
+++ b/garnet/lib/rust/fidl_fuchsia_pkg_ext/src/repo.rs
@@ -8,18 +8,18 @@ use {
     fuchsia_uri::pkg_uri::{PkgUri, RepoUri},
     serde_derive::{Deserialize, Serialize},
     std::convert::TryFrom,
-    std::mem,
+    std::{fmt, mem},
 };
 
 /// Convenience wrapper for the FIDL RepositoryKeyConfig type
-#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
+#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "lowercase", tag = "type", content = "value", deny_unknown_fields)]
 pub enum RepositoryKey {
     Ed25519(#[serde(with = "hex_serde")] Vec<u8>),
 }
 
 /// Convenience wrapper for the FIDL RepositoryBlobConfig type
-#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
+#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "lowercase", tag = "type", content = "value", deny_unknown_fields)]
 pub enum RepositoryBlobKey {
     Aes(#[serde(with = "hex_serde")] Vec<u8>),
@@ -40,17 +40,27 @@ impl MirrorConfig {
 }
 
 /// Convenience wrapper for generating [MirrorConfig] values.
+#[derive(Clone, Debug)]
 pub struct MirrorConfigBuilder {
     config: MirrorConfig,
 }
 
 impl MirrorConfigBuilder {
-    pub fn new(mirror_url: String) -> Self {
+    pub fn new(mirror_url: impl Into<String>) -> Self {
         MirrorConfigBuilder {
-            config: MirrorConfig { mirror_url: mirror_url, subscribe: false, blob_key: None },
+            config: MirrorConfig {
+                mirror_url: mirror_url.into(),
+                subscribe: false,
+                blob_key: None,
+            },
         }
     }
 
+    pub fn mirror_url(mut self, mirror_url: impl Into<String>) -> Self {
+        self.config.mirror_url = mirror_url.into();
+        self
+    }
+
     pub fn subscribe(mut self, subscribe: bool) -> Self {
         self.config.subscribe = subscribe;
         self
@@ -66,6 +76,12 @@ impl MirrorConfigBuilder {
     }
 }
 
+impl Into<MirrorConfig> for MirrorConfigBuilder {
+    fn into(self) -> MirrorConfig {
+        self.build()
+    }
+}
+
 impl TryFrom<fidl::MirrorConfig> for MirrorConfig {
     type Error = RepositoryParseError;
     fn try_from(other: fidl::MirrorConfig) -> Result<Self, RepositoryParseError> {
@@ -169,6 +185,7 @@ impl Into<fidl::RepositoryConfig> for RepositoryConfig {
 }
 
 /// Convenience wrapper for generating [RepositoryConfig] values.
+#[derive(Clone, Debug)]
 pub struct RepositoryConfigBuilder {
     config: RepositoryConfig,
 }
@@ -177,7 +194,7 @@ impl RepositoryConfigBuilder {
     pub fn new(repo_url: RepoUri) -> Self {
         RepositoryConfigBuilder {
             config: RepositoryConfig {
-                repo_url: repo_url,
+                repo_url,
                 root_keys: vec![],
                 mirrors: vec![],
                 update_package_uri: None,
@@ -185,13 +202,18 @@ impl RepositoryConfigBuilder {
         }
     }
 
+    pub fn repo_url(mut self, repo_url: RepoUri) -> Self {
+        self.config.repo_url = repo_url;
+        self
+    }
+
     pub fn add_root_key(mut self, key: RepositoryKey) -> Self {
         self.config.root_keys.push(key);
         self
     }
 
-    pub fn add_mirror(mut self, mirror: MirrorConfig) -> Self {
-        self.config.mirrors.push(mirror);
+    pub fn add_mirror(mut self, mirror: impl Into<MirrorConfig>) -> Self {
+        self.config.mirrors.push(mirror.into());
         self
     }
 
@@ -205,6 +227,12 @@ impl RepositoryConfigBuilder {
     }
 }
 
+impl Into<RepositoryConfig> for RepositoryConfigBuilder {
+    fn into(self) -> RepositoryConfig {
+        self.build()
+    }
+}
+
 /// Wraper for serializing repository configs to the on-disk JSON format.
 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
 #[serde(tag = "version", content = "content", deny_unknown_fields)]
@@ -231,6 +259,13 @@ impl Into<fidl::RepositoryKeyConfig> for RepositoryKey {
     }
 }
 
+impl fmt::Debug for RepositoryKey {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        let RepositoryKey::Ed25519(ref value) = self;
+        f.debug_tuple("Ed25519").field(&hex::encode(value)).finish()
+    }
+}
+
 impl TryFrom<fidl::RepositoryBlobKey> for RepositoryBlobKey {
     type Error = RepositoryParseError;
     fn try_from(id: fidl::RepositoryBlobKey) -> Result<Self, RepositoryParseError> {
@@ -249,6 +284,13 @@ impl Into<fidl::RepositoryBlobKey> for RepositoryBlobKey {
     }
 }
 
+impl fmt::Debug for RepositoryBlobKey {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        let RepositoryBlobKey::Aes(ref value) = self;
+        f.debug_tuple("Aes").field(&hex::encode(value)).finish()
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
diff --git a/garnet/lib/rust/fuchsia_uri_rewrite/src/rule.rs b/garnet/lib/rust/fuchsia_uri_rewrite/src/rule.rs
index 3a17a326196d9383c273db22a6aa42d917b2694c..e61d14c5d3fe34914f95c5d061724361b91e91f2 100644
--- a/garnet/lib/rust/fuchsia_uri_rewrite/src/rule.rs
+++ b/garnet/lib/rust/fuchsia_uri_rewrite/src/rule.rs
@@ -30,11 +30,16 @@ pub enum RuleConfig {
 impl Rule {
     /// Creates a new `Rule`.
     pub fn new(
-        host_match: String,
-        host_replacement: String,
-        path_prefix_match: String,
-        path_prefix_replacement: String,
+        host_match: impl Into<String>,
+        host_replacement: impl Into<String>,
+        path_prefix_match: impl Into<String>,
+        path_prefix_replacement: impl Into<String>,
     ) -> Result<Self, RuleParseError> {
+        let host_match = host_match.into();
+        let host_replacement = host_replacement.into();
+        let path_prefix_match = path_prefix_match.into();
+        let path_prefix_replacement = path_prefix_replacement.into();
+
         fn validate_host(s: &str) -> Result<(), RuleParseError> {
             PkgUri::new_repository(s.to_owned()).map_err(|_err| RuleParseError::InvalidHost)?;
             Ok(())
@@ -165,13 +170,8 @@ mod serde_tests {
     macro_rules! rule {
         ($host_match:expr => $host_replacement:expr,
          $path_prefix_match:expr => $path_prefix_replacement:expr) => {
-            Rule::new(
-                $host_match.to_owned(),
-                $host_replacement.to_owned(),
-                $path_prefix_match.to_owned(),
-                $path_prefix_replacement.to_owned(),
-            )
-            .unwrap()
+            Rule::new($host_match, $host_replacement, $path_prefix_match, $path_prefix_replacement)
+                .unwrap()
         };
     }
 
@@ -322,19 +322,19 @@ mod rule_tests {
                 #[test]
                 fn $test_name() {
                     let error = Rule::new(
-                        $host_match.to_owned(),
-                        $host_replacement.to_owned(),
-                        $path_prefix_match.to_owned(),
-                        $path_prefix_replacement.to_owned()
+                        $host_match,
+                        $host_replacement,
+                        $path_prefix_match,
+                        $path_prefix_replacement,
                     )
                     .expect_err("should have failed to parse");
                     assert_eq!(error, $error);
 
                     let error = Rule::new(
-                        $host_replacement.to_owned(),
-                        $host_match.to_owned(),
-                        $path_prefix_replacement.to_owned(),
-                        $path_prefix_match.to_owned()
+                        $host_replacement,
+                        $host_match,
+                        $path_prefix_replacement,
+                        $path_prefix_match,
                     )
                     .expect_err("should have failed to parse");
                     assert_eq!(error, $error);
diff --git a/garnet/tests/amberctl/BUILD.gn b/garnet/tests/amberctl/BUILD.gn
index 0faf5ec4ed6a5d0dfe0052d89cc5f00a8d41332b..59df86f799b7f61717419e7ec33d9f509dc3bf89 100644
--- a/garnet/tests/amberctl/BUILD.gn
+++ b/garnet/tests/amberctl/BUILD.gn
@@ -13,10 +13,15 @@ rustc_library("driver") {
   with_unit_tests = true
 
   deps = [
+    "//garnet/lib/rust/fidl_fuchsia_pkg_ext",
+    "//garnet/lib/rust/fuchsia_uri",
+    "//garnet/lib/rust/fuchsia_uri_rewrite",
     "//garnet/public/lib/fidl/rust/fidl",
     "//garnet/public/rust/fuchsia-async",
     "//garnet/public/rust/fuchsia-component",
     "//sdk/fidl/fuchsia.amber:fuchsia.amber-rustc",
+    "//sdk/fidl/fuchsia.pkg:fuchsia.pkg-rustc",
+    "//sdk/fidl/fuchsia.pkg.rewrite:fuchsia.pkg.rewrite-rustc",
     "//sdk/fidl/fuchsia.sys:fuchsia.sys-rustc",
     "//third_party/rust_crates:failure",
     "//third_party/rust_crates:hex",
diff --git a/garnet/tests/amberctl/src/lib.rs b/garnet/tests/amberctl/src/lib.rs
index c23983c89d539d01df422729708ade90d359ae73..e8938d8b7f61a72972db79dbad2d32179b42ab8f 100644
--- a/garnet/tests/amberctl/src/lib.rs
+++ b/garnet/tests/amberctl/src/lib.rs
@@ -6,13 +6,23 @@
 #![cfg(test)]
 
 use {
+    failure::Error,
     fidl_fuchsia_amber::{ControlMarker as AmberMarker, ControlProxy as AmberProxy},
+    fidl_fuchsia_pkg::{RepositoryManagerMarker, RepositoryManagerProxy},
+    fidl_fuchsia_pkg_ext::{
+        MirrorConfigBuilder, RepositoryConfig, RepositoryConfigBuilder, RepositoryKey,
+    },
+    fidl_fuchsia_pkg_rewrite::{
+        EngineMarker as RewriteEngineMarker, EngineProxy as RewriteEngineProxy,
+    },
     fidl_fuchsia_sys::TerminationReason,
     fuchsia_async as fasync,
     fuchsia_component::{
         client::{App, AppBuilder, Stdio},
         server::{NestedEnvironment, ServiceFs},
     },
+    fuchsia_uri::pkg_uri::RepoUri,
+    fuchsia_uri_rewrite::Rule,
     futures::prelude::*,
     std::{convert::TryInto, fs::File},
 };
@@ -23,6 +33,10 @@ use types::SourceConfigBuilder;
 const ROOT_KEY_1: &str = "be0b983f7396da675c40c6b93e47fced7c1e9ea8a32a1fe952ba8f519760b307";
 const ROOT_KEY_2: &str = "00112233445566778899aabbccddeeffffeeddccbbaa99887766554433221100";
 
+fn amberctl() -> AppBuilder {
+    AppBuilder::new("fuchsia-pkg://fuchsia.com/amberctl-tests#meta/amberctl.cmx".to_owned())
+}
+
 struct Mounts {
     misc: tempfile::TempDir,
     data_amber: tempfile::TempDir,
@@ -39,11 +53,14 @@ impl Mounts {
 
 struct Proxies {
     amber: AmberProxy,
+    repo_manager: RepositoryManagerProxy,
+    rewrite_engine: RewriteEngineProxy,
 }
 
 struct TestEnv {
     _amber: App,
-    mounts: Mounts,
+    _pkg_resolver: App,
+    _mounts: Mounts,
     env: NestedEnvironment,
     proxies: Proxies,
 }
@@ -69,43 +86,67 @@ impl TestEnv {
         )
         .expect("/data/amber to mount");
 
+        let mut pkg_resolver = AppBuilder::new(
+            "fuchsia-pkg://fuchsia.com/pkg_resolver#meta/pkg_resolver.cmx".to_owned(),
+        );
+
         let mut fs = ServiceFs::new();
-        fs.add_proxy_service_to::<AmberMarker, _>(amber.directory_request().unwrap().clone());
+        fs.add_proxy_service_to::<AmberMarker, _>(amber.directory_request().unwrap().clone())
+            .add_proxy_service_to::<RepositoryManagerMarker, _>(
+                pkg_resolver.directory_request().unwrap().clone(),
+            )
+            .add_proxy_service_to::<RewriteEngineMarker, _>(
+                pkg_resolver.directory_request().unwrap().clone(),
+            );
+
         let env = fs
             .create_salted_nested_environment("amberctl_env")
             .expect("nested environment to create successfully");
         fasync::spawn(fs.collect());
 
         let amber = amber.spawn(env.launcher()).expect("amber to launch");
+        let pkg_resolver = pkg_resolver.spawn(env.launcher()).expect("amber to launch");
 
         let amber_proxy = env.connect_to_service::<AmberMarker>().expect("connect to amber");
+        let repo_manager_proxy = env
+            .connect_to_service::<RepositoryManagerMarker>()
+            .expect("connect to repository manager");
+        let rewrite_engine_proxy =
+            env.connect_to_service::<RewriteEngineMarker>().expect("connect to rewrite engine");
 
-        Self { _amber: amber, mounts, env, proxies: Proxies { amber: amber_proxy } }
+        Self {
+            _amber: amber,
+            _pkg_resolver: pkg_resolver,
+            _mounts: mounts,
+            env,
+            proxies: Proxies {
+                amber: amber_proxy,
+                repo_manager: repo_manager_proxy,
+                rewrite_engine: rewrite_engine_proxy,
+            },
+        }
     }
 
-    /// Tear down the test environment, retaining the state directories.
-    fn into_mounts(self) -> Mounts {
-        self.mounts
-    }
+    async fn _run_amberctl(&self, builder: AppBuilder) {
+        let fut =
+            builder.stderr(Stdio::Inherit).output(self.env.launcher()).expect("amberctl to launch");
+        let output = await!(fut).expect("amberctl to run");
 
-    /// Re-create the test environment, re-using the existing temporary state directories.
-    fn restart(self) -> Self {
-        Self::new_with_mounts(self.into_mounts())
+        assert_eq!(output.exit_status.reason(), TerminationReason::Exited);
+        assert!(
+            output.exit_status.success(),
+            "amberctl exited with {}\nSTDOUT\n{}\nSTDOUT",
+            output.exit_status.code(),
+            String::from_utf8_lossy(&output.stdout),
+        );
     }
 
-    async fn run_amberctl<'a>(&'a self, args: &'a [&'a str]) {
-        let fut = AppBuilder::new(
-            "fuchsia-pkg://fuchsia.com/amberctl-tests#meta/amberctl.cmx".to_owned(),
-        )
-        .args(args.into_iter().map(|s| *s))
-        .add_dir_to_namespace(
-            "/sources".to_string(),
-            File::open("/pkg/data/sources").expect("/pkg/data/sources to exist"),
-        )
-        .expect("/sources to mount")
-        .stderr(Stdio::Inherit)
-        .output(self.env.launcher())
-        .expect("amberctl to launch");
+    async fn run_amberctl<'a>(&'a self, args: &'a [impl std::fmt::Debug + AsRef<str>]) {
+        let fut = amberctl()
+            .args(args.into_iter().map(|s| s.as_ref()))
+            .stderr(Stdio::Inherit)
+            .output(self.env.launcher())
+            .expect("amberctl to launch");
         let output = await!(fut).expect("amberctl to run");
 
         assert_eq!(output.exit_status.reason(), TerminationReason::Exited);
@@ -118,6 +159,32 @@ impl TestEnv {
         );
     }
 
+    async fn run_amberctl_add_static_src(&self, name: &'static str) {
+        await!(self._run_amberctl(
+            amberctl()
+                .add_dir_to_namespace(
+                    "/configs".to_string(),
+                    File::open("/pkg/data/sources").expect("/pkg/data/sources to exist"),
+                )
+                .expect("static /configs to mount")
+                .args(["add_src", "-f"].into_iter().cloned())
+                .arg(format!("/configs/{}", name))
+        ));
+    }
+
+    async fn run_amberctl_add_src(&self, source: types::SourceConfig) {
+        let mut config_file = tempfile::tempfile().expect("temp config file to create");
+        serde_json::to_writer(&mut config_file, &source).expect("source config to serialize");
+
+        await!(self._run_amberctl(
+            amberctl()
+                .add_dir_to_namespace("/configs/test.json".to_string(), config_file)
+                .expect("static /configs to mount")
+                // Run amberctl in non-exclusive mode so it doesn't disable existing source configs
+                .args(["add_src", "-x", "-f", "/configs/test.json"].iter().map(|s| *s))
+        ));
+    }
+
     async fn amber_list_sources(&self) -> Vec<types::SourceConfig> {
         let sources = await!(self.proxies.amber.list_srcs()).unwrap();
 
@@ -130,51 +197,90 @@ impl TestEnv {
         sources.sort_unstable();
         sources
     }
+
+    async fn resolver_list_repos(&self) -> Vec<RepositoryConfig> {
+        let (iterator, iterator_server_end) = fidl::endpoints::create_proxy().unwrap();
+        self.proxies.repo_manager.list(iterator_server_end).unwrap();
+        await!(collect_iterator(|| iterator.next())).unwrap()
+    }
+
+    async fn rewrite_engine_list_rules(&self) -> Vec<Rule> {
+        let (iterator, iterator_server_end) = fidl::endpoints::create_proxy().unwrap();
+        self.proxies.rewrite_engine.list(iterator_server_end).unwrap();
+        await!(collect_iterator(|| iterator.next())).unwrap()
+    }
+}
+
+async fn collect_iterator<F, E, I, O>(mut next: impl FnMut() -> F) -> Result<Vec<O>, Error>
+where
+    F: Future<Output = Result<Vec<I>, fidl::Error>>,
+    I: TryInto<O, Error = E>,
+    Error: From<E>,
+{
+    let mut res = Vec::new();
+    loop {
+        let more = await!(next())?;
+        if more.is_empty() {
+            break;
+        }
+        res.extend(more.into_iter().map(|cfg| cfg.try_into()).collect::<Result<Vec<_>, _>>()?);
+    }
+    Ok(res)
 }
 
 struct SourceConfigGenerator {
-    builder: SourceConfigBuilder,
-    root_id: String,
-    root_url: String,
+    id_prefix: String,
     n: usize,
 }
 
 impl SourceConfigGenerator {
-    fn new(builder: SourceConfigBuilder) -> Self {
-        let config = builder.clone().build();
-        Self {
-            root_id: config.id().to_owned(),
-            root_url: config.repo_url().to_owned(),
-            builder,
-            n: 0,
-        }
+    fn new(id_prefix: impl Into<String>) -> Self {
+        Self { id_prefix: id_prefix.into(), n: 0 }
     }
 }
 
 impl Iterator for SourceConfigGenerator {
-    type Item = types::SourceConfigBuilder;
+    type Item = (types::SourceConfigBuilder, RepositoryConfigBuilder);
 
     fn next(&mut self) -> Option<Self::Item> {
-        let id = format!("{}{:02}", &self.root_id, self.n);
-        let url = format!("{}/{:02}", &self.root_url, self.n);
+        let id = format!("{}{:02}", &self.id_prefix, self.n);
+        let repo_url = format!("fuchsia-pkg://{}", &id);
+        let mirror_url = format!("http://example.com/{}", &id);
         self.n += 1;
 
-        Some(self.builder.clone().id(id).repo_url(url))
+        Some((
+            SourceConfigBuilder::new(id)
+                .repo_url(mirror_url.clone())
+                .add_root_key(ROOT_KEY_1)
+                .auto(true),
+            RepositoryConfigBuilder::new(RepoUri::parse(&repo_url).unwrap())
+                .add_root_key(RepositoryKey::Ed25519(hex::decode(ROOT_KEY_1).unwrap()))
+                .add_mirror(MirrorConfigBuilder::new(mirror_url).subscribe(true)),
+        ))
     }
 }
 
+fn make_test_repo_config() -> RepositoryConfig {
+    RepositoryConfigBuilder::new("fuchsia-pkg://test".parse().unwrap())
+        .add_root_key(RepositoryKey::Ed25519(hex::decode(ROOT_KEY_1).unwrap()))
+        .add_mirror(MirrorConfigBuilder::new("http://example.com").subscribe(true))
+        .build()
+}
+
 #[fasync::run_singlethreaded(test)]
-async fn test_amber_starts_with_no_sources() {
+async fn test_services_start_with_no_config() {
     let env = TestEnv::new();
 
     assert_eq!(await!(env.amber_list_sources()), vec![]);
+    assert_eq!(await!(env.resolver_list_repos()), vec![]);
+    assert_eq!(await!(env.rewrite_engine_list_rules()), vec![]);
 }
 
 #[fasync::run_singlethreaded(test)]
 async fn test_add_src() {
     let env = TestEnv::new();
 
-    await!(env.run_amberctl(&["add_src", "-f", "/sources/test.json"]));
+    await!(env.run_amberctl_add_static_src("test.json"));
 
     let cfg_test = SourceConfigBuilder::new("test")
         .repo_url("http://example.com")
@@ -183,44 +289,43 @@ async fn test_add_src() {
         .add_root_key(ROOT_KEY_1)
         .build();
 
-    assert_eq!(await!(env.amber_list_sources()), vec![cfg_test.clone()]);
-
-    // Ensure source configs persist across service restarts
-    let env = env.restart();
     assert_eq!(await!(env.amber_list_sources()), vec![cfg_test]);
+    assert_eq!(await!(env.resolver_list_repos()), vec![make_test_repo_config()]);
+    assert_eq!(
+        await!(env.rewrite_engine_list_rules()),
+        vec![Rule::new("fuchsia.com", "test", "/", "/").unwrap()]
+    );
 }
 
 #[fasync::run_singlethreaded(test)]
 async fn test_add_src_disables_other_sources() {
     let env = TestEnv::new();
 
-    let configs = SourceConfigGenerator::new(
-        SourceConfigBuilder::new("test")
-            .repo_url("http://example.com")
-            .rate_period(60)
-            .auto(true)
-            .add_root_key(ROOT_KEY_1),
-    )
-    .take(3)
-    .collect::<Vec<_>>();
-
-    for config in &configs {
-        assert_eq!(
-            await!(env.proxies.amber.add_src(&mut config.clone().build().into())).unwrap(),
-            true
-        );
+    let configs = SourceConfigGenerator::new("testgen").take(3).collect::<Vec<_>>();
+
+    for (config, _) in &configs {
+        await!(env.run_amberctl_add_src(config.clone().build().into()));
     }
 
-    await!(env.run_amberctl(&["add_src", "-f", "/sources/test.json"]));
+    await!(env.run_amberctl_add_static_src("test.json"));
 
-    let mut configs =
-        configs.into_iter().map(|builder| builder.enabled(false).build()).collect::<Vec<_>>();
+    let mut source_configs = vec![];
+    let mut repo_configs = vec![make_test_repo_config()];
+    for (source_config, repo_config) in configs {
+        source_configs.push(source_config.enabled(false).build());
+        repo_configs.push(repo_config.build());
+    }
     let test_config =
         serde_json::from_reader(File::open("/pkg/data/sources/test.json").unwrap()).unwrap();
-    configs.push(test_config);
-    configs.sort_unstable();
+    source_configs.push(test_config);
+    source_configs.sort_unstable();
 
-    assert_eq!(await!(env.amber_list_sources()), configs);
+    assert_eq!(await!(env.amber_list_sources()), source_configs);
+    assert_eq!(await!(env.resolver_list_repos()), repo_configs);
+    assert_eq!(
+        await!(env.rewrite_engine_list_rules()),
+        vec![Rule::new("fuchsia.com", "test", "/", "/").unwrap()]
+    );
 }
 
 #[fasync::run_singlethreaded(test)]
@@ -239,30 +344,55 @@ async fn test_rm_src() {
         .add_root_key(ROOT_KEY_2)
         .build();
 
-    assert_eq!(await!(env.proxies.amber.add_src(&mut cfg_a.clone().into())).unwrap(), true);
-    assert_eq!(await!(env.proxies.amber.add_src(&mut cfg_b.clone().into())).unwrap(), true);
-
-    await!(env.run_amberctl(&["rm_src", "-n", "b"]));
-    assert_eq!(await!(env.amber_list_sources()), vec![cfg_a]);
+    await!(env.run_amberctl_add_src(cfg_a.clone().into()));
+    await!(env.run_amberctl_add_src(cfg_b.clone().into()));
 
     await!(env.run_amberctl(&["rm_src", "-n", "a"]));
+    assert_eq!(await!(env.amber_list_sources()), vec![cfg_b]);
+    assert_eq!(
+        await!(env.resolver_list_repos()),
+        vec![RepositoryConfigBuilder::new("fuchsia-pkg://b".parse().unwrap())
+            .add_root_key(RepositoryKey::Ed25519(hex::decode(ROOT_KEY_2).unwrap()))
+            .add_mirror(MirrorConfigBuilder::new("http://example.com/b"))
+            .build()]
+    );
+    // rm_src removes all rules, so no source remains enabled.
+    assert_eq!(await!(env.rewrite_engine_list_rules()), vec![]);
+
+    await!(env.run_amberctl(&["rm_src", "-n", "b"]));
     assert_eq!(await!(env.amber_list_sources()), vec![]);
+    assert_eq!(await!(env.resolver_list_repos()), vec![]);
+    assert_eq!(await!(env.rewrite_engine_list_rules()), vec![]);
 }
 
 #[fasync::run_singlethreaded(test)]
 async fn test_enable_src() {
     let env = TestEnv::new();
 
-    let cfg = SourceConfigBuilder::new("test")
+    let source = SourceConfigBuilder::new("test")
         .repo_url("http://example.com")
         .enabled(false)
         .add_root_key(ROOT_KEY_1);
 
-    assert_eq!(await!(env.proxies.amber.add_src(&mut cfg.clone().build().into())).unwrap(), true);
+    let repo = RepositoryConfigBuilder::new("fuchsia-pkg://test".parse().unwrap())
+        .add_root_key(RepositoryKey::Ed25519(hex::decode(ROOT_KEY_1).unwrap()))
+        .add_mirror(MirrorConfigBuilder::new("http://example.com"))
+        .build();
+
+    await!(env.run_amberctl_add_src(source.clone().build().into()));
+
+    assert_eq!(await!(env.resolver_list_repos()), vec![repo.clone()]);
+    // Adding a disabled source does not add a rewrite rule for it.
+    assert_eq!(await!(env.rewrite_engine_list_rules()), vec![]);
 
     await!(env.run_amberctl(&["enable_src", "-n", "test"]));
 
-    assert_eq!(await!(env.amber_list_sources()), vec![cfg.enabled(true).build()]);
+    assert_eq!(await!(env.amber_list_sources()), vec![source.enabled(true).build()]);
+    assert_eq!(await!(env.resolver_list_repos()), vec![repo]);
+    assert_eq!(
+        await!(env.rewrite_engine_list_rules()),
+        vec![Rule::new("fuchsia.com", "test", "/", "/").unwrap()]
+    );
 }
 
 #[fasync::run_singlethreaded(test)]
@@ -270,33 +400,39 @@ async fn test_enable_src_disables_other_sources() {
     let env = TestEnv::new();
 
     // add some enabled sources
-    let mut gen = SourceConfigGenerator::new(
-        SourceConfigBuilder::new("test").repo_url("http://example.com").add_root_key(ROOT_KEY_1),
-    );
+    let mut gen = SourceConfigGenerator::new("test");
     let configs = gen.by_ref().take(3).collect::<Vec<_>>();
-    for config in &configs {
-        assert_eq!(
-            await!(env.proxies.amber.add_src(&mut config.clone().build().into())).unwrap(),
-            true
-        );
+    for (config, _) in &configs {
+        await!(env.run_amberctl_add_src(config.clone().build().into()));
     }
 
-    // add an initially disabled source.
-    let config = gen.next().unwrap().enabled(false);
+    // add an initially disabled source
+    let (config, repo) = gen.next().unwrap();
+    let config = config.enabled(false);
     let c = config.clone().build();
     let id = c.id().to_owned();
-    assert_eq!(await!(env.proxies.amber.add_src(&mut c.into())).unwrap(), true);
+    await!(env.run_amberctl_add_src(c.into()));
 
     // enable that source
     let args = ["enable_src", "-n", &id];
     await!(env.run_amberctl(&args));
 
     // verify the enabled sources are now disabled and the disabled source is now enabled
-    let mut configs =
-        configs.into_iter().map(|builder| builder.enabled(false).build()).collect::<Vec<_>>();
-    configs.push(config.enabled(true).build());
-    configs.sort_unstable();
-    assert_eq!(await!(env.amber_list_sources()), configs);
+    let mut source_configs = vec![];
+    let mut repo_configs = vec![];
+    for (source_config, repo_config) in configs {
+        source_configs.push(source_config.enabled(false).build());
+        repo_configs.push(repo_config.build());
+    }
+    source_configs.push(config.enabled(true).build());
+    repo_configs.push(repo.build());
+    source_configs.sort_unstable();
+    assert_eq!(await!(env.amber_list_sources()), source_configs);
+    assert_eq!(await!(env.resolver_list_repos()), repo_configs);
+    assert_eq!(
+        await!(env.rewrite_engine_list_rules()),
+        vec![Rule::new("fuchsia.com", id, "/", "/").unwrap()]
+    );
 }
 
 #[fasync::run_singlethreaded(test)]
@@ -313,8 +449,8 @@ async fn test_disable_src() {
         .rate_period(60)
         .add_root_key(ROOT_KEY_2);
 
-    assert_eq!(await!(env.proxies.amber.add_src(&mut cfg_a.clone().build().into())).unwrap(), true);
-    assert_eq!(await!(env.proxies.amber.add_src(&mut cfg_b.clone().build().into())).unwrap(), true);
+    await!(env.run_amberctl_add_src(cfg_a.clone().build().into()));
+    await!(env.run_amberctl_add_src(cfg_b.clone().build().into()));
 
     await!(env.run_amberctl(&["disable_src", "-n", "a"]));
 
@@ -322,4 +458,19 @@ async fn test_disable_src() {
         await!(env.amber_list_sources()),
         vec![cfg_a.enabled(false).build(), cfg_b.enabled(true).build().into(),]
     );
+    assert_eq!(
+        await!(env.resolver_list_repos()),
+        vec![
+            RepositoryConfigBuilder::new("fuchsia-pkg://a".parse().unwrap())
+                .add_root_key(RepositoryKey::Ed25519(hex::decode(ROOT_KEY_1).unwrap()))
+                .add_mirror(MirrorConfigBuilder::new("http://example.com/a"))
+                .build(),
+            RepositoryConfigBuilder::new("fuchsia-pkg://b".parse().unwrap())
+                .add_root_key(RepositoryKey::Ed25519(hex::decode(ROOT_KEY_2).unwrap()))
+                .add_mirror(MirrorConfigBuilder::new("http://example.com/b"))
+                .build(),
+        ]
+    );
+    // disabling any source clears all rewrite rules.
+    assert_eq!(await!(env.rewrite_engine_list_rules()), vec![]);
 }
diff --git a/garnet/tests/amberctl/src/types.rs b/garnet/tests/amberctl/src/types.rs
index cbf00faec8ca244399e166dfc67bdd143417df22..eedbbf7b8fc031b5da14dd20b9169fdca97f15e3 100644
--- a/garnet/tests/amberctl/src/types.rs
+++ b/garnet/tests/amberctl/src/types.rs
@@ -35,11 +35,6 @@ impl SourceConfigBuilder {
         }
     }
 
-    pub fn id(mut self, value: impl Into<String>) -> Self {
-        self.config.id = value.into();
-        self
-    }
-
     pub fn repo_url(mut self, value: impl Into<String>) -> Self {
         self.config.repo_url = value.into();
         self.config.blob_repo_url = format!("{}/blobs", self.config.repo_url);
@@ -98,9 +93,6 @@ impl SourceConfig {
     pub fn id(&self) -> &str {
         self.id.as_str()
     }
-    pub fn repo_url(&self) -> &str {
-        self.repo_url.as_str()
-    }
 }
 
 impl Into<fidl::SourceConfig> for SourceConfig {