[release] Snap to 9ea8a42c2a
Change-Id: I350451c7f575a2e469cbe1940ec2e01dff5bbf02
diff --git a/build/components/cmx/BUILD.gn b/build/components/cmx/BUILD.gn
index fa55f5b..09a6933 100644
--- a/build/components/cmx/BUILD.gn
+++ b/build/components/cmx/BUILD.gn
@@ -643,7 +643,6 @@
     "//src/ui/examples/flatland-display:flatland-display_manifest_compile",
     "//src/ui/examples/flatland-view-provider:flatland-view-provider-v1_manifest_compile",
     "//src/ui/examples/simplest_app:simplest_app_component_manifest_compile",
-    "//src/ui/examples/yuv_to_image_pipe:yuv_to_image_pipe_component_manifest_compile",
     "//src/ui/input/drivers/goldfish_sensor:goldfish-sensor-parser-fuzzer_component_manifest_compile",
     "//src/ui/scenic:component_manifest_compile",
     "//src/ui/scenic/lib/input/tests/fuzzers:input-fuzzer_component_manifest_compile",
diff --git a/build/dart/BUILD.gn b/build/dart/BUILD.gn
index c5511d3..3d64335 100644
--- a/build/dart/BUILD.gn
+++ b/build/dart/BUILD.gn
@@ -111,7 +111,6 @@
   deps = [
     ":gen_reference_docs_test($host_toolchain)",
     ":generate_dart_toc_test($host_toolchain)",
-    "tests",
   ]
 }
 
diff --git a/build/dart/tests/BUILD.gn b/build/dart/tests/BUILD.gn
deleted file mode 100644
index facf36b..0000000
--- a/build/dart/tests/BUILD.gn
+++ /dev/null
@@ -1,39 +0,0 @@
-# Copyright 2020 The Fuchsia Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//build/components.gni")
-import("//build/dart/dart_component.gni")
-import("//build/dart/dart_library.gni")
-import("//build/python/python_host_test.gni")
-
-group("tests") {
-  testonly = true
-  deps = [ ":build-dart-test-package" ]
-}
-
-dart_library("lib") {
-  package_name = "build_dart_test"
-  null_safe = true
-  sources = [ "main.dart" ]
-  deps = [ "//sdk/dart/fuchsia" ]
-}
-
-resource("text-file") {
-  sources = [ "text_file.txt" ]
-  outputs = [ "data/text_file.txt" ]
-}
-
-dart_component("dart-component") {
-  manifest = "meta/dart-component.cmx"
-  main_package = "build_dart_test"
-  null_safe = true
-  deps = [
-    ":lib",
-    ":text-file",
-  ]
-}
-
-fuchsia_package("build-dart-test-package") {
-  deps = [ ":dart-component" ]
-}
diff --git a/build/dart/tests/lib/main.dart b/build/dart/tests/lib/main.dart
deleted file mode 100644
index 1c902c8..0000000
--- a/build/dart/tests/lib/main.dart
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2020 The Fuchsia Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'dart:io';
-
-import 'package:fuchsia/fuchsia.dart' as fuchsia;
-
-void main(List<String> args) {
-  // Verify that resource loading works
-  final txt = File('/pkg/data/text_file.txt').readAsStringSync();
-  print(txt);
-  fuchsia.exit(0);
-}
diff --git a/build/dart/tests/meta/dart-component.cmx b/build/dart/tests/meta/dart-component.cmx
deleted file mode 100644
index 88b87e1..0000000
--- a/build/dart/tests/meta/dart-component.cmx
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-    "program": {
-        "data": "data/dart-component"
-    },
-    "sandbox": {
-        "services": [
-            "fuchsia.sys.Environment",
-            "fuchsia.ui.input.ImeService"
-        ]
-    }
-}
diff --git a/build/dart/tests/pubspec.yaml b/build/dart/tests/pubspec.yaml
deleted file mode 100644
index fe65bde..0000000
--- a/build/dart/tests/pubspec.yaml
+++ /dev/null
@@ -1,8 +0,0 @@
-# Copyright 2020 The Fuchsia Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-name: build.dart.tests
-environment:
-  sdk: '>=2.8.0 <3.0.0'
-
diff --git a/build/dart/tests/text_file.txt b/build/dart/tests/text_file.txt
deleted file mode 100644
index 8ab686ea..0000000
--- a/build/dart/tests/text_file.txt
+++ /dev/null
@@ -1 +0,0 @@
-Hello, World!
diff --git a/build/images/network-conformance/assemble_network_conformance_system.gni b/build/images/network-conformance/assemble_network_conformance_system.gni
index 04f8b1b..12ab53e 100644
--- a/build/images/network-conformance/assemble_network_conformance_system.gni
+++ b/build/images/network-conformance/assemble_network_conformance_system.gni
@@ -48,7 +48,6 @@
       "//src/developer/sshd-host",
       "//src/developer/sshd-host:config",
       "//src/connectivity/network",
-      "//src/connectivity/network/dhcp",
       "//src/connectivity/network/mdns/bundles:config",
       "//src/connectivity/network/mdns/bundles:services",
       "//src/connectivity/network/netcfg:config-default",
diff --git a/bundles/packages/prod/BUILD.gn b/bundles/packages/prod/BUILD.gn
index f3f9593..fd2f4cf 100644
--- a/bundles/packages/prod/BUILD.gn
+++ b/bundles/packages/prod/BUILD.gn
@@ -39,7 +39,6 @@
     ":tiles",
     ":tracing",
     ":wlan_drivers",
-    "//src/connectivity/network/dhcp:dhcpd",
     "//src/connectivity/network/mdns/bundles:services",
     "//src/connectivity/network/mdns/bundles:utils",
     "//src/connectivity/telephony/drivers",
diff --git a/docs/contribute/governance/rfcs/0172_ui_activity_service.md b/docs/contribute/governance/rfcs/0172_ui_activity_service.md
new file mode 100644
index 0000000..e7445bd
--- /dev/null
+++ b/docs/contribute/governance/rfcs/0172_ui_activity_service.md
@@ -0,0 +1,453 @@
+<!-- Generated with `fx rfc` -->
+<!-- mdformat off(templates not supported) -->
+{% set rfcid = "RFC-0172" %}
+{% include "docs/contribute/governance/rfcs/_common/_rfc_header.md" %}
+# {{ rfc.name }}: {{ rfc.title }}
+{# Fuchsia RFCs use templates to display various fields from _rfcs.yaml. View the #}
+{# fully rendered RFCs at https://fuchsia.dev/fuchsia-src/contribute/governance/rfcs #}
+<!-- SET the `rfcid` VAR ABOVE. DO NOT EDIT ANYTHING ELSE ABOVE THIS LINE. -->
+
+<!-- mdformat on -->
+
+<!-- This should begin with an H2 element (for example, ## Summary).-->
+
+## Summary
+
+This RFC introduces a new UI Activity Service which replaces and downscopes
+the responsibilities of a pre-existing version.
+
+The proposed service adds two new FIDL protocols:
+
+- a private FIDL protocol `fuchsia.input.interaction.observation.Aggregator` to
+  collect evidence of user input activity, and
+- a partner FIDL protocol `fuchsia.input.interaction.Notifier` to notify
+  clients of changes in user input activity state.
+
+## Motivation
+
+We would like to provide a service that notifies other parts of the system
+whether recent user input activity has occurred. This service is useful to
+inform other system services such as power-conserving protocols or screensaver
+functionality.
+
+There is a pre-existing ["Activity Service"][1] which establishes itself as a
+source of truth regarding user idle state in the system. This proposal is a
+departure from the current approach and introduces a new "UI Activity Service"
+to indicate a narrower scope of responsibility–-over user input activity alone
+rather than system activity altogether–-thereby enabling new features in some
+products while reducing technical debt in others.
+
+[1]: https://cs.opensource.google/fuchsia/fuchsia/+/main:src/sys/activity/
+
+## Stakeholders
+
+_Facilitator:_
+
+leannogasawara@google.com
+
+_Reviewers:_
+
+- jsankey@google.com
+- neelsa@google.com
+- sanjayc@google.com
+- quiche@google.com
+- wittrock@google.com
+
+_Consulted:_
+
+- anwilson@google.com
+- comfoltey@google.com
+- fmil@google.com
+- kpozin@google.com
+- palmer@google.com
+- yaar@google.com
+
+_Socialization:_
+
+This RFC was discussed during doc review with the Fuchsia Input team, and
+requested reviews for [privacy][2] and [security][3].
+
+[2]: https://fxbug.dev/99288
+[3]: https://fxbug.dev/99287
+
+## Design
+
+### Activity State
+
+**Activity** is user input interaction with the device that happened recently.
+
+**Recently** is defined as some product-configured time threshold, e.g. 15
+minutes.
+
+When the device has not received activity recently, it is considered **idle**.
+
+**User input interaction** is constrained to the use cases listed below for the
+initial implementation.
+
+### Requirements
+
+The following user interactions MUST result in the service entering or renewing
+an **active** state:
+
+- A user touched a screen.
+- A user interacted with a mouse or keyboard.
+- A user pressed a media button, such as volume up or down.
+
+The following user interactions SHOULD result in the service entering or
+renewing an **active** state:
+
+- A user enabled or disabled a screen reader. If the user does this using one of
+  the input modes described above, the action would be captured implicitly.
+  Alternatively, if this is done via a change to SetUI, it is recommended to be
+  be treated as akin to opening the device lid.
+- A user has opened the device lid.
+- A user turned the device on.
+
+The following interactions are not considered active user input activities due
+to their corresponding rationales given inline. Therefore they MUST NOT result
+in the service entering an **active** state:
+
+- A user is watching a video or listening to an audio file:
+  `fuchsia.media.ActivityReporter` should be used instead of the activity
+  service discussed in this RFC.
+
+### Protocol and service
+
+We introduce a new internal FIDL protocol,
+`fuchsia.input.interaction.observation.Aggregator` and a new FIDL protocol
+`fuchsia.input.interaction.Notifier` to the partner SDK. These protocols will
+be implemented and exposed by a new `Activity` class within the [Input
+Pipeline][4] component.
+
+[4]: /docs/contribute/governance/rfcs/0096_user_input_arch.md#input-pipeline
+
+#### `fuchsia.input.interaction.observation.Aggregator`
+
+The clients of this API report that they believe they have evidence of user
+input activity, and we treat their information as such. Therefore access is
+restricted to in-tree components via capability routing. See
+[Security considerations](#security-considerations) for more.
+
+```
+library fuchsia.input.interaction.observation;
+
+using zx;
+
+/// The Aggregator protocol collects evidence of user activity and uses this
+/// evidence to set the system's activity state.
+@discoverable
+protocol Aggregator {
+    /// Reports a discrete activity such as a keystroke.
+    ReportDiscreteActivity(struct {
+        activity DiscreteActivity;
+        event_time zx.time;
+    }) -> ();
+};
+```
+
+Unlike its predecessor, which collected both discrete and ongoing events (i.e.
+activities with a start and end time), the initial version of this protocol
+defined by this RFC only collects "Discrete" [activities][5]. The ability to
+collect ongoing events was initially introduced to support media playback,
+which is explicitly _not_ a use case of this RFC.
+
+[5]: https://cs.opensource.google/fuchsia/fuchsia/+/main:sdk/fidl/fuchsia.ui.activity/activity.fidl
+
+#### `fuchsia.input.interaction.Notifier`
+
+The clients of this API should want to subscribe to changes between user
+interaction "active" and "idle" states as defined similarly to
+[`fuchsia.ui.activity.State`][6]. This RFC plans to create a similar enum in
+the `fuchsia.input.interaction` library that will be `flexible` in case of
+future need for expansion. Access does not need to be restricted, see
+[Privacy considerations](#privacy-considerations). Examples include:
+
+- In-tree: Accessibility Manager silences the screen reader from verbalizing
+  changes to a screen (e.g. every minute if a clock is displaying) when the
+  device enters an idle state.
+- Out-of-tree: A client returns to a home screen when the device enters an idle
+  state.
+
+```
+library fuchsia.input.interaction;
+
+/// The Notifier protocol offers a subscription interface through
+/// which clients can watch for changes in the system's activity state.
+@discoverable
+protocol Notifier {
+    /// Subscribe to changes in the system's state.
+    /// The server will always respond immediately with the initial state,
+    /// and after that whenever the system's state changes.
+    WatchState(table {}) -> (resource struct {
+        state State;
+    });
+};
+```
+
+The proposed protocol will differ from the existing
+[`fuchsia.ui.activity.Notifier.WatchState`][7] protocol because it will use a
+hanging get pattern, removing the need for a [Listener][8] protocol entirely. It
+also does not send a timestamp.
+
+[6]: https://cs.opensource.google/fuchsia/fuchsia/+/main:sdk/fidl/fuchsia.ui.activity/state.fidl
+[7]: https://cs.opensource.google/fuchsia/fuchsia/+/main:sdk/fidl/fuchsia.ui.activity/provider.fidl;l=16
+[8]: https://cs.opensource.google/fuchsia/fuchsia/+/main:sdk/fidl/fuchsia.ui.activity/provider.fidl;l=24
+
+#### Lid Sensor
+
+The UI Activity Service can use the `fuchsia.hardware.input` FIDL to watch for
+lid sensor driver reports and transition to an active state upon receiving a
+lid-opened report.
+
+### Integrating with Input Pipeline
+
+#### Space Considerations
+
+Implementing the UI Activity Service (A) within the Input Pipeline (IP)
+component instead of as its own component can save ~172 KB in disk space,
+depending on the product and board configurations. For space-constrained
+products, reducing size frees room for other improvements in the system.
+
+| Build                          | A      | IP      | [IP + two FIDLs][9] |
+| ------------------------------ | ------ | ------- | ------------------- |
+| workstation_pro.chromebook-x64 | 196 KB | 1364 KB | 1388 KB             |
+| core.astro                     | 184 KB | 688 KB  | 700 KB              |
+
+[9]: https://fuchsia-review.googlesource.com/c/fuchsia/+/673448
+
+#### Additional considerations
+
+Integrating the UI Activity Service within Input Pipeline includes the
+following:
+
+- The service SHOULD be single-threaded because it is not necessary or
+  beneficial to be multi-threaded at this time. Additionally, Input Pipeline is
+  single-threaded, and converting the library to support a multi-threaded
+  approach would introduce unnecessary complexity.
+- The service SHOULD be implemented in Rust to avoid introducing the complexity
+  required to manage multiple languages in one library.
+- The service will not be available in recovery mode, since recovery mode does
+  not use the Input Pipeline or Scene Manager. The absence of the activity
+  service for recovery mode is not an issue because, by design, recovery mode
+  uses a single highly-integrated component to minimize dependencies.
+- The service SHOULD NOT process `InputReport`s by marking events handled or
+  sending them to other components, as that makes the service aware of and
+  responsible of more information than is necessary to determine whether
+  activity has happened recently.
+- The service SHOULD NOT have special knowledge of the sources of input events
+  (as these SHOULD be reported via
+  `fuchsia.input.interaction.observation.Aggregator`).
+- The service therefore SHOULD NOT be implemented as an `InputHandler`.
+
+### Configurable idle threshold
+
+The idle threshold is how much time has passed since the last user activity for
+the system to become idle. This can be set using [structured configuration][10]
+in the Input Pipeline or Scene Manager component at the product level and will
+be set to 15 minutes for the current products.
+
+[10]: /docs/contribute/governance/rfcs/0127_structured_configuration.md
+
+## Implementation
+
+The service will be implemented as follows:
+
+1. Define the new `fuchsia.input.interaction.observation.Aggregator` and
+   `fuchsia.input.interaction.Notifier` FIDL protocols.
+2. Implement and unit test a new `Activity` service that is initialized within
+   Input Pipeline to serve both protocols.
+3. Relevant `InputHandlers` report activity via
+   `fuchsia.input.interaction.observation.Aggregator`. Note: Reporting activity
+   during this phase rather than during the binding phase is preferred because
+   [`InputHandlers`][11] are responsible for dispatching information from
+   `InputEvents` to other components or services.
+
+   Affected handlers include:
+
+- MediaButtonsHandler
+- MouseInjectorHandler
+- TouchInjectorHandler
+- KeypressHandler (new)
+
+[11]: /docs/contribute/governance/rfcs/0096_user_input_arch.md#input-pipeline
+
+4. Add integration tests for the following cases:
+
+- `fuchsia.input.interaction.observation.Aggregator` informs Activity state
+- `fuchsia.input.interaction.Notifier` is notified when system transitions to
+  Active
+- `fuchsia.input.interaction.Notifier` is notified when system transitions to
+  Idle
+
+=== `fuchsia.input.interaction.Notifier` is available for use by clients at
+this point ===
+
+5. Deprecate and remove `fuchsia.ui.activity.Tracker`,
+   `fuchsia.ui.activity.Provider`, and `fuchsia.ui.activity.control.Control`
+
+- Mark the FIDL protocols as deprecated with instructions to use the new
+  protocols
+- Migrate existing usages of `fuchsia.ui.activity.Provider` to
+  `fuchsia.input.interaction.Notifier` [in [Cobalt][12], [Omaha][13], and
+  [PowerManager][14]]
+- Remove usage of `fuchsia.ui.activity.Tracker` (non-functional)
+- Delete `//src/sys/activity`
+
+[12]: https://cs.opensource.google/fuchsia/fuchsia/+/main:src/cobalt/bin/system-metrics/system_metrics_daemon.cc?q=fuchsia_ui_activity%20OR%20fuchsia.ui.activity%20OR%20fuchsia::ui::activity&ss=fuchsia%2Ffuchsia&start=71
+[13]: https://cs.opensource.google/fuchsia/fuchsia/+/main:src/sys/pkg/bin/omaha-client/src/policy.rs?q=fuchsia_ui_activity%20OR%20fuchsia.ui.activity%20OR%20fuchsia::ui::activity&ss=fuchsia%2Ffuchsia&start=71
+[14]: https://cs.opensource.google/fuchsia/fuchsia/+/main:src/power/power-manager/src/activity_handler.rs?q=fuchsia_ui_activity%20&ss=fuchsia%2Ffuchsia
+
+## Performance
+
+The UI Activity Service MUST NOT add latency to the processing of input events
+in the system.
+
+While the protocols proposed do not introduce asynchronous or computation-heavy
+logic, we may want to introduce throttling to high-frequency input events. For
+example, it's common for mice to send about 1000 events/second, so it may be
+desirable to rate-limit the FIDL calls from `MouseInjectorHandler` to the
+Activity Service.
+
+We can monitor pre-existing performance tests, such as input latency tests, to
+discern whether changes regress performance.
+
+## Security considerations
+
+The primary abuse vector for the UI Activity Service is to keep the system awake
+by spuriously reporting activity using
+`fuchsia.input.interaction.observation.Aggregator`. For instance, a malicious
+application could periodically report activity to keep the system awake. This is
+considered low risk given that
+`fuchsia.input.interaction.observation.Aggregator` is only available in-tree,
+not offered in the SDK, and will only be used by components in the UI realm.
+
+Any service like this one poses the risk of delaying software updates, keeping
+the screen unlocked, depleting the battery, and possibly denying service to
+other applications on the device. Delegated data access is a risk not solved by
+this RFC. Clients relying on idle state for features like the aforementioned
+SHOULD implement their own maximum activity limit if relevant when using this
+service, e.g. a forced system OTA once a user has been active for 24 hours.
+
+Since this capability has to be routed through CFV2, it should be noted that the
+platform only grants ability to report activity to Input Pipeline and
+potentially A11y Manager at the platform level, and that products can further
+route to trusted components.
+
+## Privacy considerations
+
+While the UI Activity Service does not expose any information about the nature
+of user activity, it does share whether activity has occurred recently within a
+preset time threshold via `fuchsia.input.interaction.Notifier`.
+
+Whether someone is at or using their device at a specific time is privacy
+impacting and sensitive, and therefore something we would not want untrusted
+components to have access to. Since this capability has to be routed through
+CFV2, it should be noted that the platform should only grant ability to observe
+activity that are trusted at the platform level, and that products can further
+route to trusted components.
+
+Timestamps specifically will not be made available to subscribers.
+
+## Testing
+
+Our testing strategy includes
+
+- Unit testing
+- Integration testing using existing tools
+
+## Documentation
+
+The service will include a README overview, and new FIDL protocols will be
+accompanied with comments inline.
+
+## Drawbacks
+
+### Input Pipeline Failures
+
+If the Input Pipeline panics or deadlocks, the UI Activity Service would also
+fail. In that case, subscribers to changes in idle state may want to default to
+active-state behavior rather than idle-state behavior, and should have other
+mitigating measures such as a maximum activity timeout to ensure critical
+processes are still able to occur. If the Input Pipeline is not able to receive
+or respond to Input Reports, there may be wider issues in the system.
+
+## Alternatives
+
+### Use Existing `fuchsia.ui.activity.Tracker` protocol
+
+This approach prefers creating a new
+`fuchsia.input.interaction.observation.Aggregator` protocol.
+
+- The current protocol is in the partner SDK and cannot be restricted to the
+  private SDK for use. Partner SDK inclusion will have additional CTS testing
+  requirements.
+- Aggregator more clearly indicates what the protocol is doing.
+- The current set of use cases does not require use of `StartOngoingActivity` or
+  `EndOngoingActivity`, and future needs may want to re-evaluate the current
+  pattern.
+
+### Modify Existing `fuchsia.ui.activity.Provider` protocol
+
+This approach prefers creating a new `fuchsia.input.interaction.Notifier`
+protocol.
+
+- The current protocol can be controlled by the
+  `fuchsia.ui.activity.control.Control` protocol.
+- The `WatchState` protocol would have to be migrated to the proposed
+  hanging-get pattern.
+- The `WatchState` protocol unnecessarily releases a timestamp that we do not
+  want clients to rely on without a concrete use case or intention for exposing
+  it.
+
+### Incorporate Other Activity Signals
+
+Certain signals may be considered activity in some user flows but not others.
+Accordingly, the correctness of how to interpret signals will vary depending on
+the needs of a given form factor or service.
+
+It is recommended that the `fuchsia.input.interaction.Notifier` protocol is
+used to consume UI activity state, and can be referenced in combination with
+other forms of activity (e.g., from audio, microphone, or camera) to determine
+some final user or system state as is relevant on a case-by-case basis. For
+example, a screensaver feature might consult just user input activity, whereas a
+lockscreen feature might consult both user input activity and user
+authentication state.
+
+### Support Configuring a Subset of Activity Types to Consider
+
+In the future, certain services may want to follow an activity state as
+determined by a subset of user input modes, e.g. recent touch but not recent
+keypress. This would introduce exponential combinations between the possible
+input modes.
+
+Given a current lack of interest in more elaborate features, we decided not to
+prematurely introduce this complexity.
+
+### Integrate with System Authentication for Fuchsia
+
+Both System Authentication and UI Activity Service care about some notion of
+user behavior, but with minimal practical overlap. For instance, System
+Authentication cares about a more granular set of user presence states,
+including user distance from the device and whether a user is an account owner.
+
+While it might be possible to track user activity in a way which is amenable to
+both use cases, it may not be necessary to proactively integrate these two
+systems without a driving use case in mind.
+
+## Future work
+
+### Allow Subscribers to Configure an Idle Time Threshold
+
+In the future, certain services may want to determine their own recency
+thresholds for user input interactions. While this RFC does not propose a
+solution, omitting this feature for simplicity of an initial implementation,
+the protocols propose may be extended to add support when concrete use cases
+arise.
+
+## Prior art and references
+
+- The [chrome.idle API][15] supports "active", "idle", and "locked" states.
+- Fuchsia's [activity notifier][16] in Root Presenter was removed due to unuse.
+
+[15]: https://developer.chrome.com/docs/extensions/reference/idle/
+[16]: https://fuchsia-review.googlesource.com/c/fuchsia/+/539865
diff --git a/docs/contribute/governance/rfcs/0174_scale_in_flatland.md b/docs/contribute/governance/rfcs/0174_scale_in_flatland.md
new file mode 100644
index 0000000..584d997
--- /dev/null
+++ b/docs/contribute/governance/rfcs/0174_scale_in_flatland.md
@@ -0,0 +1,336 @@
+<!-- Generated with `fx rfc` -->
+<!-- mdformat off(templates not supported) -->
+{% set rfcid = "RFC-0174" %}
+{% include "docs/contribute/governance/rfcs/_common/_rfc_header.md" %}
+# {{ rfc.name }}: {{ rfc.title }}
+{# Fuchsia RFCs use templates to display various fields from _rfcs.yaml. View the #}
+{# fully rendered RFCs at https://fuchsia.dev/fuchsia-src/contribute/governance/rfcs #}
+<!-- SET the `rfcid` VAR ABOVE. DO NOT EDIT ANYTHING ELSE ABOVE THIS LINE. -->
+
+<!-- mdformat on -->
+
+<!-- This should begin with an H2 element (for example, ## Summary).-->
+
+## Summary
+
+This RFC describes how graphical scaling operations are handled in
+[Flatland][flatland]. Scenic, as Fuchsia's system compositor, can handle
+upscaling or downscaling a Flatland instance's output through Flatland's
+SetScale() method. Floating-point scale factors may be used, with a guarantee
+that the resulting image avoids sub-pixel rendering. A Flatland instance only
+knows about the device pixel ratio value. In addition to the design of scale,
+this RFC defines some common terminology for referring to the pixel spaces.
+
+## Motivation
+
+Graphical scale operation is commonly needed for many UI features which
+Flatland, as a complete system compositor, should offer. Three of them are
+described in detail below.
+
+### Upsample
+
+Upsampling is increasing the rendered size of a Flatland instance or an Image.
+As an example, consider magnification. Magnification can be done by asking
+Scenic to upsample a Flatland client application's output.
+
+### Downsample
+
+Downsampling is decreasing the rendered size of a Flatland instance or an
+Image. As an example, consider
+[GNOME's Activities Overview](https://help.gnome.org/misc/release-notes/3.6/users-activities-overview.html.en){: .external}.
+A graphical system shell may want to make a Flatland client application continue
+rendering at the same resolution, but shrink their content output to a smaller
+rectangle by asking Scenic.
+
+### Device Pixel Ratio
+
+Device pixel ratio is defined so that the display manufacturers can upscale the
+resolutions of their devices while still being able to display content,
+designed for lower resolutions, at the same size on the new screens. As an
+example, 4K and 1080p screen versions of a laptop would look identical from a
+distance, all buttons and layout being the same size physically. Only when you
+get closer you would be able to tell that 4K is sharper, because it packs 4
+times the physical pixels in the same physical space as 1080p.
+
+## Stakeholders
+
+_Facilitator:_ neelsa@google.com
+
+_Reviewers:_
+
+* Input: neelsa@google.com, quiche@google.com
+* Accessibility: lucasradaelli@google.com
+* Scenic: jaeheon@google.com, dworsham@google.com, jjosh@google.com
+
+_Socialization:_
+
+A detailed version of this design was reviewed internally with Scenic, Input
+and Accessibility teams. Alternative solutions were also discussed.
+
+## Glossary
+
+* [Flatland][flatland]
+  * Scenic's new 2D composition API.
+* [Gfx][gfx]
+  * Scenic's old 3D composition API that is deprecated.
+* [View][view]
+  * A visual region of graphical content.
+  * It has a coordinate system, a bounding box, and a defined spatial
+    relationship to its ancestors, via the View Tree.
+* HiDPI
+  * High dots per inch.
+  * The industry term used for displays packing more pixels in the same
+    physical area. For example, screens with 240 dpi or higher are considered
+    HiDPI.
+* PP
+  * Physical pixels.
+  * Display's physical pixel count. i.e. if we have 4K and FHD screen
+    variants of the same laptop, 4K has 3840x2160 physical pixels and FHD has
+    1920x1080 physical pixels, so icons will appear the same size on both
+    laptop variants, just sharper on the HiDPI display
+* DIP
+  * Device independent pixels a.k.a. Logical display pixels a.k.a. density
+    independent pixels.
+  * Display's logical pixel count. i.e. if we have 4K and FHD screen variants
+    of the same laptop, both 4K and FHD most likely have 1920x1080 device
+    independent pixels.
+* DPR
+  * Device pixel ratio.
+  * The ratio between the display's physical pixels and the display's device
+    independent pixels.
+* LP
+  * Logical View pixels a.k.a. View's device independent pixels
+  * View's logical pixel count. Affects the content layout.
+* AP
+  * Allocation pixels.
+  * View's draw buffer allocation pixel count.
+* PSS
+  * Parent-set scales a.k.a. Accumulated scales.
+  * All parent SetScale() values multiplied. Ratio between a View's DIPs to the
+    display's DIPs.
+
+## Design
+
+To accommodate all the use cases described in the Motivation section, we define
+a set of relationships that establishes what values may be set by a parent view
+on their child (parent-defined), what values can be set only by Flatland
+(system-defined), and what values may be observed by a child (child-observed).
+
+__Device pixel ratio__ is system-defined uniformly for all views on a single
+display. DPR is child-observed.
+
+A child view's size is measured in __LPs__. This size is parent-defined and
+child-observed. A view has _optimal resolution_ if the size and alignment of
+the view's LPs precisely matches the display's __DIPs__ for the region used to
+render the view. A view should act as if it has optimal resolution, where
+LP=DIP in size and alignment. However, the optimal resolution is *not*
+child-observed, and can be intentionally broken by a parent view (using Scale,
+see below) to allow features such as Magnification or GNOME's Activities
+Overview.
+
+Scale is introduced as a floating-point value. It is parent-defined but not
+child-observed! Parent-set scales for a child view is the multiplication of all
+scales introduced by all ancestor views of that child. __PSS__ describes the
+ratio from a child view's LPs to the display's DIPs. By hiding the PSS,
+Flatland retains control over how many __physical pixels__ a child view
+occupies on the display, without forcing the child to re-allocate or re-present
+when upsampling or downsampling.
+
+We make a further distinction with allocation pixels, which is the size of the
+buffer used to present content to display. This is a client implementation
+detail, but to remove confusion, we describe its relationship to other pixel
+spaces:
+
+* __HiDPI__-aware clients want to look sharp and can control the size of their
+  content. Their __Allocation pixels__ is Logical View pixels multiplied with
+  DPR.
+* DPR-oblivious clients want to present content in just one size. They ignore
+  DPR, and their Allocation pixels are equal to their Logical View pixels.
+* Custom-allocation clients have content that enforces their Allocation pixels,
+  i.e. video player or WebCanvas. These clients will define their own
+  relationship between Allocation pixels and Logical View pixels.
+
+Flatland Mappings:
+
+* PP<sub>Display</sub> = DIP<sub>Display</sub>* DPR
+* PP<sub>View</sub> = LP<sub>View</sub> * PSS * DPR
+* AP<sub>View</sub> to LP<sub>View</sub> is actually the client's own business.
+  * AP<sub>View</sub> = LP<sub>View</sub> * DPR is recommended for HiDPI-aware
+    clients.
+
+The pixel space relationships described above are illustrated in Figure 1.
+
+**Figure 1 - Flatland** ![This figure presents the pixel space relationships.](
+    resources/0174_scale_in_flatland/scale_diagram.svg "Figure 1")
+
+The proposed changes are represented by the following FIDL snippet:
+
+```
+type LayoutInfo = table {
+    /// The layout size of a View in logical pixels, defined by the parent's call to
+    /// [`SetViewportProperties`]. Clients should re-layout their content when this value changes.
+    1: logical_size fuchsia.math.SizeU;
+
+    /// The ratio from physical display pixels to the display's device independent pixels.
+    /// Clients should not necessarily re-layout their content when this value changes. Clients may
+    /// accommodate by reallocating their Image buffers that are adjusted by [`device_pixel_ratio`].
+    /// HiDPI-aware clients that want to avoid sampling artifacts should render onto a buffer with
+    /// at least the size round([`logical_size`] * [`device_pixel_ratio`]).
+    /// Note that rounding is not C-style float-to-int truncation. The floating-point product should
+    /// be converted to the nearest integer.
+    2: device_pixel_ratio fuchsia.math.VecF;
+};
+
+protocol Flatland {
+
+/// Sets the scale on a Transform. The order of geometric attribute application is addressed
+/// in the documentation for SetTranslation().
+/// Note that if there is a Viewport set on this Transform, the child Flatland will not be notified
+/// about the changes made here. This method should only be used if the intention is to upsample or
+/// downsample the Viewport's output. Use [`SetViewportProperties`] if the intention is to resize or
+/// relayout the Viewport.
+SetScale(struct {
+        transform_id TransformId;
+        scale fuchsia.math.VecF;
+    });
+}
+```
+
+DPR is applied as a scale to each Image before Scenic renders or sends them to
+the display. If a client wants to support HiDPI, they are expected to layout
+their content using the given LP, but allocate larger buffers by using the
+reported DPR to reverse the effects of Scenic's scaling.
+
+PSS is not communicated the the children. That means upscaling through Flatland
+may cause blurry artifacts. However, this is preferred to forcing the client to
+re-allocate and re-present, for example because buffer size would grow by the
+scale factor and risk OOMs.
+
+We may end up with non-integer pixels with float scales. If we have a
+consistent way of attaching to a nearby integer pixel value(round), we
+shouldn't end up with artifacts.
+
+Flatland clients may want to go from logical pixels to physical pixels and that
+requires knowing PSS in addition to DPR. However, this is only necessary for
+input, so this design pushes this conversion information to
+[`fuchsia.ui.pointer`](/sdk/fidl/fuchsia.ui.pointer) API.
+
+The clients may respond to DPR changes at their own pace using this API. They
+will all be signaled about these changes at the same time, so we don't have a
+cascading delay jank pattern.
+
+The client should always expect to work with a single DPR value. When we
+support multiple displays in the future, we can report a single DPR value
+coming multiple displays, or the DPR value of the display they are on.
+
+### Contrast with Legacy
+
+Note that in Gfx(Legacy 3D API), DPR and PSS was multiplied into a single value
+and reported to the child. Hence, scale manipulation caused allocation
+side-effects in client code, causing OOMs and other unintended side effects,
+including large-scale architectural workarounds to achieve effects like
+Magnification and confusion around DPR. This design for Flatland diverges by
+suggesting that the child only needs to know about DPR for graphics purposes.
+
+Gfx Mappings:
+
+* PP<sub>View</sub> = LP<sub>View</sub> * pixel_scale
+  * pixel_scale = PSS * DPR
+  * pixel_scale is the only metric Gfx returns.
+
+## Implementation
+
+This design involves changes in
+[`fuchsia.ui.composition/Flatland`](/sdk/fidl/fuchsia.ui.composition/flatland.fidl)
+API. The implementation will be done in three main steps:
+
+* Remove the usage of `pixel_scale` field, which is being deprecated, in-tree
+  and out-of-tree.
+* Complete in-tree changes. Send DPR information based on what is reported from
+  the display.
+* Change in-tree and out-of-tree client code to make use of DPR information to
+  adjust AP.
+
+## Performance
+
+This proposal reduces the memory usage of DPI-aware clients in comparison to
+Gfx API. In Gfx, DPR and PSS was multiplied into a single value and reported to
+the child. DPI-aware client would respond to the accumulation of all scales.
+As an example, if they were scaled by 5 by their parent and DPR of 2 applied,
+they would have received a `pixel_scale` of 10 and allocated a 10 times larger
+buffer. This proposal distinguishes DPR and PSS, so the unnecesssary allocations
+are gone.
+
+This proposal reduces the expectations of re-allocate and re-layout from the
+clients. We can implement smoother upsample and downsample operations, because
+we no longer rely on the Flatland client to repond to scale changes.
+
+## Security considerations
+
+This proposal does not affect the security model of Flatland API. The API
+guarantees around other graphical operations, such as clip limiting the input
+receiving area of a View, still apply to the scaled content.
+
+This proposal reduces the extent of information passed to a Flatland view. In
+Gfx, DPR and PSS was multiplied into a single value and reported to the child.
+This proposal only reports DPR information. A child Flatland instance does not
+have an indication of how their parent Flatland instance decides to present
+their content, scaled or not, and cannot react to scaling changes.
+
+## Privacy considerations
+
+This proposal suggests sending DPR information to Flatland clients. Device
+Pixel Ratio is derived from the physical and technical properties of the
+display unit. Although a very specific information, this property and ratio is
+not unique and may be common across multiple hardware, so it isn't very useful
+for fingerprinting. Furthermore, DPR is absolutely necessary for preparing
+quality graphical output.
+
+## Testing
+
+Flatland API has been tested in a layered approach, which scale feature will
+follow:
+
+* Unit tests in Scenic codebase.
+* UI Integration tests, in [/src/ui/tests](/src/ui/tests), that exercise the
+  contracts around Flatland APIs.
+* System tests that capture output pixels from devices with DPR values
+  different than one.
+* Runtimes such as Chromium and Flutter write integration tests to test against
+  usage of SetScale() and different DPR values.
+
+## Documentation
+
+Some of the [Flatland][flatland] documentation will be updated following this
+RFC to describe the HiDPI-aware client behavior and how scaling works.
+
+## Drawbacks, alternatives, and unknowns
+
+There isn't a significant cost for implementing this proposal. This proposal
+suggests a DPR solution that will work no matter which display hardware is
+used. It may also be extended for multi-display use cases.
+
+There were a couple alternatives considered for this design.
+
+* DPR was added as a configurable static value in some Chromium configurations.
+  That is clearly not scalable across different applications.
+* To avoid rounding issues, we considered only allowing integer scale factors.
+  However, there are exceptions to this in some DPR configurations.
+* We could fall into a slow sub-pixel rendering path when we have
+  floating-point values. However, there isn't a compelling reason to do so, and
+  it would hinder performance by not allowing performance passing buffers
+  directly to the display.
+
+## Prior art and references
+
+* [RFC-0162:Flatland](/docs/contribute/governance/rfcs/0162_flatland.md)
+* [RFC-0147:View system](/docs/contribute/governance/rfcs/0147_view_system.md)
+* [RFC-0166:One UI stack](/docs/contribute/governance/rfcs/0166_ui_stack.md)
+* Flatland share some architectural similarities with the Wayland protocol,
+which handles
+[High density surfaces](https://wayland-book.com/surfaces-in-depth/hidpi.html){: .external}
+in a similar way to the proposed design.
+
+[flatland]: /sdk/fidl/fuchsia.ui.composition/flatland.fidl
+[gfx]: /sdk/fidl/fuchsia.ui.scenic/session.fidl
+[view]: /docs/contribute/governance/rfcs/0147_view_system.md
diff --git a/docs/contribute/governance/rfcs/_rfcs.yaml b/docs/contribute/governance/rfcs/_rfcs.yaml
index 38a476e..05575cb 100644
--- a/docs/contribute/governance/rfcs/_rfcs.yaml
+++ b/docs/contribute/governance/rfcs/_rfcs.yaml
@@ -2219,6 +2219,19 @@
   submitted: '2022-05-19'
   reviewed: '2022-06-23'
 
+- name: 'RFC-0172'
+  title: 'UI Activity Service'
+  short_description: 'Proposal for a service for determining user input active or idle state and notifying clients.'
+  authors: ['carolineliu@google.com']
+  file: '0172_ui_activity_service.md'
+  area: ['HCI', 'UI']
+  issue: ['86773']
+  gerrit_change_id: ['686247']
+  status: 'Accepted'
+  reviewers: ['jsankey@google.com', 'neelsa@google.com', 'sanjay@google.com','quiche@google.com', 'wittrock@google.com']
+  submitted: '2022-06-02'
+  reviewed: '2022-06-30'
+
 - name: 'RFC-0173'
   title: 'Structured Configuration in Component Framework APIs'
   short_description: 'Define the publicly-facing implementation of basic Structured Configuration features.'
@@ -2232,3 +2245,16 @@
   consulted: ['hjfreyer@google.com', 'xbhatnag@google.com', 'aaronwood@google.com', 'mcgrathr@google.com', 'shayba@google.com']
   submitted: '2022-04-11'
   reviewed: '2022-06-30'
+
+- name: 'RFC-0174'
+  title: 'Scale in Flatland'
+  short_description: 'Define how graphical scaling operations are handled in Flatland.'
+  authors: ['emircan@google.com']
+  file: '0174_scale_in_flatland.md'
+  area: ['UI']
+  issue: ['99312']
+  gerrit_change_id: ['686572']
+  status: 'Accepted'
+  reviewers: ['dworsham@google.com', 'jaeheon@google.com', 'jjosh@google.com', 'lucasradaelli@google.com', 'neelsa@google.com', 'quiche@google.com']
+  submitted: '2022-06-02'
+  reviewed: '2022-07-01'
diff --git a/docs/contribute/governance/rfcs/_toc.yaml b/docs/contribute/governance/rfcs/_toc.yaml
index 87a92e3e..607ba3b 100644
--- a/docs/contribute/governance/rfcs/_toc.yaml
+++ b/docs/contribute/governance/rfcs/_toc.yaml
@@ -356,5 +356,10 @@
     path: /docs/contribute/governance/rfcs/0170_remove_binary_images_from_the_update_package.md
   - title: "RFC-0171: Improved diagnostics routing"
     path: /docs/contribute/governance/rfcs/0171_improved_diagnostics_routing.md
+  - title: "RFC-0172: UI Activity Service"
+    path: /docs/contribute/governance/rfcs/0172_ui_activity_service.md
   - title: "RFC-0173: Structured Configuration in Component Framework APIs"
     path: /docs/contribute/governance/rfcs/0173_structured_config_cf_apis.md
+  - title: "RFC-0174: Scale in Flatland"
+    path: /docs/contribute/governance/rfcs/0174_scale_in_flatland.md
+
diff --git a/docs/contribute/governance/rfcs/resources/0174_scale_in_flatland/scale_diagram.svg b/docs/contribute/governance/rfcs/resources/0174_scale_in_flatland/scale_diagram.svg
new file mode 100644
index 0000000..8943a91
--- /dev/null
+++ b/docs/contribute/governance/rfcs/resources/0174_scale_in_flatland/scale_diagram.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:lucid="lucid" width="1160" height="700"><g transform="translate(-20 0)" lucid:page-tab-id="0_0"><path d="M0 0h1760v1360H0z" fill="#fff"/><path d="M180 226a6 6 0 0 1 6-6h148a6 6 0 0 1 6 6v108a6 6 0 0 1-6 6H186a6 6 0 0 1-6-6z" stroke="#3a414a" fill="#f2f2ff"/><use xlink:href="#a" transform="matrix(1,0,0,1,192,232) translate(25.407407407407405 53.77777777777778)"/><path d="M140 164.33a6 6 0 0 1 6-6h228a6 6 0 0 1 6 6v51.34a6 6 0 0 1-6 6H146a6 6 0 0 1-6-6z" stroke="#000" stroke-opacity="0" fill="#fff" fill-opacity="0"/><use xlink:href="#b" transform="matrix(1,0,0,1,145,163.33333333333334) translate(30.98765432098766 17.77777777777778)"/><use xlink:href="#c" transform="matrix(1,0,0,1,145,163.33333333333334) translate(65.06172839506173 17.77777777777778)"/><use xlink:href="#d" transform="matrix(1,0,0,1,145,163.33333333333334) translate(148.82716049382714 17.77777777777778)"/><use xlink:href="#e" transform="matrix(1,0,0,1,145,163.33333333333334) translate(83.54938271604938 44.44444444444444)"/><path d="M500 186a6 6 0 0 1 6-6h188a6 6 0 0 1 6 6v148a6 6 0 0 1-6 6H506a6 6 0 0 1-6-6z" stroke="#3a414a" fill="#f2f2ff"/><use xlink:href="#f" transform="matrix(1,0,0,1,512,192) translate(45.407407407407405 73.02777777777777)"/><path d="M300 226a6 6 0 0 1 6-6h228a6 6 0 0 1 6 6v48a6 6 0 0 1-6 6H306a6 6 0 0 1-6-6z" stroke="#000" stroke-opacity="0" fill="#fff" fill-opacity="0"/><use xlink:href="#g" transform="matrix(1,0,0,1,305,225) translate(57.067901234567884 30.27777777777778)"/><path d="M340.96 280h142.2" stroke="#3a414a" fill="none"/><path d="M340.98 280.5h-.52v-1h.52z" stroke="#3a414a" stroke-width=".05" fill="#3a414a"/><path d="M497.93 280l-14.27 4.64v-9.28z" stroke="#3a414a" fill="#3a414a"/><path d="M900 86a6 6 0 0 1 6-6h248a6 6 0 0 1 6 6v188a6 6 0 0 1-6 6H906a6 6 0 0 1-6-6z" stroke="#3a414a" fill="#fff0f0"/><use xlink:href="#h" transform="matrix(1,0,0,1,912,92) translate(75.4074074074074 94.77777777777777)"/><path d="M900 26a6 6 0 0 1 6-6h228a6 6 0 0 1 6 6v48a6 6 0 0 1-6 6H906a6 6 0 0 1-6-6z" stroke="#000" stroke-opacity="0" fill="#fff" fill-opacity="0"/><use xlink:href="#i" transform="matrix(1,0,0,1,905,25) translate(17.746913580246925 30.27777777777778)"/><use xlink:href="#j" transform="matrix(1,0,0,1,905,25) translate(53.11728395061729 30.27777777777778)"/><use xlink:href="#e" transform="matrix(1,0,0,1,905,25) translate(149.35185185185185 30.27777777777778)"/><path d="M480 124.33a6 6 0 0 1 6-6h228a6 6 0 0 1 6 6v51.34a6 6 0 0 1-6 6H486a6 6 0 0 1-6-6z" stroke="#000" stroke-opacity="0" fill="#fff" fill-opacity="0"/><use xlink:href="#k" transform="matrix(1,0,0,1,485,123.33333333333334) translate(57.87037037037037 17.77777777777778)"/><use xlink:href="#l" transform="matrix(1,0,0,1,485,123.33333333333334) translate(100.58641975308642 17.77777777777778)"/><use xlink:href="#m" transform="matrix(1,0,0,1,485,123.33333333333334) translate(13.981481481481495 44.44444444444444)"/><use xlink:href="#n" transform="matrix(1,0,0,1,485,123.33333333333334) translate(151.820987654321 44.44444444444444)"/><path d="M40 406a6 6 0 0 1 6-6h148a6 6 0 0 1 6 6v108a6 6 0 0 1-6 6H46a6 6 0 0 1-6-6z" stroke="#3a414a" fill="#fff3d9"/><use xlink:href="#a" transform="matrix(1,0,0,1,52,412) translate(25.407407407407405 53.77777777777778)"/><path d="M700.93 259.8l183.4-73.7" stroke="#3a414a" fill="none"/><path d="M701.13 260.27l-.67.27v-1.08l.3-.12z" stroke="#3a414a" stroke-width=".05" fill="#3a414a"/><path d="M898.04 180.6l-11.5 9.62-3.47-8.6z" stroke="#3a414a" fill="#3a414a"/><path d="M676.64 209.72a6 6 0 0 1 3.33-7.8l211.57-85a6 6 0 0 1 7.8 3.33l17.9 44.54a6 6 0 0 1-3.34 7.8l-211.56 85a6 6 0 0 1-7.8-3.34z" stroke="#000" stroke-opacity="0" fill="#fff" fill-opacity="0"/><use xlink:href="#o" transform="matrix(0.9279126205765748,-0.37279775827479655,0.37279775827479655,0.9279126205765748,680.9077829172483,206.92768488923923) translate(50.33950617283949 30.27777777777778)"/><path d="M910 236a6 6 0 0 1 6-6h228a6 6 0 0 1 6 6v48a6 6 0 0 1-6 6H916a6 6 0 0 1-6-6z" stroke="#000" stroke-opacity="0" fill="#fff" fill-opacity="0"/><use xlink:href="#p" transform="matrix(1,0,0,1,915,235) translate(64.38271604938271 30.27777777777778)"/><use xlink:href="#q" transform="matrix(1,0,0,1,915,235) translate(138.45679012345678 30.27777777777778)"/><path d="M700.87 260.3l185.27 130.28" stroke="#3a414a" fill="none"/><path d="M701.17 259.9l-.57.8-.14-.1v-1.2z" stroke="#3a414a" stroke-width=".05" fill="#3a414a"/><path d="M898.2 399.07l-14.32-4.42 5.33-7.58z" stroke="#3a414a" fill="#3a414a"/><path d="M709.95 284.66a6 6 0 0 1 8.44-.88l176.94 143.78a6 6 0 0 1 .88 8.44l-30.27 37.25a6 6 0 0 1-8.44.88L680.57 330.35a6 6 0 0 1-.88-8.44z" stroke="#000" stroke-opacity="0" fill="#fff" fill-opacity="0"/><use xlink:href="#r" transform="matrix(0.7761062442738899,0.6306021706266773,-0.6306021706266773,0.7761062442738899,714.4620217425024,287.03354207450286) translate(50.33950617283949 30.27777777777778)"/><path d="M900 326a6 6 0 0 1 6-6h188a6 6 0 0 1 6 6v148a6 6 0 0 1-6 6H906a6 6 0 0 1-6-6z" stroke="#3a414a" fill="#fff3d9"/><use xlink:href="#f" transform="matrix(1,0,0,1,912,332) translate(45.407407407407405 73.02777777777777)"/><path d="M919.47 437.9H1080v63.34H919.47z" fill="#fff" fill-opacity="0"/><use xlink:href="#p" transform="matrix(1,0,0,1,924.4711254578572,442.910895606836) translate(6.364197530864203 17.77777777777778)"/><use xlink:href="#s" transform="matrix(1,0,0,1,924.4711254578572,442.910895606836) translate(80.43827160493828 17.77777777777778)"/><path d="M259.54 340.66l-124.44 52.5" stroke="#3a414a" fill="none"/><path d="M259.72 341.12l-.28-.66h1.85z" stroke="#3a414a" stroke-width=".05" fill="#3a414a"/><path d="M121.5 398.9l11.33-9.8 3.6 8.53z" stroke="#3a414a" fill="#3a414a"/><path d="M260 626a6 6 0 0 1 6-6h228a6 6 0 0 1 6 6v48a6 6 0 0 1-6 6H266a6 6 0 0 1-6-6z" stroke="#000" stroke-opacity="0" fill="#fff" fill-opacity="0"/><use xlink:href="#t" transform="matrix(1,0,0,1,265,625) translate(8.641975308641975 30.27777777777778)"/><use xlink:href="#u" transform="matrix(1,0,0,1,265,625) translate(45.18518518518518 30.27777777777778)"/><use xlink:href="#e" transform="matrix(1,0,0,1,265,625) translate(158.45679012345678 30.27777777777778)"/><path d="M40 480h160v63.33H40z" fill="#fff" fill-opacity="0"/><use xlink:href="#p" transform="matrix(1,0,0,1,45,485) translate(5.864197530864203 17.77777777777778)"/><use xlink:href="#s" transform="matrix(1,0,0,1,45,485) translate(79.93827160493828 17.77777777777778)"/><path d="M220 406a6 6 0 0 1 6-6h168a6 6 0 0 1 6 6v128a6 6 0 0 1-6 6H226a6 6 0 0 1-6-6z" stroke="#3a414a" fill="#fff0f0"/><use xlink:href="#v" transform="matrix(1,0,0,1,232,412) translate(35.407407407407405 61.27777777777778)"/><path d="M190 506a6 6 0 0 1 6-6h228a6 6 0 0 1 6 6v28a6 6 0 0 1-6 6H196a6 6 0 0 1-6-6z" stroke="#000" stroke-opacity="0" fill="#fff" fill-opacity="0"/><use xlink:href="#p" transform="matrix(1,0,0,1,195.00000000000006,505) translate(64.38271604938271 19.65277777777778)"/><use xlink:href="#q" transform="matrix(1,0,0,1,195.00000000000006,505) translate(138.45679012345678 19.65277777777778)"/><path d="M440 406a6 6 0 0 1 6-6h188a6 6 0 0 1 6 6v148a6 6 0 0 1-6 6H446a6 6 0 0 1-6-6z" stroke="#3a414a" fill="#e3fae3"/><g><use xlink:href="#w" transform="matrix(1,0,0,1,452,412) translate(39.23456790123456 73.02777777777777)"/></g><path d="M260.35 340.82l47.13 47.13" stroke="#3a414a" fill="none"/><path d="M260.72 340.47l-.7.7-.73-.7h1.4z" stroke="#3a414a" stroke-width=".05" fill="#3a414a"/><path d="M317.93 398.4l-13.37-6.82 6.56-6.56z" stroke="#3a414a" fill="#3a414a"/><path d="M260.5 340.57l263.47 55.58" stroke="#3a414a" fill="none"/><path d="M260.4 341.06l-2.82-.6h2.94z" stroke="#3a414a" stroke-width=".05" fill="#3a414a"/><path d="M538.42 399.2l-14.92 1.6 1.92-9.08z" stroke="#3a414a" fill="#3a414a"/><g fill="none"><path d="M394 580a6 6 0 0 1 6 6v8a6 6 0 0 1-6 6H46a6 6 0 0 1-6-6v-8a6 6 0 0 1 6-6z"/><path d="M40 580c0 5.52 4.48 10 10 10h160c5.52 0 10 4.48 10 10 0-5.52 4.48-10 10-10h160c5.52 0 10-4.48 10-10" stroke="#3a414a"/></g><path d="M100 586a6 6 0 0 1 6-6h228a6 6 0 0 1 6 6v48a6 6 0 0 1-6 6H106a6 6 0 0 1-6-6z" stroke="#000" stroke-opacity="0" fill="#fff" fill-opacity="0"/><g><use xlink:href="#x" transform="matrix(1,0,0,1,105,585) translate(52.222222222222214 30.27777777777778)"/></g><g fill="none"><path d="M634 580a6 6 0 0 1 6 6v48a6 6 0 0 1-6 6H446a6 6 0 0 1-6-6v-48a6 6 0 0 1 6-6z"/><path d="M440 580c0 5.52 4.48 10 10 10h80c5.52 0 10 4.48 10 10 0-5.52 4.48-10 10-10h80c5.52 0 10-4.48 10-10" stroke="#3a414a"/></g><path d="M420 586a6 6 0 0 1 6-6h228a6 6 0 0 1 6 6v48a6 6 0 0 1-6 6H426a6 6 0 0 1-6-6z" stroke="#000" stroke-opacity="0" fill="#fff" fill-opacity="0"/><g><use xlink:href="#y" transform="matrix(1,0,0,1,425,585) translate(26.29629629629632 30.27777777777778)"/></g><defs><path fill="#3a414a" d="M27 0v-27h64v-190l-56 39v-29l58-41h29v221h61V0H27" id="z"/><path fill="#3a414a" d="M101-251c68 0 85 55 85 127S166 4 100 4C33 4 14-52 14-124c0-73 17-127 87-127zm-1 229c47 0 54-49 54-102s-4-102-53-102c-51 0-55 48-55 102 0 53 5 102 54 102" id="A"/><path fill="#3a414a" d="M141 0L90-78 38 0H4l68-98-65-92h35l48 74 47-74h35l-64 92 68 98h-35" id="B"/><g id="a"><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,0,0)" xlink:href="#z"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,12.345679012345679,0)" xlink:href="#A"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,24.691358024691358,0)" xlink:href="#A"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,37.03703703703704,0)" xlink:href="#B"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,48.14814814814815,0)" xlink:href="#z"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,60.49382716049383,0)" xlink:href="#A"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,72.8395061728395,0)" xlink:href="#A"/></g><path fill="#3a414a" d="M24 0v-248h52v208h133V0H24" id="C"/><path fill="#3a414a" d="M24-248c93 1 206-16 204 79-1 75-69 88-152 82V0H24v-248zm52 121c47 0 100 7 100-41 0-47-54-39-100-39v80" id="D"/><g id="b"><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,0,0)" xlink:href="#C"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,13.518518518518517,0)" xlink:href="#D"/></g><path fill="#3a414a" d="M110-194c64 0 96 36 96 99 0 64-35 99-97 99-61 0-95-36-95-99 0-62 34-99 96-99zm-1 164c35 0 45-28 45-65 0-40-10-65-43-65-34 0-45 26-45 65 0 36 10 65 43 65" id="E"/><path fill="#3a414a" d="M195-6C206 82 75 100 31 46c-4-6-6-13-8-21l49-6c3 16 16 24 34 25 40 0 42-37 40-79-11 22-30 35-61 35-53 0-70-43-70-97 0-56 18-96 73-97 30 0 46 14 59 34l2-30h47zm-90-29c32 0 41-27 41-63 0-35-9-62-40-62-32 0-39 29-40 63 0 36 9 62 39 62" id="F"/><path fill="#3a414a" d="M25-224v-37h50v37H25zM25 0v-190h50V0H25" id="G"/><path fill="#3a414a" d="M190-63c-7 42-38 67-86 67-59 0-84-38-90-98-12-110 154-137 174-36l-49 2c-2-19-15-32-35-32-30 0-35 28-38 64-6 74 65 87 74 30" id="H"/><path fill="#3a414a" d="M133-34C117-15 103 5 69 4 32 3 11-16 11-54c-1-60 55-63 116-61 1-26-3-47-28-47-18 1-26 9-28 27l-52-2c7-38 36-58 82-57s74 22 75 68l1 82c-1 14 12 18 25 15v27c-30 8-71 5-69-32zm-48 3c29 0 43-24 42-57-32 0-66-3-65 30 0 17 8 27 23 27" id="I"/><path fill="#3a414a" d="M25 0v-261h50V0H25" id="J"/><g id="c"><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,0,0)" xlink:href="#C"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,13.518518518518517,0)" xlink:href="#E"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,27.037037037037035,0)" xlink:href="#F"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,40.55555555555555,0)" xlink:href="#G"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,46.72839506172839,0)" xlink:href="#H"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,59.07407407407407,0)" xlink:href="#I"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,71.41975308641975,0)" xlink:href="#J"/></g><path fill="#3a414a" d="M147 0H94L2-248h55l64 206c17-72 42-137 63-206h54" id="K"/><path fill="#3a414a" d="M185-48c-13 30-37 53-82 52C43 2 14-33 14-96s30-98 90-98c62 0 83 45 84 108H66c0 31 8 55 39 56 18 0 30-7 34-22zm-45-69c5-46-57-63-70-21-2 6-4 13-4 21h74" id="L"/><path fill="#3a414a" d="M231 0h-52l-39-155L100 0H48L-1-190h46L77-45c9-52 24-97 36-145h53l37 145 32-145h46" id="M"/><g id="d"><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,0,0)" xlink:href="#K"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,14.382716049382715,0)" xlink:href="#G"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,20.555555555555554,0)" xlink:href="#L"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,32.901234567901234,0)" xlink:href="#M"/></g><path fill="#3a414a" d="M135-194c53 0 70 44 70 98 0 56-19 98-73 100-31 1-45-17-59-34 3 33 2 69 2 105H25l-1-265h48c2 10 0 23 3 31 11-24 29-35 60-35zM114-30c33 0 39-31 40-66 0-38-9-64-40-64-56 0-55 130 0 130" id="N"/><path fill="#3a414a" d="M144 0l-44-69L55 0H2l70-98-66-92h53l41 62 40-62h54l-67 91 71 99h-54" id="O"/><path fill="#3a414a" d="M137-138c1-29-70-34-71-4 15 46 118 7 119 86 1 83-164 76-172 9l43-7c4 19 20 25 44 25 33 8 57-30 24-41C81-84 22-81 20-136c-2-80 154-74 161-7" id="P"/><g id="e"><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,0,0)" xlink:href="#N"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,13.518518518518517,0)" xlink:href="#G"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,19.691358024691358,0)" xlink:href="#O"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,32.03703703703704,0)" xlink:href="#L"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,44.382716049382715,0)" xlink:href="#J"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,50.55555555555556,0)" xlink:href="#P"/></g><path fill="#3a414a" d="M126-127c33 6 58 20 58 59 0 88-139 92-164 29-3-8-5-16-6-25l32-3c6 27 21 44 54 44 32 0 52-15 52-46 0-38-36-46-79-43v-28c39 1 72-4 72-42 0-27-17-43-46-43-28 0-47 15-49 41l-32-3c6-42 35-63 81-64 48-1 79 21 79 65 0 36-21 52-52 59" id="Q"/><g id="f"><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,0,0)" xlink:href="#Q"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,12.345679012345679,0)" xlink:href="#A"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,24.691358024691358,0)" xlink:href="#A"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,37.03703703703704,0)" xlink:href="#B"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,48.14814814814815,0)" xlink:href="#Q"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,60.49382716049383,0)" xlink:href="#A"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,72.8395061728395,0)" xlink:href="#A"/></g><path fill="#3a414a" d="M194-120c59 52 2 134-86 124-53-6-90-18-98-63l31-7c8 30 29 43 70 43 46 0 84-17 71-62-32-43-135-20-137-98-3-89 178-90 191-16l-30 9c-8-48-127-53-127 5 0 53 81 35 115 65" id="R"/><path fill="#3a414a" d="M111-194c62-3 86 47 72 106H45c-7 38 6 69 45 68 27-1 43-14 53-32l24 11C152-15 129 4 87 4 38 3 12-23 12-71c0-70 32-119 99-123zm44 81c14-66-71-72-95-28-4 8-8 17-11 28h106" id="S"/><path fill="#3a414a" d="M51-54c-9 22 5 41 31 30L79-1C43 14 10-4 19-52l22-115H19l5-23h22l19-43h21l-9 43h35l-4 23H73" id="T"/><path fill="#3a414a" d="M44-68c0 29 11 47 38 47 30 0 42-19 51-41l28 9C148-21 126 4 82 4 31 4 10-29 12-85c3-77 74-140 146-93 12 8 15 23 18 40l-31 5c-1-22-13-36-36-36-52 0-65 49-65 101" id="U"/><path fill="#3a414a" d="M165-48c-4 18 1 34 23 27l-3 20c-29 8-62 0-52-35h-2C116-14 99 4 63 4 30 4 8-16 8-49c0-68 71-67 138-67 10-26 0-56-31-54-26 1-42 9-47 31l-32-5c8-67 160-71 144 15-5 28-9 54-15 81zM42-50c3 52 80 24 89-6 7-12 7-24 11-38-47 1-103-4-100 44" id="V"/><path fill="#3a414a" d="M6 0l50-261h32L37 0H6" id="W"/><path fill="#3a414a" d="M17-49c1-102 45-165 103-212h30C92-210 49-145 49-46 49 4 63 43 85 75H54C30 43 18 2 17-49" id="X"/><path fill="#3a414a" d="M148-71c0-36-31-44-71-41l5-28c46 3 81-6 81-50 0-23-15-35-39-35-30 0-49 17-56 41l-32-3c12-39 42-63 90-64 43 0 69 19 71 60 1 43-30 58-65 66 27 5 49 22 49 53 0 85-133 99-164 35-4-7-7-15-9-22l29-9c8 25 25 45 58 45 34 1 53-17 53-48" id="Y"/><path fill="#3a414a" d="M98-137C97-35 54 29-4 75h-31C23 23 67-41 67-140c0-50-15-89-37-121h31c24 32 36 73 37 124" id="Z"/><g id="g"><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,0,0)" xlink:href="#R"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,14.814814814814813,0)" xlink:href="#S"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,27.160493827160494,0)" xlink:href="#T"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,33.333333333333336,0)" xlink:href="#R"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,48.14814814814815,0)" xlink:href="#U"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,59.25925925925927,0)" xlink:href="#V"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,71.60493827160495,0)" xlink:href="#W"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,76.4814814814815,0)" xlink:href="#S"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,88.82716049382718,0)" xlink:href="#X"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,96.17283950617286,0)" xlink:href="#Y"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,108.51851851851855,0)" xlink:href="#Z"/></g><path fill="#3a414a" d="M110-160c48 1 74 30 74 79 0 53-28 85-80 85-65 0-83-55-86-122-5-90 50-162 133-122 14 7 22 21 27 39l-31 6c-5-40-67-38-82-6-9 19-15 44-15 74 11-20 30-34 60-33zm-7 138c34 0 49-23 49-58s-16-56-50-56c-29 0-50 16-49 49 1 36 15 65 50 65" id="aa"/><g id="h"><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,0,0)" xlink:href="#aa"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,12.345679012345679,0)" xlink:href="#A"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,24.691358024691358,0)" xlink:href="#A"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,37.03703703703704,0)" xlink:href="#B"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,48.14814814814815,0)" xlink:href="#aa"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,60.49382716049383,0)" xlink:href="#A"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,72.8395061728395,0)" xlink:href="#A"/></g><g id="i"><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,0,0)" xlink:href="#D"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,14.814814814814813,0)" xlink:href="#D"/></g><path fill="#3a414a" d="M114-157C55-157 80-60 75 0H25v-261h50l-1 109c12-26 28-41 61-42 86-1 58 113 63 194h-50c-7-57 23-157-34-157" id="ab"/><path fill="#3a414a" d="M123 10C108 53 80 86 19 72V37c35 8 53-11 59-39L3-190h52l48 148c12-52 28-100 44-148h51" id="ac"/><g id="j"><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,0,0)" xlink:href="#D"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,14.814814814814813,0)" xlink:href="#ab"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,28.33333333333333,0)" xlink:href="#ac"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,40.679012345679006,0)" xlink:href="#P"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,53.02469135802468,0)" xlink:href="#G"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,59.197530864197525,0)" xlink:href="#H"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,71.5432098765432,0)" xlink:href="#I"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,83.88888888888889,0)" xlink:href="#J"/></g><path fill="#3a414a" d="M24-248c120-7 223 5 221 122C244-46 201 0 124 0H24v-248zM76-40c74 7 117-18 117-86 0-67-45-88-117-82v168" id="ad"/><path fill="#3a414a" d="M24 0v-248h52V0H24" id="ae"/><g id="k"><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,0,0)" xlink:href="#ad"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,15.987654320987653,0)" xlink:href="#ae"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,22.160493827160494,0)" xlink:href="#D"/></g><path fill="#3a414a" d="M128 0H69L1-190h53L99-40l48-150h52" id="af"/><g id="l"><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,0,0)" xlink:href="#ad"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,15.987654320987653,0)" xlink:href="#L"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,28.333333333333332,0)" xlink:href="#af"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,40.67901234567901,0)" xlink:href="#G"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,46.851851851851855,0)" xlink:href="#H"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,59.19753086419753,0)" xlink:href="#L"/></g><path fill="#3a414a" d="M135-194c87-1 58 113 63 194h-50c-7-57 23-157-34-157-59 0-34 97-39 157H25l-1-190h47c2 12-1 28 3 38 12-26 28-41 61-42" id="ag"/><path fill="#3a414a" d="M88-194c31-1 46 15 58 34l-1-101h50l1 261h-48c-2-10 0-23-3-31C134-8 116 4 84 4 32 4 16-41 15-95c0-56 19-97 73-99zm17 164c33 0 40-30 41-66 1-37-9-64-41-64s-38 30-39 65c0 43 13 65 39 65" id="ah"/><path fill="#3a414a" d="M115-3C79 11 28 4 28-45v-112H4v-33h27l15-45h31v45h36v33H77v99c-1 23 16 31 38 25v30" id="ai"/><g id="m"><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,0,0)" xlink:href="#ae"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,6.172839506172839,0)" xlink:href="#ag"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,19.691358024691358,0)" xlink:href="#ah"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,33.20987654320987,0)" xlink:href="#L"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,45.55555555555555,0)" xlink:href="#N"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,59.07407407407407,0)" xlink:href="#L"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,71.41975308641975,0)" xlink:href="#ag"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,84.93827160493827,0)" xlink:href="#ah"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,98.45679012345678,0)" xlink:href="#I"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,110.80246913580247,0)" xlink:href="#ag"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,124.32098765432099,0)" xlink:href="#ai"/></g><g id="n"><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,0,0)" xlink:href="#D"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,14.814814814814813,0)" xlink:href="#G"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,20.98765432098765,0)" xlink:href="#O"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,33.33333333333333,0)" xlink:href="#L"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,45.679012345679006,0)" xlink:href="#J"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,51.85185185185185,0)" xlink:href="#P"/></g><path fill="#3a414a" d="M248-139C247-14 144 9 11 0l48-248c105-6 190 8 189 109zm-34 0c1-70-52-87-126-82L50-27c98 7 163-21 164-112" id="aj"/><path fill="#3a414a" d="M238-179c0 84-86 86-175 82L45 0H11l48-248c82 0 179-12 179 69zm-34 1c0-53-64-42-116-43l-19 98c63 0 135 9 135-55" id="ak"/><path fill="#3a414a" d="M249-183c0 48-29 72-75 77L221 0h-36l-43-103H65L45 0H11l48-248c83 2 190-17 190 65zm-34 3c0-55-74-39-127-41l-18 92c63-1 145 13 145-51" id="al"/><path fill="#3a414a" d="M123-251c66 0 92 69 53 113C138-94 73-75 39-27h128l-5 27H-2c17-92 123-93 159-163 14-28-1-64-36-62-29 2-46 17-54 41l-30-6c13-36 37-61 86-61" id="am"/><g id="o"><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,0,0)" xlink:href="#aj"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,15.987654320987653,0)" xlink:href="#ak"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,30.80246913580247,0)" xlink:href="#al"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,46.79012345679012,0)" xlink:href="#R"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,61.60493827160494,0)" xlink:href="#U"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,72.71604938271605,0)" xlink:href="#V"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,85.06172839506173,0)" xlink:href="#W"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,89.93827160493828,0)" xlink:href="#S"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,102.28395061728396,0)" xlink:href="#X"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,109.62962962962965,0)" xlink:href="#am"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,121.97530864197533,0)" xlink:href="#Z"/></g><path fill="#3a414a" d="M30 0v-248h33v221h125V0H30" id="an"/><path fill="#3a414a" d="M141-36C126-15 110 5 73 4 37 3 15-17 15-53c-1-64 63-63 125-63 3-35-9-54-41-54-24 1-41 7-42 31l-33-3c5-37 33-52 76-52 45 0 72 20 72 64v82c-1 20 7 32 28 27v20c-31 9-61-2-59-35zM48-53c0 20 12 33 32 33 41-3 63-29 60-74-43 2-92-5-92 41" id="ao"/><path fill="#3a414a" d="M115-194c55 1 70 41 70 98S169 2 115 4C84 4 66-9 55-30l1 105H24l-1-265h31l2 30c10-21 28-34 59-34zm-8 174c40 0 45-34 45-75s-6-73-45-74c-42 0-51 32-51 76 0 43 10 73 51 73" id="ap"/><path fill="#3a414a" d="M59-47c-2 24 18 29 38 22v24C64 9 27 4 27-40v-127H5v-23h24l9-43h21v43h35v23H59v120" id="aq"/><path fill="#3a414a" d="M100-194c62-1 85 37 85 99 1 63-27 99-86 99S16-35 15-95c0-66 28-99 85-99zM99-20c44 1 53-31 53-75 0-43-8-75-51-75s-53 32-53 75 10 74 51 75" id="ar"/><g id="p"><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,0,0)" xlink:href="#an"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,12.345679012345679,0)" xlink:href="#ao"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,24.691358024691358,0)" xlink:href="#ap"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,37.03703703703704,0)" xlink:href="#aq"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,43.20987654320988,0)" xlink:href="#ar"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,55.55555555555556,0)" xlink:href="#ap"/></g><path fill="#3a414a" d="M155-56V0h-30v-56H8v-25l114-167h33v167h35v25h-35zm-30-156c-27 46-58 90-88 131h88v-131" id="as"/><path fill="#3a414a" d="M194 0L95-120 63-95V0H30v-248h33v124l119-124h40L117-140 236 0h-42" id="at"/><g id="q"><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,0,0)" xlink:href="#as"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,12.345679012345679,0)" xlink:href="#at"/></g><path fill="#3a414a" d="M9 0l6-27h63l36-188-63 39 6-31 66-41h29L110-27h60l-5 27H9" id="au"/><g id="r"><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,0,0)" xlink:href="#aj"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,15.987654320987653,0)" xlink:href="#ak"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,30.80246913580247,0)" xlink:href="#al"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,46.79012345679012,0)" xlink:href="#R"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,61.60493827160494,0)" xlink:href="#U"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,72.71604938271605,0)" xlink:href="#V"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,85.06172839506173,0)" xlink:href="#W"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,89.93827160493828,0)" xlink:href="#S"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,102.28395061728396,0)" xlink:href="#X"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,109.62962962962965,0)" xlink:href="#au"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,121.97530864197533,0)" xlink:href="#Z"/></g><path fill="#3a414a" d="M134-131c28 9 52 24 51 62-1 50-34 73-85 73S17-19 16-69c0-36 21-54 49-61-75-25-45-126 34-121 46 3 78 18 79 63 0 33-17 51-44 57zm-34-11c31 1 46-15 46-44 0-28-17-43-47-42-29 0-46 13-45 42 1 28 16 44 46 44zm1 122c35 0 51-18 51-52 0-30-18-46-53-46-33 0-51 17-51 47 0 34 19 51 53 51" id="av"/><path fill="#3a414a" d="M30-248c87 1 191-15 191 75 0 78-77 80-158 76V0H30v-248zm33 125c57 0 124 11 124-50 0-59-68-47-124-48v98" id="aw"/><g id="s"><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,0,0)" xlink:href="#z"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,12.345679012345679,0)" xlink:href="#A"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,24.691358024691358,0)" xlink:href="#av"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,37.03703703703704,0)" xlink:href="#A"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,49.382716049382715,0)" xlink:href="#aw"/></g><path fill="#3a414a" d="M199 0l-22-63H83L61 0H9l90-248h61L250 0h-51zm-33-102l-36-108c-10 38-24 72-36 108h72" id="ax"/><g id="t"><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,0,0)" xlink:href="#ax"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,15.987654320987653,0)" xlink:href="#D"/></g><g id="u"><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,0,0)" xlink:href="#ax"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,15.987654320987653,0)" xlink:href="#J"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,22.160493827160494,0)" xlink:href="#J"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,28.333333333333332,0)" xlink:href="#E"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,41.85185185185185,0)" xlink:href="#H"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,54.197530864197525,0)" xlink:href="#I"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,66.5432098765432,0)" xlink:href="#ai"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,73.88888888888889,0)" xlink:href="#G"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,80.06172839506172,0)" xlink:href="#E"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,93.58024691358024,0)" xlink:href="#ag"/></g><path fill="#3a414a" d="M101-251c82-7 93 87 43 132L82-64C71-53 59-42 53-27h129V0H18c2-99 128-94 128-182 0-28-16-43-45-43s-46 15-49 41l-32-3c6-41 34-60 81-64" id="ay"/><g id="v"><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,0,0)" xlink:href="#ay"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,12.345679012345679,0)" xlink:href="#A"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,24.691358024691358,0)" xlink:href="#A"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,37.03703703703704,0)" xlink:href="#B"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,48.14814814814815,0)" xlink:href="#ay"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,60.49382716049383,0)" xlink:href="#A"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,72.8395061728395,0)" xlink:href="#A"/></g><path fill="#3a414a" d="M64 0c3-98 48-159 88-221H18v-27h164v26C143-157 98-101 97 0H64" id="az"/><g id="w"><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,0,0)" xlink:href="#z"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,12.345679012345679,0)" xlink:href="#A"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,24.691358024691358,0)" xlink:href="#ay"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,37.03703703703704,0)" xlink:href="#as"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,49.382716049382715,0)" xlink:href="#B"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,60.49382716049382,0)" xlink:href="#az"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,72.8395061728395,0)" xlink:href="#ay"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,85.18518518518519,0)" xlink:href="#av"/></g><path fill="#3a414a" d="M179 0l23-115H67L45 0H11l48-248h34L72-143h135l21-105h32L212 0h-33" id="aA"/><path fill="#3a414a" d="M50-231l6-30h32l-6 30H50zM6 0l37-190h31L37 0H6" id="aB"/><path fill="#3a414a" d="M14 0l48-248h34L48 0H14" id="aC"/><path fill="#3a414a" d="M18-82l6-28h88l-6 28H18" id="aD"/><path fill="#3a414a" d="M188 0h-37l-8-164L71 0H34L18-190h31l8 164 75-164h34l8 164 74-164h31" id="aE"/><path fill="#3a414a" d="M66-151c12-25 30-51 66-40l-6 26C45-176 58-65 38 0H6l36-190h30" id="aF"/><g id="x"><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,0,0)" xlink:href="#aA"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,15.987654320987653,0)" xlink:href="#aB"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,20.864197530864196,0)" xlink:href="#aj"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,36.85185185185185,0)" xlink:href="#ak"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,51.66666666666666,0)" xlink:href="#aC"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,57.8395061728395,0)" xlink:href="#aD"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,65.18518518518518,0)" xlink:href="#V"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,77.53086419753086,0)" xlink:href="#aE"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,93.51851851851852,0)" xlink:href="#V"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,105.8641975308642,0)" xlink:href="#aF"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,113.20987654320989,0)" xlink:href="#S"/></g><path fill="#3a414a" d="M125-24c46 0 70-25 89-51l25 16C214-24 183 4 123 4 8 4-5-146 60-208c32-50 158-61 191-5 5 7 9 14 11 22l-32 10c-9-28-33-43-69-43-74 0-108 51-108 125 0 46 24 75 72 75" id="aG"/><path fill="#3a414a" d="M67 3c-93-2-31-127-26-193h32L48-50c-3 39 53 32 70 12 30-34 30-101 43-152h32L157 0h-30c1-10 6-24 4-33-14 20-29 37-64 36" id="aH"/><path fill="#3a414a" d="M55-144c13 50 104 24 104 88C159 21 15 23 1-39l26-10c6 40 102 42 102-4-13-50-104-23-104-87 0-71 143-71 148-8l-29 4c-5-35-85-37-89 0" id="aI"/><path fill="#3a414a" d="M30-147c31-64 166-65 159 27C183-49 158 1 86 4 9 8 1-88 30-147zM88-20c53 0 68-48 68-100 0-31-11-51-44-50-52 1-68 46-68 97 0 32 13 53 44 53" id="aJ"/><path fill="#3a414a" d="M248-111c6-24 9-61-24-58-72 9-57 108-77 169h-31l26-142c3-37-50-30-64-10C52-115 50-51 37 0H6l36-190h30c-1 10-6 24-4 32 13-43 101-52 105 5 13-22 29-41 61-41 90 0 28 129 23 194h-31" id="aK"/><path fill="#3a414a" d="M67-158c22-48 132-52 116 29L158 0h-32l25-140c3-38-53-32-70-12C52-117 51-52 38 0H6l36-190h30" id="aL"/><g id="y"><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,0,0)" xlink:href="#aG"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,15.987654320987653,0)" xlink:href="#aH"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,28.333333333333332,0)" xlink:href="#aI"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,39.44444444444444,0)" xlink:href="#T"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,45.617283950617285,0)" xlink:href="#aJ"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,57.96296296296296,0)" xlink:href="#aK"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,76.41975308641975,0)" xlink:href="#aD"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,83.76543209876543,0)" xlink:href="#V"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,96.11111111111111,0)" xlink:href="#W"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,100.98765432098766,0)" xlink:href="#W"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,105.8641975308642,0)" xlink:href="#aJ"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,118.20987654320989,0)" xlink:href="#U"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,129.320987654321,0)" xlink:href="#V"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,141.66666666666666,0)" xlink:href="#T"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,147.8395061728395,0)" xlink:href="#aB"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,152.71604938271602,0)" xlink:href="#aJ"/><use transform="matrix(0.06172839506172839,0,0,0.06172839506172839,165.0617283950617,0)" xlink:href="#aL"/></g></defs></g></svg>
\ No newline at end of file
diff --git a/docs/development/languages/fidl/tutorials/llcpp/basics/server.md b/docs/development/languages/fidl/tutorials/llcpp/basics/server.md
index e75545f..88488cd 100644
--- a/docs/development/languages/fidl/tutorials/llcpp/basics/server.md
+++ b/docs/development/languages/fidl/tutorials/llcpp/basics/server.md
@@ -181,7 +181,7 @@
 
 * `"//zircon/system/ulib/async-loop:async-loop-cpp"`: This library contains the
   asynchronous event loop code.
-* `"//sdk/lib/sys/component/llcpp"`: This library is used to publish
+* `"//sdk/lib/sys/component/cpp"`: This library is used to publish
   capabilities, e.g. protocols, to the component's outgoing directory.
 * `"//sdk/lib/syslog/cpp"`: This library is used to log messages.
 
diff --git a/docs/development/testing/scenic_testing.md b/docs/development/testing/scenic_testing.md
index 63308b7..bf4947c 100644
--- a/docs/development/testing/scenic_testing.md
+++ b/docs/development/testing/scenic_testing.md
@@ -217,12 +217,6 @@
   - **Build dependency:** `//src/ui/examples/simplest_app`
   - **Package URI:** `fuchsia-pkg://fuchsia.com/simplest_app#meta/simplest_app.cmx`
 
-* **yuv_to_image_pipe**
-  - An application that updates the scene using an ImagePipe.
-  - **Source:** [`//src/ui/examples/yuv_to_image_pipe`](/src/ui/examples/yuv_to_image_pipe)
-  - **Build dependency:** `//src/ui/examples/yuv_to_image_pipe`
-  - **Package URI:** `fuchsia-pkg://fuchsia.com/yuv_to_image_pipe#meta/yuv_to_image_pipe.cmx`
-
 To run these applications, you need to include the following dependency in your `fx set`
 configuration:
 
diff --git a/docs/glossary/_glossary.yaml b/docs/glossary/_glossary.yaml
index 6e03494..d4d85c7 100644
--- a/docs/glossary/_glossary.yaml
+++ b/docs/glossary/_glossary.yaml
@@ -1086,7 +1086,7 @@
   image."
   see_also: ["<a href=\"/docs/glossary#package\">Package</a>",
              "<a href=\"/docs/glossary#product-owner\">Product owner</a>"]
-  related_guides: ["<a href=\"docs/development/build/build_system/bringup.md\">Bringup Product Definition</a>"]
+  related_guides: ["<a href=\"/docs/development/build/build_system/bringup.md\">Bringup Product Definition</a>"]
   area: ["System"]
 
 - term: "Product bundle"
diff --git a/examples/components/config/integration_test/cpp/test.cc b/examples/components/config/integration_test/cpp/test.cc
index 4f7360a..9107e76 100644
--- a/examples/components/config/integration_test/cpp/test.cc
+++ b/examples/components/config/integration_test/cpp/test.cc
@@ -52,7 +52,7 @@
 
     if (replace_config_value) {
       // [START config_replace]
-      realm_builder.ReplaceConfigValue(name, "greeting", "Fuchsia");
+      realm_builder.SetConfigValue(name, "greeting", "Fuchsia");
       // [END config_replace]
     }
 
diff --git a/examples/components/config/integration_test/rust/lib.rs b/examples/components/config/integration_test/rust/lib.rs
index 007f32a..541ee5b 100644
--- a/examples/components/config/integration_test/rust/lib.rs
+++ b/examples/components/config/integration_test/rust/lib.rs
@@ -44,10 +44,7 @@
 
     let expected_greeting = if replace_config_value {
         // [START config_replace]
-        builder
-            .replace_config_value_string(&config_component, "greeting", "Fuchsia")
-            .await
-            .unwrap();
+        builder.set_config_value_string(&config_component, "greeting", "Fuchsia").await.unwrap();
         // [END config_replace]
         "Fuchsia"
     } else {
diff --git a/examples/drivers/v2/demo-number/BUILD.gn b/examples/drivers/v2/demo-number/BUILD.gn
index 74663b1..2ce2a29 100644
--- a/examples/drivers/v2/demo-number/BUILD.gn
+++ b/examples/drivers/v2/demo-number/BUILD.gn
@@ -27,7 +27,7 @@
     "//sdk/lib/driver2:devfs_exporter",
     "//sdk/lib/driver2:inspect",
     "//sdk/lib/driver2:llcpp",
-    "//sdk/lib/sys/component/llcpp",
+    "//sdk/lib/sys/component/cpp",
     "//src/devices/lib/driver:driver_runtime",
   ]
 }
diff --git a/examples/drivers/v2/demo-number/demo_number.cc b/examples/drivers/v2/demo-number/demo_number.cc
index 89cd7c5..7c545d9 100644
--- a/examples/drivers/v2/demo-number/demo_number.cc
+++ b/examples/drivers/v2/demo-number/demo_number.cc
@@ -10,7 +10,7 @@
 #include <lib/driver2/record_cpp.h>
 #include <lib/driver2/start_args.h>
 #include <lib/driver2/structured_logger.h>
-#include <lib/sys/component/llcpp/outgoing_directory.h>
+#include <lib/sys/component/cpp/outgoing_directory.h>
 #include <zircon/errors.h>
 
 namespace fdf2 = fuchsia_driver_framework;
diff --git a/examples/fidl/cpp/server/BUILD.gn b/examples/fidl/cpp/server/BUILD.gn
index 1fd58f8..0c36453 100644
--- a/examples/fidl/cpp/server/BUILD.gn
+++ b/examples/fidl/cpp/server/BUILD.gn
@@ -15,7 +15,7 @@
   sources = [ "main.cc" ]
   deps = [
     "//examples/fidl/fuchsia.examples:fuchsia.examples_cpp",
-    "//sdk/lib/sys/component/llcpp",
+    "//sdk/lib/sys/component/cpp",
     "//sdk/lib/syslog/cpp",
     "//zircon/system/ulib/async-loop:async-loop-cpp",
   ]
diff --git a/examples/fidl/cpp/server/main.cc b/examples/fidl/cpp/server/main.cc
index 3ec05ac..074ffa0 100644
--- a/examples/fidl/cpp/server/main.cc
+++ b/examples/fidl/cpp/server/main.cc
@@ -8,7 +8,7 @@
 
 // [START includes]
 #include <lib/async-loop/cpp/loop.h>
-#include <lib/sys/component/llcpp/outgoing_directory.h>
+#include <lib/sys/component/cpp/outgoing_directory.h>
 #include <lib/syslog/cpp/macros.h>
 // [END includes]
 
diff --git a/examples/fidl/llcpp/server/BUILD.gn b/examples/fidl/llcpp/server/BUILD.gn
index d1eff3c..0bcf8bd 100644
--- a/examples/fidl/llcpp/server/BUILD.gn
+++ b/examples/fidl/llcpp/server/BUILD.gn
@@ -10,7 +10,7 @@
   sources = [ "main.cc" ]
   deps = [
     "//examples/fidl/fuchsia.examples:fuchsia.examples_llcpp",
-    "//sdk/lib/sys/component/llcpp",
+    "//sdk/lib/sys/component/cpp",
     "//sdk/lib/syslog/cpp",
     "//zircon/system/ulib/async-loop:async-loop-cpp",
   ]
diff --git a/examples/fidl/llcpp/server/main.cc b/examples/fidl/llcpp/server/main.cc
index e8432c8..7d8a233 100644
--- a/examples/fidl/llcpp/server/main.cc
+++ b/examples/fidl/llcpp/server/main.cc
@@ -8,7 +8,7 @@
 
 // [START includes]
 #include <lib/async-loop/cpp/loop.h>
-#include <lib/sys/component/llcpp/outgoing_directory.h>
+#include <lib/sys/component/cpp/outgoing_directory.h>
 #include <lib/syslog/cpp/macros.h>
 // [END includes]
 
diff --git a/examples/fidl/llcpp/services/server/BUILD.gn b/examples/fidl/llcpp/services/server/BUILD.gn
index 607a94f..960502e 100644
--- a/examples/fidl/llcpp/services/server/BUILD.gn
+++ b/examples/fidl/llcpp/services/server/BUILD.gn
@@ -13,7 +13,7 @@
     "//sdk/lib/fdio",
     "//sdk/lib/fidl",
     "//sdk/lib/stdcompat",
-    "//sdk/lib/sys/component/llcpp",
+    "//sdk/lib/sys/component/cpp",
     "//sdk/lib/sys/cpp",
     "//zircon/system/ulib/async-loop:async-loop-cpp",
     "//zircon/system/ulib/async-loop:async-loop-default",
diff --git a/examples/fidl/llcpp/services/server/main.cc b/examples/fidl/llcpp/services/server/main.cc
index c13e4de..1b73010 100644
--- a/examples/fidl/llcpp/services/server/main.cc
+++ b/examples/fidl/llcpp/services/server/main.cc
@@ -5,8 +5,8 @@
 #include <fidl/fuchsia.examples/cpp/wire.h>
 #include <lib/async-loop/cpp/loop.h>
 #include <lib/async-loop/default.h>
-#include <lib/sys/component/llcpp/handlers.h>
-#include <lib/sys/component/llcpp/outgoing_directory.h>
+#include <lib/sys/component/cpp/handlers.h>
+#include <lib/sys/component/cpp/outgoing_directory.h>
 
 #include <iostream>
 
diff --git a/products/tests/boot_test/product_config.json b/products/tests/boot_test/product_config.json
index 5621fc9..5938562 100644
--- a/products/tests/boot_test/product_config.json
+++ b/products/tests/boot_test/product_config.json
@@ -1,5 +1,6 @@
 {
     "platform": {
+        "build_type": "user",
         "additional_serial_log_tags": [
             "session-manager"
         ]
diff --git a/sdk/BUILD.gn b/sdk/BUILD.gn
index 42acfd4..eca98bd 100644
--- a/sdk/BUILD.gn
+++ b/sdk/BUILD.gn
@@ -249,6 +249,7 @@
   cross_compiled = [
     "//tools/fidl/fidlgen_hlcpp:fidlgen_sdk",
     "//tools/cmc:cmc_sdk",
+    "//tools/configc:configc_sdk",
     "//tools/fvdl:fvdl_sdk",
     "//tools/sdk-tools/fconfig:fconfig_sdk",
     "//tools/sdk-tools/fpublish:fpublish_sdk",
@@ -627,6 +628,7 @@
     "//sdk/lib/driver_runtime:driver_runtime_sdk",
     "//sdk/lib/fidl/cpp/wire:wire_sdk",
     "//sdk/lib/input_report_reader:input_report_reader_sdk",
+    "//sdk/lib/sys/component/cpp:cpp_sdk",
     "//sdk/lib/sys/component/llcpp:llcpp_sdk",
     "//src/devices/bin/driver_runtime:driver_runtime_sdk",
     "//src/devices/lib/mmio:mmio_sdk",
diff --git a/sdk/cts/release/BUILD.gn b/sdk/cts/release/BUILD.gn
index 4fbc201d..cbf993f 100644
--- a/sdk/cts/release/BUILD.gn
+++ b/sdk/cts/release/BUILD.gn
@@ -14,6 +14,14 @@
 
 compatibility_test_suite("canary_tests") {
   path = "//prebuilt/cts/canary/$host_platform/cts"
+
+  # TODO(https://fxbug.dev/103694) re-enable this once fxr/695223 rolls to canary
+  disabled_tests = [
+    {
+      archive_name = "fuchsia-element-tests"
+      component_name = "fuchsia-element-test.cm"
+    },
+  ]
 }
 
 compatibility_test_suite("current_milestone_tests") {
diff --git a/sdk/dart/fuchsia_component_test/fuchsia_component_test.api b/sdk/dart/fuchsia_component_test/fuchsia_component_test.api
index 6032bc4..83fe028 100644
--- a/sdk/dart/fuchsia_component_test/fuchsia_component_test.api
+++ b/sdk/dart/fuchsia_component_test/fuchsia_component_test.api
@@ -1884,7 +1884,43 @@
               "returnType": "Future<void>",
               "type": "MethodDeclarationImpl"
             },
-            "replaceConfigValue": {
+            "replaceRealmDecl": {
+              "SimpleFormalParameterImpl": {
+                "decl": {
+                  "isConst": false,
+                  "isFinal": false,
+                  "isOptional": false,
+                  "isOptionalNamed": false,
+                  "isOptionalPositional": false,
+                  "isPositional": true,
+                  "isRequired": true,
+                  "isRequiredNamed": false,
+                  "isRequiredPositional": true,
+                  "name": "decl",
+                  "type": "SimpleFormalParameterImpl",
+                  "varType": "fdecl.Component"
+                }
+              },
+              "isAbstract": false,
+              "isGetter": false,
+              "isOperator": false,
+              "isSetter": false,
+              "isStatic": false,
+              "name": "replaceRealmDecl",
+              "returnType": "Future<void>",
+              "type": "MethodDeclarationImpl"
+            },
+            "rootRealm": {
+              "isAbstract": false,
+              "isGetter": true,
+              "isOperator": false,
+              "isSetter": false,
+              "isStatic": false,
+              "name": "rootRealm",
+              "returnType": "SubRealmBuilder",
+              "type": "MethodDeclarationImpl"
+            },
+            "setConfigValue": {
               "SimpleFormalParameterImpl": {
                 "childRef": {
                   "isConst": false,
@@ -1934,11 +1970,11 @@
               "isOperator": false,
               "isSetter": false,
               "isStatic": false,
-              "name": "replaceConfigValue",
+              "name": "setConfigValue",
               "returnType": "Future<void>",
               "type": "MethodDeclarationImpl"
             },
-            "replaceConfigValueBool": {
+            "setConfigValueBool": {
               "SimpleFormalParameterImpl": {
                 "childRef": {
                   "isConst": false,
@@ -1988,11 +2024,11 @@
               "isOperator": false,
               "isSetter": false,
               "isStatic": false,
-              "name": "replaceConfigValueBool",
+              "name": "setConfigValueBool",
               "returnType": "Future<void>",
               "type": "MethodDeclarationImpl"
             },
-            "replaceConfigValueBoolVector": {
+            "setConfigValueBoolVector": {
               "SimpleFormalParameterImpl": {
                 "childRef": {
                   "isConst": false,
@@ -2042,11 +2078,11 @@
               "isOperator": false,
               "isSetter": false,
               "isStatic": false,
-              "name": "replaceConfigValueBoolVector",
+              "name": "setConfigValueBoolVector",
               "returnType": "Future<void>",
               "type": "MethodDeclarationImpl"
             },
-            "replaceConfigValueInt16": {
+            "setConfigValueInt16": {
               "SimpleFormalParameterImpl": {
                 "childRef": {
                   "isConst": false,
@@ -2096,11 +2132,11 @@
               "isOperator": false,
               "isSetter": false,
               "isStatic": false,
-              "name": "replaceConfigValueInt16",
+              "name": "setConfigValueInt16",
               "returnType": "Future<void>",
               "type": "MethodDeclarationImpl"
             },
-            "replaceConfigValueInt16Vector": {
+            "setConfigValueInt16Vector": {
               "SimpleFormalParameterImpl": {
                 "childRef": {
                   "isConst": false,
@@ -2150,11 +2186,11 @@
               "isOperator": false,
               "isSetter": false,
               "isStatic": false,
-              "name": "replaceConfigValueInt16Vector",
+              "name": "setConfigValueInt16Vector",
               "returnType": "Future<void>",
               "type": "MethodDeclarationImpl"
             },
-            "replaceConfigValueInt32": {
+            "setConfigValueInt32": {
               "SimpleFormalParameterImpl": {
                 "childRef": {
                   "isConst": false,
@@ -2204,11 +2240,11 @@
               "isOperator": false,
               "isSetter": false,
               "isStatic": false,
-              "name": "replaceConfigValueInt32",
+              "name": "setConfigValueInt32",
               "returnType": "Future<void>",
               "type": "MethodDeclarationImpl"
             },
-            "replaceConfigValueInt32Vector": {
+            "setConfigValueInt32Vector": {
               "SimpleFormalParameterImpl": {
                 "childRef": {
                   "isConst": false,
@@ -2258,11 +2294,11 @@
               "isOperator": false,
               "isSetter": false,
               "isStatic": false,
-              "name": "replaceConfigValueInt32Vector",
+              "name": "setConfigValueInt32Vector",
               "returnType": "Future<void>",
               "type": "MethodDeclarationImpl"
             },
-            "replaceConfigValueInt64": {
+            "setConfigValueInt64": {
               "SimpleFormalParameterImpl": {
                 "childRef": {
                   "isConst": false,
@@ -2312,11 +2348,11 @@
               "isOperator": false,
               "isSetter": false,
               "isStatic": false,
-              "name": "replaceConfigValueInt64",
+              "name": "setConfigValueInt64",
               "returnType": "Future<void>",
               "type": "MethodDeclarationImpl"
             },
-            "replaceConfigValueInt64Vector": {
+            "setConfigValueInt64Vector": {
               "SimpleFormalParameterImpl": {
                 "childRef": {
                   "isConst": false,
@@ -2366,11 +2402,11 @@
               "isOperator": false,
               "isSetter": false,
               "isStatic": false,
-              "name": "replaceConfigValueInt64Vector",
+              "name": "setConfigValueInt64Vector",
               "returnType": "Future<void>",
               "type": "MethodDeclarationImpl"
             },
-            "replaceConfigValueInt8": {
+            "setConfigValueInt8": {
               "SimpleFormalParameterImpl": {
                 "childRef": {
                   "isConst": false,
@@ -2420,11 +2456,11 @@
               "isOperator": false,
               "isSetter": false,
               "isStatic": false,
-              "name": "replaceConfigValueInt8",
+              "name": "setConfigValueInt8",
               "returnType": "Future<void>",
               "type": "MethodDeclarationImpl"
             },
-            "replaceConfigValueInt8Vector": {
+            "setConfigValueInt8Vector": {
               "SimpleFormalParameterImpl": {
                 "childRef": {
                   "isConst": false,
@@ -2474,11 +2510,11 @@
               "isOperator": false,
               "isSetter": false,
               "isStatic": false,
-              "name": "replaceConfigValueInt8Vector",
+              "name": "setConfigValueInt8Vector",
               "returnType": "Future<void>",
               "type": "MethodDeclarationImpl"
             },
-            "replaceConfigValueString": {
+            "setConfigValueString": {
               "SimpleFormalParameterImpl": {
                 "childRef": {
                   "isConst": false,
@@ -2528,11 +2564,11 @@
               "isOperator": false,
               "isSetter": false,
               "isStatic": false,
-              "name": "replaceConfigValueString",
+              "name": "setConfigValueString",
               "returnType": "Future<void>",
               "type": "MethodDeclarationImpl"
             },
-            "replaceConfigValueStringVector": {
+            "setConfigValueStringVector": {
               "SimpleFormalParameterImpl": {
                 "childRef": {
                   "isConst": false,
@@ -2582,11 +2618,11 @@
               "isOperator": false,
               "isSetter": false,
               "isStatic": false,
-              "name": "replaceConfigValueStringVector",
+              "name": "setConfigValueStringVector",
               "returnType": "Future<void>",
               "type": "MethodDeclarationImpl"
             },
-            "replaceConfigValueUint16": {
+            "setConfigValueUint16": {
               "SimpleFormalParameterImpl": {
                 "childRef": {
                   "isConst": false,
@@ -2636,11 +2672,11 @@
               "isOperator": false,
               "isSetter": false,
               "isStatic": false,
-              "name": "replaceConfigValueUint16",
+              "name": "setConfigValueUint16",
               "returnType": "Future<void>",
               "type": "MethodDeclarationImpl"
             },
-            "replaceConfigValueUint16Vector": {
+            "setConfigValueUint16Vector": {
               "SimpleFormalParameterImpl": {
                 "childRef": {
                   "isConst": false,
@@ -2690,11 +2726,11 @@
               "isOperator": false,
               "isSetter": false,
               "isStatic": false,
-              "name": "replaceConfigValueUint16Vector",
+              "name": "setConfigValueUint16Vector",
               "returnType": "Future<void>",
               "type": "MethodDeclarationImpl"
             },
-            "replaceConfigValueUint32": {
+            "setConfigValueUint32": {
               "SimpleFormalParameterImpl": {
                 "childRef": {
                   "isConst": false,
@@ -2744,11 +2780,11 @@
               "isOperator": false,
               "isSetter": false,
               "isStatic": false,
-              "name": "replaceConfigValueUint32",
+              "name": "setConfigValueUint32",
               "returnType": "Future<void>",
               "type": "MethodDeclarationImpl"
             },
-            "replaceConfigValueUint32Vector": {
+            "setConfigValueUint32Vector": {
               "SimpleFormalParameterImpl": {
                 "childRef": {
                   "isConst": false,
@@ -2798,11 +2834,11 @@
               "isOperator": false,
               "isSetter": false,
               "isStatic": false,
-              "name": "replaceConfigValueUint32Vector",
+              "name": "setConfigValueUint32Vector",
               "returnType": "Future<void>",
               "type": "MethodDeclarationImpl"
             },
-            "replaceConfigValueUint64": {
+            "setConfigValueUint64": {
               "SimpleFormalParameterImpl": {
                 "childRef": {
                   "isConst": false,
@@ -2852,11 +2888,11 @@
               "isOperator": false,
               "isSetter": false,
               "isStatic": false,
-              "name": "replaceConfigValueUint64",
+              "name": "setConfigValueUint64",
               "returnType": "Future<void>",
               "type": "MethodDeclarationImpl"
             },
-            "replaceConfigValueUint64Vector": {
+            "setConfigValueUint64Vector": {
               "SimpleFormalParameterImpl": {
                 "childRef": {
                   "isConst": false,
@@ -2906,11 +2942,11 @@
               "isOperator": false,
               "isSetter": false,
               "isStatic": false,
-              "name": "replaceConfigValueUint64Vector",
+              "name": "setConfigValueUint64Vector",
               "returnType": "Future<void>",
               "type": "MethodDeclarationImpl"
             },
-            "replaceConfigValueUint8": {
+            "setConfigValueUint8": {
               "SimpleFormalParameterImpl": {
                 "childRef": {
                   "isConst": false,
@@ -2960,11 +2996,11 @@
               "isOperator": false,
               "isSetter": false,
               "isStatic": false,
-              "name": "replaceConfigValueUint8",
+              "name": "setConfigValueUint8",
               "returnType": "Future<void>",
               "type": "MethodDeclarationImpl"
             },
-            "replaceConfigValueUint8Vector": {
+            "setConfigValueUint8Vector": {
               "SimpleFormalParameterImpl": {
                 "childRef": {
                   "isConst": false,
@@ -3014,45 +3050,9 @@
               "isOperator": false,
               "isSetter": false,
               "isStatic": false,
-              "name": "replaceConfigValueUint8Vector",
+              "name": "setConfigValueUint8Vector",
               "returnType": "Future<void>",
               "type": "MethodDeclarationImpl"
-            },
-            "replaceRealmDecl": {
-              "SimpleFormalParameterImpl": {
-                "decl": {
-                  "isConst": false,
-                  "isFinal": false,
-                  "isOptional": false,
-                  "isOptionalNamed": false,
-                  "isOptionalPositional": false,
-                  "isPositional": true,
-                  "isRequired": true,
-                  "isRequiredNamed": false,
-                  "isRequiredPositional": true,
-                  "name": "decl",
-                  "type": "SimpleFormalParameterImpl",
-                  "varType": "fdecl.Component"
-                }
-              },
-              "isAbstract": false,
-              "isGetter": false,
-              "isOperator": false,
-              "isSetter": false,
-              "isStatic": false,
-              "name": "replaceRealmDecl",
-              "returnType": "Future<void>",
-              "type": "MethodDeclarationImpl"
-            },
-            "rootRealm": {
-              "isAbstract": false,
-              "isGetter": true,
-              "isOperator": false,
-              "isSetter": false,
-              "isStatic": false,
-              "name": "rootRealm",
-              "returnType": "SubRealmBuilder",
-              "type": "MethodDeclarationImpl"
             }
           },
           "VariableDeclarationImpl": {
@@ -4676,7 +4676,33 @@
               "returnType": "Future<void>",
               "type": "MethodDeclarationImpl"
             },
-            "replaceConfigValue": {
+            "replaceRealmDecl": {
+              "SimpleFormalParameterImpl": {
+                "decl": {
+                  "isConst": false,
+                  "isFinal": false,
+                  "isOptional": false,
+                  "isOptionalNamed": false,
+                  "isOptionalPositional": false,
+                  "isPositional": true,
+                  "isRequired": true,
+                  "isRequiredNamed": false,
+                  "isRequiredPositional": true,
+                  "name": "decl",
+                  "type": "SimpleFormalParameterImpl",
+                  "varType": "fdecl.Component"
+                }
+              },
+              "isAbstract": false,
+              "isGetter": false,
+              "isOperator": false,
+              "isSetter": false,
+              "isStatic": false,
+              "name": "replaceRealmDecl",
+              "returnType": "Future<void>",
+              "type": "MethodDeclarationImpl"
+            },
+            "setConfigValue": {
               "SimpleFormalParameterImpl": {
                 "childRef": {
                   "isConst": false,
@@ -4726,11 +4752,11 @@
               "isOperator": false,
               "isSetter": false,
               "isStatic": false,
-              "name": "replaceConfigValue",
+              "name": "setConfigValue",
               "returnType": "Future<void>",
               "type": "MethodDeclarationImpl"
             },
-            "replaceConfigValueBool": {
+            "setConfigValueBool": {
               "SimpleFormalParameterImpl": {
                 "childRef": {
                   "isConst": false,
@@ -4780,11 +4806,11 @@
               "isOperator": false,
               "isSetter": false,
               "isStatic": false,
-              "name": "replaceConfigValueBool",
+              "name": "setConfigValueBool",
               "returnType": "Future<void>",
               "type": "MethodDeclarationImpl"
             },
-            "replaceConfigValueBoolVector": {
+            "setConfigValueBoolVector": {
               "SimpleFormalParameterImpl": {
                 "childRef": {
                   "isConst": false,
@@ -4834,11 +4860,11 @@
               "isOperator": false,
               "isSetter": false,
               "isStatic": false,
-              "name": "replaceConfigValueBoolVector",
+              "name": "setConfigValueBoolVector",
               "returnType": "Future<void>",
               "type": "MethodDeclarationImpl"
             },
-            "replaceConfigValueInt16": {
+            "setConfigValueInt16": {
               "SimpleFormalParameterImpl": {
                 "childRef": {
                   "isConst": false,
@@ -4888,11 +4914,11 @@
               "isOperator": false,
               "isSetter": false,
               "isStatic": false,
-              "name": "replaceConfigValueInt16",
+              "name": "setConfigValueInt16",
               "returnType": "Future<void>",
               "type": "MethodDeclarationImpl"
             },
-            "replaceConfigValueInt16Vector": {
+            "setConfigValueInt16Vector": {
               "SimpleFormalParameterImpl": {
                 "childRef": {
                   "isConst": false,
@@ -4942,11 +4968,11 @@
               "isOperator": false,
               "isSetter": false,
               "isStatic": false,
-              "name": "replaceConfigValueInt16Vector",
+              "name": "setConfigValueInt16Vector",
               "returnType": "Future<void>",
               "type": "MethodDeclarationImpl"
             },
-            "replaceConfigValueInt32": {
+            "setConfigValueInt32": {
               "SimpleFormalParameterImpl": {
                 "childRef": {
                   "isConst": false,
@@ -4996,11 +5022,11 @@
               "isOperator": false,
               "isSetter": false,
               "isStatic": false,
-              "name": "replaceConfigValueInt32",
+              "name": "setConfigValueInt32",
               "returnType": "Future<void>",
               "type": "MethodDeclarationImpl"
             },
-            "replaceConfigValueInt32Vector": {
+            "setConfigValueInt32Vector": {
               "SimpleFormalParameterImpl": {
                 "childRef": {
                   "isConst": false,
@@ -5050,11 +5076,11 @@
               "isOperator": false,
               "isSetter": false,
               "isStatic": false,
-              "name": "replaceConfigValueInt32Vector",
+              "name": "setConfigValueInt32Vector",
               "returnType": "Future<void>",
               "type": "MethodDeclarationImpl"
             },
-            "replaceConfigValueInt64": {
+            "setConfigValueInt64": {
               "SimpleFormalParameterImpl": {
                 "childRef": {
                   "isConst": false,
@@ -5104,11 +5130,11 @@
               "isOperator": false,
               "isSetter": false,
               "isStatic": false,
-              "name": "replaceConfigValueInt64",
+              "name": "setConfigValueInt64",
               "returnType": "Future<void>",
               "type": "MethodDeclarationImpl"
             },
-            "replaceConfigValueInt64Vector": {
+            "setConfigValueInt64Vector": {
               "SimpleFormalParameterImpl": {
                 "childRef": {
                   "isConst": false,
@@ -5158,11 +5184,11 @@
               "isOperator": false,
               "isSetter": false,
               "isStatic": false,
-              "name": "replaceConfigValueInt64Vector",
+              "name": "setConfigValueInt64Vector",
               "returnType": "Future<void>",
               "type": "MethodDeclarationImpl"
             },
-            "replaceConfigValueInt8": {
+            "setConfigValueInt8": {
               "SimpleFormalParameterImpl": {
                 "childRef": {
                   "isConst": false,
@@ -5212,11 +5238,11 @@
               "isOperator": false,
               "isSetter": false,
               "isStatic": false,
-              "name": "replaceConfigValueInt8",
+              "name": "setConfigValueInt8",
               "returnType": "Future<void>",
               "type": "MethodDeclarationImpl"
             },
-            "replaceConfigValueInt8Vector": {
+            "setConfigValueInt8Vector": {
               "SimpleFormalParameterImpl": {
                 "childRef": {
                   "isConst": false,
@@ -5266,11 +5292,11 @@
               "isOperator": false,
               "isSetter": false,
               "isStatic": false,
-              "name": "replaceConfigValueInt8Vector",
+              "name": "setConfigValueInt8Vector",
               "returnType": "Future<void>",
               "type": "MethodDeclarationImpl"
             },
-            "replaceConfigValueString": {
+            "setConfigValueString": {
               "SimpleFormalParameterImpl": {
                 "childRef": {
                   "isConst": false,
@@ -5320,11 +5346,11 @@
               "isOperator": false,
               "isSetter": false,
               "isStatic": false,
-              "name": "replaceConfigValueString",
+              "name": "setConfigValueString",
               "returnType": "Future<void>",
               "type": "MethodDeclarationImpl"
             },
-            "replaceConfigValueStringVector": {
+            "setConfigValueStringVector": {
               "SimpleFormalParameterImpl": {
                 "childRef": {
                   "isConst": false,
@@ -5374,11 +5400,11 @@
               "isOperator": false,
               "isSetter": false,
               "isStatic": false,
-              "name": "replaceConfigValueStringVector",
+              "name": "setConfigValueStringVector",
               "returnType": "Future<void>",
               "type": "MethodDeclarationImpl"
             },
-            "replaceConfigValueUint16": {
+            "setConfigValueUint16": {
               "SimpleFormalParameterImpl": {
                 "childRef": {
                   "isConst": false,
@@ -5428,11 +5454,11 @@
               "isOperator": false,
               "isSetter": false,
               "isStatic": false,
-              "name": "replaceConfigValueUint16",
+              "name": "setConfigValueUint16",
               "returnType": "Future<void>",
               "type": "MethodDeclarationImpl"
             },
-            "replaceConfigValueUint16Vector": {
+            "setConfigValueUint16Vector": {
               "SimpleFormalParameterImpl": {
                 "childRef": {
                   "isConst": false,
@@ -5482,11 +5508,11 @@
               "isOperator": false,
               "isSetter": false,
               "isStatic": false,
-              "name": "replaceConfigValueUint16Vector",
+              "name": "setConfigValueUint16Vector",
               "returnType": "Future<void>",
               "type": "MethodDeclarationImpl"
             },
-            "replaceConfigValueUint32": {
+            "setConfigValueUint32": {
               "SimpleFormalParameterImpl": {
                 "childRef": {
                   "isConst": false,
@@ -5536,11 +5562,11 @@
               "isOperator": false,
               "isSetter": false,
               "isStatic": false,
-              "name": "replaceConfigValueUint32",
+              "name": "setConfigValueUint32",
               "returnType": "Future<void>",
               "type": "MethodDeclarationImpl"
             },
-            "replaceConfigValueUint32Vector": {
+            "setConfigValueUint32Vector": {
               "SimpleFormalParameterImpl": {
                 "childRef": {
                   "isConst": false,
@@ -5590,11 +5616,11 @@
               "isOperator": false,
               "isSetter": false,
               "isStatic": false,
-              "name": "replaceConfigValueUint32Vector",
+              "name": "setConfigValueUint32Vector",
               "returnType": "Future<void>",
               "type": "MethodDeclarationImpl"
             },
-            "replaceConfigValueUint64": {
+            "setConfigValueUint64": {
               "SimpleFormalParameterImpl": {
                 "childRef": {
                   "isConst": false,
@@ -5644,11 +5670,11 @@
               "isOperator": false,
               "isSetter": false,
               "isStatic": false,
-              "name": "replaceConfigValueUint64",
+              "name": "setConfigValueUint64",
               "returnType": "Future<void>",
               "type": "MethodDeclarationImpl"
             },
-            "replaceConfigValueUint64Vector": {
+            "setConfigValueUint64Vector": {
               "SimpleFormalParameterImpl": {
                 "childRef": {
                   "isConst": false,
@@ -5698,11 +5724,11 @@
               "isOperator": false,
               "isSetter": false,
               "isStatic": false,
-              "name": "replaceConfigValueUint64Vector",
+              "name": "setConfigValueUint64Vector",
               "returnType": "Future<void>",
               "type": "MethodDeclarationImpl"
             },
-            "replaceConfigValueUint8": {
+            "setConfigValueUint8": {
               "SimpleFormalParameterImpl": {
                 "childRef": {
                   "isConst": false,
@@ -5752,11 +5778,11 @@
               "isOperator": false,
               "isSetter": false,
               "isStatic": false,
-              "name": "replaceConfigValueUint8",
+              "name": "setConfigValueUint8",
               "returnType": "Future<void>",
               "type": "MethodDeclarationImpl"
             },
-            "replaceConfigValueUint8Vector": {
+            "setConfigValueUint8Vector": {
               "SimpleFormalParameterImpl": {
                 "childRef": {
                   "isConst": false,
@@ -5806,33 +5832,7 @@
               "isOperator": false,
               "isSetter": false,
               "isStatic": false,
-              "name": "replaceConfigValueUint8Vector",
-              "returnType": "Future<void>",
-              "type": "MethodDeclarationImpl"
-            },
-            "replaceRealmDecl": {
-              "SimpleFormalParameterImpl": {
-                "decl": {
-                  "isConst": false,
-                  "isFinal": false,
-                  "isOptional": false,
-                  "isOptionalNamed": false,
-                  "isOptionalPositional": false,
-                  "isPositional": true,
-                  "isRequired": true,
-                  "isRequiredNamed": false,
-                  "isRequiredPositional": true,
-                  "name": "decl",
-                  "type": "SimpleFormalParameterImpl",
-                  "varType": "fdecl.Component"
-                }
-              },
-              "isAbstract": false,
-              "isGetter": false,
-              "isOperator": false,
-              "isSetter": false,
-              "isStatic": false,
-              "name": "replaceRealmDecl",
+              "name": "setConfigValueUint8Vector",
               "returnType": "Future<void>",
               "type": "MethodDeclarationImpl"
             }
diff --git a/sdk/dart/fuchsia_component_test/lib/src/realm_builder.dart b/sdk/dart/fuchsia_component_test/lib/src/realm_builder.dart
index 0abafcc..afa95d6 100644
--- a/sdk/dart/fuchsia_component_test/lib/src/realm_builder.dart
+++ b/sdk/dart/fuchsia_component_test/lib/src/realm_builder.dart
@@ -819,23 +819,23 @@
   }
 
   /// Replaces a value of a given configuration field
-  Future<void> replaceConfigValue(
+  Future<void> setConfigValue(
     ChildRef childRef,
     String key,
     fconfig.ValueSpec value,
   ) {
     childRef.checkScope(realmPath);
-    return realmProxy.replaceConfigValue(childRef.name, key, value);
+    return realmProxy.setConfigValue(childRef.name, key, value);
   }
 
   /// Replaces a boolean value of a given configuration field
-  Future<void> replaceConfigValueBool(
+  Future<void> setConfigValueBool(
     ChildRef childRef,
     String key,
     // ignore: avoid_positional_boolean_parameters
     bool value,
   ) {
-    return replaceConfigValue(
+    return setConfigValue(
       childRef,
       key,
       _singleValue(fconfig.SingleValue.withBool(value)),
@@ -843,12 +843,12 @@
   }
 
   /// Replaces a uint8 value of a given configuration field
-  Future<void> replaceConfigValueUint8(
+  Future<void> setConfigValueUint8(
     ChildRef childRef,
     String key,
     int value,
   ) {
-    return replaceConfigValue(
+    return setConfigValue(
       childRef,
       key,
       _singleValue(fconfig.SingleValue.withUint8(value)),
@@ -856,12 +856,12 @@
   }
 
   /// Replaces a uint16 value of a given configuration field
-  Future<void> replaceConfigValueUint16(
+  Future<void> setConfigValueUint16(
     ChildRef childRef,
     String key,
     int value,
   ) {
-    return replaceConfigValue(
+    return setConfigValue(
       childRef,
       key,
       _singleValue(fconfig.SingleValue.withUint16(value)),
@@ -869,12 +869,12 @@
   }
 
   /// Replaces a uint32 value of a given configuration field
-  Future<void> replaceConfigValueUint32(
+  Future<void> setConfigValueUint32(
     ChildRef childRef,
     String key,
     int value,
   ) {
-    return replaceConfigValue(
+    return setConfigValue(
       childRef,
       key,
       _singleValue(fconfig.SingleValue.withUint32(value)),
@@ -882,12 +882,12 @@
   }
 
   /// Replaces a uint64 value of a given configuration field
-  Future<void> replaceConfigValueUint64(
+  Future<void> setConfigValueUint64(
     ChildRef childRef,
     String key,
     int value,
   ) {
-    return replaceConfigValue(
+    return setConfigValue(
       childRef,
       key,
       _singleValue(fconfig.SingleValue.withUint64(value)),
@@ -895,12 +895,12 @@
   }
 
   /// Replaces a int8 value of a given configuration field
-  Future<void> replaceConfigValueInt8(
+  Future<void> setConfigValueInt8(
     ChildRef childRef,
     String key,
     int value,
   ) {
-    return replaceConfigValue(
+    return setConfigValue(
       childRef,
       key,
       _singleValue(fconfig.SingleValue.withInt8(value)),
@@ -908,12 +908,12 @@
   }
 
   /// Replaces a int16 value of a given configuration field
-  Future<void> replaceConfigValueInt16(
+  Future<void> setConfigValueInt16(
     ChildRef childRef,
     String key,
     int value,
   ) {
-    return replaceConfigValue(
+    return setConfigValue(
       childRef,
       key,
       _singleValue(fconfig.SingleValue.withInt16(value)),
@@ -921,12 +921,12 @@
   }
 
   /// Replaces a int32 value of a given configuration field
-  Future<void> replaceConfigValueInt32(
+  Future<void> setConfigValueInt32(
     ChildRef childRef,
     String key,
     int value,
   ) {
-    return replaceConfigValue(
+    return setConfigValue(
       childRef,
       key,
       _singleValue(fconfig.SingleValue.withInt32(value)),
@@ -934,12 +934,12 @@
   }
 
   /// Replaces a int64 value of a given configuration field
-  Future<void> replaceConfigValueInt64(
+  Future<void> setConfigValueInt64(
     ChildRef childRef,
     String key,
     int value,
   ) {
-    return replaceConfigValue(
+    return setConfigValue(
       childRef,
       key,
       _singleValue(fconfig.SingleValue.withInt64(value)),
@@ -947,12 +947,12 @@
   }
 
   /// Replaces a string value of a given configuration field
-  Future<void> replaceConfigValueString(
+  Future<void> setConfigValueString(
     ChildRef childRef,
     String key,
     String value,
   ) {
-    return replaceConfigValue(
+    return setConfigValue(
       childRef,
       key,
       _singleValue(fconfig.SingleValue.withString$(value)),
@@ -960,12 +960,12 @@
   }
 
   /// Replaces a boolean vector value of a given configuration field
-  Future<void> replaceConfigValueBoolVector(
+  Future<void> setConfigValueBoolVector(
     ChildRef childRef,
     String key,
     List<bool> value,
   ) {
-    return replaceConfigValue(
+    return setConfigValue(
       childRef,
       key,
       _vectorValue(fconfig.VectorValue.withBoolVector(value)),
@@ -973,12 +973,12 @@
   }
 
   /// Replaces a uint8 vector value of a given configuration field
-  Future<void> replaceConfigValueUint8Vector(
+  Future<void> setConfigValueUint8Vector(
     ChildRef childRef,
     String key,
     Uint8List value,
   ) {
-    return replaceConfigValue(
+    return setConfigValue(
       childRef,
       key,
       _vectorValue(fconfig.VectorValue.withUint8Vector(value)),
@@ -986,12 +986,12 @@
   }
 
   /// Replaces a uint16 vector value of a given configuration field
-  Future<void> replaceConfigValueUint16Vector(
+  Future<void> setConfigValueUint16Vector(
     ChildRef childRef,
     String key,
     Uint16List value,
   ) {
-    return replaceConfigValue(
+    return setConfigValue(
       childRef,
       key,
       _vectorValue(fconfig.VectorValue.withUint16Vector(value)),
@@ -999,12 +999,12 @@
   }
 
   /// Replaces a uint32 vector value of a given configuration field
-  Future<void> replaceConfigValueUint32Vector(
+  Future<void> setConfigValueUint32Vector(
     ChildRef childRef,
     String key,
     Uint32List value,
   ) {
-    return replaceConfigValue(
+    return setConfigValue(
       childRef,
       key,
       _vectorValue(fconfig.VectorValue.withUint32Vector(value)),
@@ -1012,12 +1012,12 @@
   }
 
   /// Replaces a uint64 vector value of a given configuration field
-  Future<void> replaceConfigValueUint64Vector(
+  Future<void> setConfigValueUint64Vector(
     ChildRef childRef,
     String key,
     Uint64List value,
   ) {
-    return replaceConfigValue(
+    return setConfigValue(
       childRef,
       key,
       _vectorValue(fconfig.VectorValue.withUint64Vector(value)),
@@ -1025,12 +1025,12 @@
   }
 
   /// Replaces a int8 vector value of a given configuration field
-  Future<void> replaceConfigValueInt8Vector(
+  Future<void> setConfigValueInt8Vector(
     ChildRef childRef,
     String key,
     Int8List value,
   ) {
-    return replaceConfigValue(
+    return setConfigValue(
       childRef,
       key,
       _vectorValue(fconfig.VectorValue.withInt8Vector(value)),
@@ -1038,12 +1038,12 @@
   }
 
   /// Replaces a int16 vector value of a given configuration field
-  Future<void> replaceConfigValueInt16Vector(
+  Future<void> setConfigValueInt16Vector(
     ChildRef childRef,
     String key,
     Int16List value,
   ) {
-    return replaceConfigValue(
+    return setConfigValue(
       childRef,
       key,
       _vectorValue(fconfig.VectorValue.withInt16Vector(value)),
@@ -1051,12 +1051,12 @@
   }
 
   /// Replaces a int32 vector value of a given configuration field
-  Future<void> replaceConfigValueInt32Vector(
+  Future<void> setConfigValueInt32Vector(
     ChildRef childRef,
     String key,
     Int32List value,
   ) {
-    return replaceConfigValue(
+    return setConfigValue(
       childRef,
       key,
       _vectorValue(fconfig.VectorValue.withInt32Vector(value)),
@@ -1064,12 +1064,12 @@
   }
 
   /// Replaces a int64 vector value of a given configuration field
-  Future<void> replaceConfigValueInt64Vector(
+  Future<void> setConfigValueInt64Vector(
     ChildRef childRef,
     String key,
     Int64List value,
   ) {
-    return replaceConfigValue(
+    return setConfigValue(
       childRef,
       key,
       _vectorValue(fconfig.VectorValue.withInt64Vector(value)),
@@ -1077,12 +1077,12 @@
   }
 
   /// Replaces a string vector value of a given configuration field
-  Future<void> replaceConfigValueStringVector(
+  Future<void> setConfigValueStringVector(
     ChildRef childRef,
     String key,
     List<String> value,
   ) {
-    return replaceConfigValue(
+    return setConfigValue(
       childRef,
       key,
       _vectorValue(fconfig.VectorValue.withStringVector(value)),
@@ -1272,193 +1272,193 @@
   }
 
   /// Replaces a value of a given configuration field
-  Future<void> replaceConfigValue(
+  Future<void> setConfigValue(
     ChildRef childRef,
     String key,
     fconfig.ValueSpec value,
   ) {
-    return rootRealm.replaceConfigValue(childRef, key, value);
+    return rootRealm.setConfigValue(childRef, key, value);
   }
 
   /// Replaces a boolean value of a given configuration field
-  Future<void> replaceConfigValueBool(
+  Future<void> setConfigValueBool(
     ChildRef childRef,
     String key,
     // ignore: avoid_positional_boolean_parameters
     bool value,
   ) {
-    return rootRealm.replaceConfigValueBool(childRef, key, value);
+    return rootRealm.setConfigValueBool(childRef, key, value);
   }
 
   /// Replaces a uint8 value of a given configuration field
-  Future<void> replaceConfigValueUint8(
+  Future<void> setConfigValueUint8(
     ChildRef childRef,
     String key,
     int value,
   ) {
-    return rootRealm.replaceConfigValueUint8(childRef, key, value);
+    return rootRealm.setConfigValueUint8(childRef, key, value);
   }
 
   /// Replaces a uint16 value of a given configuration field
-  Future<void> replaceConfigValueUint16(
+  Future<void> setConfigValueUint16(
     ChildRef childRef,
     String key,
     int value,
   ) {
-    return rootRealm.replaceConfigValueUint16(childRef, key, value);
+    return rootRealm.setConfigValueUint16(childRef, key, value);
   }
 
   /// Replaces a uint32 value of a given configuration field
-  Future<void> replaceConfigValueUint32(
+  Future<void> setConfigValueUint32(
     ChildRef childRef,
     String key,
     int value,
   ) {
-    return rootRealm.replaceConfigValueUint32(childRef, key, value);
+    return rootRealm.setConfigValueUint32(childRef, key, value);
   }
 
   /// Replaces a uint64 value of a given configuration field
-  Future<void> replaceConfigValueUint64(
+  Future<void> setConfigValueUint64(
     ChildRef childRef,
     String key,
     int value,
   ) {
-    return rootRealm.replaceConfigValueUint64(childRef, key, value);
+    return rootRealm.setConfigValueUint64(childRef, key, value);
   }
 
   /// Replaces a int8 value of a given configuration field
-  Future<void> replaceConfigValueInt8(
+  Future<void> setConfigValueInt8(
     ChildRef childRef,
     String key,
     int value,
   ) {
-    return rootRealm.replaceConfigValueInt8(childRef, key, value);
+    return rootRealm.setConfigValueInt8(childRef, key, value);
   }
 
   /// Replaces a int16 value of a given configuration field
-  Future<void> replaceConfigValueInt16(
+  Future<void> setConfigValueInt16(
     ChildRef childRef,
     String key,
     int value,
   ) {
-    return rootRealm.replaceConfigValueInt16(childRef, key, value);
+    return rootRealm.setConfigValueInt16(childRef, key, value);
   }
 
   /// Replaces a int32 value of a given configuration field
-  Future<void> replaceConfigValueInt32(
+  Future<void> setConfigValueInt32(
     ChildRef childRef,
     String key,
     int value,
   ) {
-    return rootRealm.replaceConfigValueInt32(childRef, key, value);
+    return rootRealm.setConfigValueInt32(childRef, key, value);
   }
 
   /// Replaces a int64 value of a given configuration field
-  Future<void> replaceConfigValueInt64(
+  Future<void> setConfigValueInt64(
     ChildRef childRef,
     String key,
     int value,
   ) {
-    return rootRealm.replaceConfigValueInt64(childRef, key, value);
+    return rootRealm.setConfigValueInt64(childRef, key, value);
   }
 
   /// Replaces a string value of a given configuration field
-  Future<void> replaceConfigValueString(
+  Future<void> setConfigValueString(
     ChildRef childRef,
     String key,
     String value,
   ) {
-    return rootRealm.replaceConfigValueString(childRef, key, value);
+    return rootRealm.setConfigValueString(childRef, key, value);
   }
 
   /// Replaces a boolean vector value of a given configuration field
-  Future<void> replaceConfigValueBoolVector(
+  Future<void> setConfigValueBoolVector(
     ChildRef childRef,
     String key,
     List<bool> value,
   ) {
-    return rootRealm.replaceConfigValueBoolVector(childRef, key, value);
+    return rootRealm.setConfigValueBoolVector(childRef, key, value);
   }
 
   /// Replaces a uint8 vector value of a given configuration field
-  Future<void> replaceConfigValueUint8Vector(
+  Future<void> setConfigValueUint8Vector(
     ChildRef childRef,
     String key,
     Uint8List value,
   ) {
-    return rootRealm.replaceConfigValueUint8Vector(childRef, key, value);
+    return rootRealm.setConfigValueUint8Vector(childRef, key, value);
   }
 
   /// Replaces a uint16 vector value of a given configuration field
-  Future<void> replaceConfigValueUint16Vector(
+  Future<void> setConfigValueUint16Vector(
     ChildRef childRef,
     String key,
     Uint16List value,
   ) {
-    return rootRealm.replaceConfigValueUint16Vector(childRef, key, value);
+    return rootRealm.setConfigValueUint16Vector(childRef, key, value);
   }
 
   /// Replaces a uint32 vector value of a given configuration field
-  Future<void> replaceConfigValueUint32Vector(
+  Future<void> setConfigValueUint32Vector(
     ChildRef childRef,
     String key,
     Uint32List value,
   ) {
-    return rootRealm.replaceConfigValueUint32Vector(childRef, key, value);
+    return rootRealm.setConfigValueUint32Vector(childRef, key, value);
   }
 
   /// Replaces a uint64 vector value of a given configuration field
-  Future<void> replaceConfigValueUint64Vector(
+  Future<void> setConfigValueUint64Vector(
     ChildRef childRef,
     String key,
     Uint64List value,
   ) {
-    return rootRealm.replaceConfigValueUint64Vector(childRef, key, value);
+    return rootRealm.setConfigValueUint64Vector(childRef, key, value);
   }
 
   /// Replaces a int8 vector value of a given configuration field
-  Future<void> replaceConfigValueInt8Vector(
+  Future<void> setConfigValueInt8Vector(
     ChildRef childRef,
     String key,
     Int8List value,
   ) {
-    return rootRealm.replaceConfigValueInt8Vector(childRef, key, value);
+    return rootRealm.setConfigValueInt8Vector(childRef, key, value);
   }
 
   /// Replaces a int16 vector value of a given configuration field
-  Future<void> replaceConfigValueInt16Vector(
+  Future<void> setConfigValueInt16Vector(
     ChildRef childRef,
     String key,
     Int16List value,
   ) {
-    return rootRealm.replaceConfigValueInt16Vector(childRef, key, value);
+    return rootRealm.setConfigValueInt16Vector(childRef, key, value);
   }
 
   /// Replaces a int32 vector value of a given configuration field
-  Future<void> replaceConfigValueInt32Vector(
+  Future<void> setConfigValueInt32Vector(
     ChildRef childRef,
     String key,
     Int32List value,
   ) {
-    return rootRealm.replaceConfigValueInt32Vector(childRef, key, value);
+    return rootRealm.setConfigValueInt32Vector(childRef, key, value);
   }
 
   /// Replaces a int64 vector value of a given configuration field
-  Future<void> replaceConfigValueInt64Vector(
+  Future<void> setConfigValueInt64Vector(
     ChildRef childRef,
     String key,
     Int64List value,
   ) {
-    return rootRealm.replaceConfigValueInt64Vector(childRef, key, value);
+    return rootRealm.setConfigValueInt64Vector(childRef, key, value);
   }
 
   /// Replaces a string vector value of a given configuration field
-  Future<void> replaceConfigValueStringVector(
+  Future<void> setConfigValueStringVector(
     ChildRef childRef,
     String key,
     List<String> value,
   ) {
-    return rootRealm.replaceConfigValueStringVector(childRef, key, value);
+    return rootRealm.setConfigValueStringVector(childRef, key, value);
   }
 
   /// Adds a route between components within the realm
diff --git a/sdk/dart/fuchsia_component_test/tests/test/dart_tests.dart b/sdk/dart/fuchsia_component_test/tests/test/dart_tests.dart
index 6176757..251550d 100644
--- a/sdk/dart/fuchsia_component_test/tests/test/dart_tests.dart
+++ b/sdk/dart/fuchsia_component_test/tests/test/dart_tests.dart
@@ -591,7 +591,7 @@
         );
 
         // NOTE: Important! This test updates the default configuration values
-        // in the successful calls to `replaceConfigValue...()` below (after the
+        // in the successful calls to `setConfigValue...()` below (after the
         // try/catch blocks).
         //
         // If this test was changed to run the EchoClient without first
@@ -617,8 +617,7 @@
         // fail to replace a config field in a component that doesn't have a config schema
         var caught = false;
         try {
-          await builder.replaceConfigValueBool(
-              localEchoServer, 'echo_bool', false);
+          await builder.setConfigValueBool(localEchoServer, 'echo_bool', false);
         } on fidl.MethodException<fctest.RealmBuilderError> catch (err) {
           expect(err.value, fctest.RealmBuilderError.noConfigSchema);
           caught = true;
@@ -629,7 +628,7 @@
         // fail to replace a field that doesn't exist
         caught = false;
         try {
-          await builder.replaceConfigValueString(
+          await builder.setConfigValueString(
               v2EchoClientStructuredConfig, 'doesnt_exist', 'test');
         } on fidl.MethodException<fctest.RealmBuilderError> catch (err) {
           expect(err.value, fctest.RealmBuilderError.noSuchConfigField);
@@ -641,7 +640,7 @@
         // fail to replace a field with the wrong type
         caught = false;
         try {
-          await builder.replaceConfigValueString(
+          await builder.setConfigValueString(
               v2EchoClientStructuredConfig, 'echo_bool', 'test');
         } on fidl.MethodException<fctest.RealmBuilderError> catch (err) {
           expect(err.value, fctest.RealmBuilderError.configValueInvalid);
@@ -654,7 +653,7 @@
         final longString = 'F' * 20;
         caught = false;
         try {
-          await builder.replaceConfigValueString(
+          await builder.setConfigValueString(
               v2EchoClientStructuredConfig, 'echo_string', longString);
         } on fidl.MethodException<fctest.RealmBuilderError> catch (err) {
           expect(err.value, fctest.RealmBuilderError.configValueInvalid);
@@ -666,7 +665,7 @@
         // fail to replace a vector whose string element violates max_len
         caught = false;
         try {
-          await builder.replaceConfigValueStringVector(
+          await builder.setConfigValueStringVector(
               v2EchoClientStructuredConfig, 'echo_string_vector', [longString]);
         } on fidl.MethodException<fctest.RealmBuilderError> catch (err) {
           expect(err.value, fctest.RealmBuilderError.configValueInvalid);
@@ -678,7 +677,7 @@
         // fail to replace a vector that violates max_count
         caught = false;
         try {
-          await builder.replaceConfigValueStringVector(
+          await builder.setConfigValueStringVector(
             v2EchoClientStructuredConfig,
             'echo_string_vector',
             ['a', 'b', 'c', 'd'],
@@ -691,15 +690,13 @@
         }
 
         // succeed at replacing all fields with proper constraints
-        await builder.replaceConfigValueString(
+        await builder.setConfigValueString(
             v2EchoClientStructuredConfig, 'echo_string', 'Foobar!');
-        await builder.replaceConfigValueStringVector(
-            v2EchoClientStructuredConfig,
-            'echo_string_vector',
-            ['Hey', 'Folks']);
-        await builder.replaceConfigValueBool(
+        await builder.setConfigValueStringVector(v2EchoClientStructuredConfig,
+            'echo_string_vector', ['Hey', 'Folks']);
+        await builder.setConfigValueBool(
             v2EchoClientStructuredConfig, 'echo_bool', true);
-        await builder.replaceConfigValueUint64(
+        await builder.setConfigValueUint64(
             v2EchoClientStructuredConfig, 'echo_num', 42);
 
         // Route logging to children
diff --git a/sdk/fidl/fuchsia.component.test/fuchsia.component.test.api b/sdk/fidl/fuchsia.component.test/fuchsia.component.test.api
index 9b39c4b..5716bc6 100644
--- a/sdk/fidl/fuchsia.component.test/fuchsia.component.test.api
+++ b/sdk/fidl/fuchsia.component.test/fuchsia.component.test.api
@@ -1,3 +1,3 @@
 {
-  "fidl/fuchsia.component.test": "4280f6d810d6d6ebf9d4097b6cf4522b"
+  "fidl/fuchsia.component.test": "a57bb9cf4d3b5e05fc202e04388a01ac"
 }
\ No newline at end of file
diff --git a/sdk/fidl/fuchsia.component.test/fuchsia.component.test.api_summary.json b/sdk/fidl/fuchsia.component.test/fuchsia.component.test.api_summary.json
index 3d2d2fd..8eaa83e 100644
--- a/sdk/fidl/fuchsia.component.test/fuchsia.component.test.api_summary.json
+++ b/sdk/fidl/fuchsia.component.test/fuchsia.component.test.api_summary.json
@@ -246,16 +246,16 @@
         "name": "fuchsia.component.test/Realm.ReplaceComponentDecl"
     },
     {
-        "declaration": "(string:100 name,string:64 key,fuchsia.component.config/ValueSpec value) -> (fuchsia.component.test/Realm_ReplaceConfigValue_Result result)",
-        "kind": "protocol/member",
-        "name": "fuchsia.component.test/Realm.ReplaceConfigValue"
-    },
-    {
         "declaration": "(fuchsia.component.decl/Component component_decl) -> (fuchsia.component.test/Realm_ReplaceRealmDecl_Result result)",
         "kind": "protocol/member",
         "name": "fuchsia.component.test/Realm.ReplaceRealmDecl"
     },
     {
+        "declaration": "(string:100 name,string:64 key,fuchsia.component.config/ValueSpec value) -> (fuchsia.component.test/Realm_SetConfigValue_Result result)",
+        "kind": "protocol/member",
+        "name": "fuchsia.component.test/Realm.SetConfigValue"
+    },
+    {
         "kind": "protocol",
         "name": "fuchsia.component.test/Realm"
     },
@@ -624,25 +624,6 @@
     },
     {
         "kind": "struct",
-        "name": "fuchsia.component.test/Realm_ReplaceConfigValue_Response"
-    },
-    {
-        "declaration": "fuchsia.component.test/RealmBuilderError",
-        "kind": "union/member",
-        "name": "fuchsia.component.test/Realm_ReplaceConfigValue_Result.err"
-    },
-    {
-        "declaration": "fuchsia.component.test/Realm_ReplaceConfigValue_Response",
-        "kind": "union/member",
-        "name": "fuchsia.component.test/Realm_ReplaceConfigValue_Result.response"
-    },
-    {
-        "kind": "union",
-        "name": "fuchsia.component.test/Realm_ReplaceConfigValue_Result",
-        "strictness": "strict"
-    },
-    {
-        "kind": "struct",
         "name": "fuchsia.component.test/Realm_ReplaceRealmDecl_Response"
     },
     {
@@ -661,6 +642,25 @@
         "strictness": "strict"
     },
     {
+        "kind": "struct",
+        "name": "fuchsia.component.test/Realm_SetConfigValue_Response"
+    },
+    {
+        "declaration": "fuchsia.component.test/RealmBuilderError",
+        "kind": "union/member",
+        "name": "fuchsia.component.test/Realm_SetConfigValue_Result.err"
+    },
+    {
+        "declaration": "fuchsia.component.test/Realm_SetConfigValue_Response",
+        "kind": "union/member",
+        "name": "fuchsia.component.test/Realm_SetConfigValue_Result.response"
+    },
+    {
+        "kind": "union",
+        "name": "fuchsia.component.test/Realm_SetConfigValue_Result",
+        "strictness": "strict"
+    },
+    {
         "declaration": "string:100",
         "kind": "table/member",
         "name": "fuchsia.component.test/Service.as"
diff --git a/sdk/fidl/fuchsia.component.test/realm_builder.fidl b/sdk/fidl/fuchsia.component.test/realm_builder.fidl
index 1dcecfa..329c501 100644
--- a/sdk/fidl/fuchsia.component.test/realm_builder.fidl
+++ b/sdk/fidl/fuchsia.component.test/realm_builder.fidl
@@ -531,15 +531,23 @@
         directory_contents DirectoryContents;
     }) -> (struct {}) error RealmBuilderError;
 
-    /// Replaces the configuration value for a field specified by `key`.
-    /// The component specified should have a config schema with this field.
+    @available(removed=9)
+    ReplaceConfigValue(struct {
+        name fuchsia.component.name;
+        key fuchsia.component.decl.ConfigKey;
+        value fuchsia.component.config.ValueSpec;
+    }) -> (struct {}) error RealmBuilderError;
+
+    /// Sets the configuration value for a field specified by `key`.
+    /// The component specified must have a config schema with this field.
     /// The value must conform to all constraints as defined by the schema.
     ///
     /// Errors:
     /// - `NO_CONFIG_SCHEMA`: component does not have a config schema
     /// - `NO_SUCH_CONFIG_FIELD`: `key` could not be found in component's config schema
     /// - `CONFIG_VALUE_INVALID`: `value` does not meet config schema constraints
-    ReplaceConfigValue(struct {
+    @available(added=9)
+    SetConfigValue(struct {
         /// The name of the component whose config value is being replaced.
         name fuchsia.component.name;
 
diff --git a/sdk/fidl/fuchsia.device.composite/BUILD.gn b/sdk/fidl/fuchsia.device.composite/BUILD.gn
new file mode 100644
index 0000000..e4b47e6
--- /dev/null
+++ b/sdk/fidl/fuchsia.device.composite/BUILD.gn
@@ -0,0 +1,17 @@
+# Copyright 2022 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/fidl/fidl.gni")
+
+fidl("fuchsia.device.composite") {
+  sources = [ "device.fidl" ]
+
+  public_deps = [ "//sdk/fidl/fuchsia.device.manager" ]
+
+  # This should only be used by the compat shim.
+  visibility = [
+    "//src/devices/bin/driver_manager/v2:driver_runner",
+    "//src/devices/misc/drivers/compat:lib",
+  ]
+}
diff --git a/sdk/fidl/fuchsia.device.composite/device.fidl b/sdk/fidl/fuchsia.device.composite/device.fidl
new file mode 100644
index 0000000..93da390
--- /dev/null
+++ b/sdk/fidl/fuchsia.device.composite/device.fidl
@@ -0,0 +1,17 @@
+// Copyright 2022 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+library fuchsia.device.composite;
+
+using fuchsia.device.manager;
+using zx;
+
+// This protocol is used by DFv1 drivers to add composite devices.
+// Newer drivers should use DeviceGroups.
+@discoverable
+protocol DeprecatedCompositeCreator {
+    AddCompositeDevice(struct {
+        name string:MAX;
+        args fuchsia.device.manager.CompositeDeviceDescriptor;
+    }) -> (struct {}) error zx.status;
+};
diff --git a/sdk/fidl/fuchsia.gpu.agis/agis.fidl b/sdk/fidl/fuchsia.gpu.agis/agis.fidl
index 7d16597..01804a6 100644
--- a/sdk/fidl/fuchsia.gpu.agis/agis.fidl
+++ b/sdk/fidl/fuchsia.gpu.agis/agis.fidl
@@ -7,27 +7,33 @@
 
 using zx;
 
+/// Client supplied id.
+alias ClientId = uint64;
+
+/// Agis internally generated id.
+alias GlobalId = uint32;
+
 /// AGIS provides FIDL services that facilitate Vulkan command tracing.
 /// It acts as an intermediary between gapii, the Android GPU Inspector
 /// interposing shared library and Vulkan layer, and the host Android GPU
-/// Inspector application.
+/// Inspector (AGI) application.
 ///
-/// Vtc == Vulkan Traceable Component
+/// Vulkan Traceable Component (vtc)
 type Vtc = resource table {
-    1: process_koid zx.koid;
-    2: process_name string:zx.MAX_NAME_LEN;
-    3: agi_socket zx.handle:SOCKET;
+    1: global_id GlobalId;
+    2: process_koid zx.koid;
+    3: process_name string:zx.MAX_NAME_LEN;
 };
 
 /// Max vtcs that may be registered.
 /// ZX_CHANNEL_MAX_MESSAGE_BYTES = 64KiB.  At an approximate message size
-/// of 50B, rounding up to 64B, we easily fit 512 vtcs in a message
+/// of 50B, rounding up to 64B, we easily fit 128 vtcs in a message
 /// with ample headroom for message size growth.
 ///
 /// The number of vtcs is determined by developers, rather than users.
 /// The typical expected vtc count is 1 considering a developer
 /// performing Vulkan tracing on 1 component at a time.
-const MAX_VTCS uint32 = 512;
+const MAX_VTCS uint32 = 128;
 
 type Error = flexible enum {
     NOT_FOUND = 1;
@@ -36,33 +42,39 @@
     INTERNAL_ERROR = 4;
 };
 
-/// The AGIS ComponentRegistry protocol allows traceable components to register as
-/// Vulkan traceable.
+/// The AGIS ComponentRegistry protocol allows traceable Fuchsia components to register
+/// as Vulkan traceable.
 @discoverable
 protocol ComponentRegistry {
-    /// Register a process as traceable and retrieve its bound socket.
-    /// For AGI, gapii will be the only client of this Register interface.
+    /// Register a process as traceable.
     Register(struct {
-        /// Client assigned unique ID for the vtc.
-        id uint64;
+        /// Client assigned ID for the vtc.
+        id ClientId;
 
         /// Process koid.
         process_koid zx.koid;
 
         /// Must match ZX_PROP_NAME of the kernel object.
         process_name string:zx.MAX_NAME_LEN;
-    }) -> (resource struct {
-        gapii_socket zx.handle:SOCKET;
-    }) error Error;
-
-    /// Remove an entry from the registry.
-    Unregister(struct {
-        id uint64;
     }) -> (struct {}) error Error;
+
+    // Unregister |global_id| from the registry.
+    Unregister(struct {
+        id ClientId;
+    }) -> (struct {}) error Error;
+
+    /// Hanging get to retrieve the Vulkan endpoint of the ffx/vulkan Zircon
+    /// socket pair.  This get is first satisfied when a client
+    /// (e.g. the AGI application) calls Listener::Listen() on the same |global_id|.
+    GetVulkanSocket(struct {
+        id ClientId;
+    }) -> (resource struct {
+        socket zx.handle:<SOCKET, optional>;
+    }) error Error;
 };
 
-/// The AGIS Observer protocol provides the interface to retrieve registered
-/// Vulkan Traceable Components.
+/// The AGIS Observer protocol provides the interface to retrieve the full list of
+/// registered Vulkan Traceable Components (vtcs).
 @discoverable
 protocol Observer {
     /// Retrieve registered components.
@@ -71,3 +83,16 @@
         vtcs vector<Vtc>:MAX_VTCS;
     }) error Error;
 };
+
+/// The AGIS Listener protocol provides the interface to initiate listening
+/// to a Vulkan Traceable Component.  Retrieves the ffx socket endpoint of the
+/// ffx/vulkan socket pair.
+@discoverable
+protocol Listener {
+    /// Initiate listening to the provided |vtc|.
+    Listen(struct {
+        global_id GlobalId;
+    }) -> (resource struct {
+        socket zx.handle:SOCKET;
+    }) error Error;
+};
diff --git a/sdk/fidl/fuchsia.inspect/fuchsia.inspect.api b/sdk/fidl/fuchsia.inspect/fuchsia.inspect.api
index 9865ecc..bf73112 100644
--- a/sdk/fidl/fuchsia.inspect/fuchsia.inspect.api
+++ b/sdk/fidl/fuchsia.inspect/fuchsia.inspect.api
@@ -1,3 +1,3 @@
 {
-  "fidl/fuchsia.inspect": "c03e896ec92f246885b1861ebbd402b0"
+  "fidl/fuchsia.inspect": "f61c867ce3d10ded90a889c076840091"
 }
\ No newline at end of file
diff --git a/sdk/fidl/fuchsia.inspect/fuchsia.inspect.api_summary.json b/sdk/fidl/fuchsia.inspect/fuchsia.inspect.api_summary.json
index bfbf654..e99b82d 100644
--- a/sdk/fidl/fuchsia.inspect/fuchsia.inspect.api_summary.json
+++ b/sdk/fidl/fuchsia.inspect/fuchsia.inspect.api_summary.json
@@ -6,12 +6,6 @@
         "value": "2040"
     },
     {
-        "declaration": "uint64",
-        "kind": "const",
-        "name": "fuchsia.inspect/MAX_TREE_NAME_LIST_SIZE",
-        "value": "64"
-    },
-    {
         "declaration": "() -> (fuchsia.inspect/TreeContent content)",
         "kind": "protocol/member",
         "name": "fuchsia.inspect/Tree.GetContent"
@@ -41,7 +35,7 @@
         "resourceness": "resource"
     },
     {
-        "declaration": "() -> (vector<string:2040>:64 name)",
+        "declaration": "() -> (vector<string:2040> name)",
         "kind": "protocol/member",
         "name": "fuchsia.inspect/TreeNameIterator.GetNext"
     },
diff --git a/sdk/fidl/fuchsia.inspect/tree.fidl b/sdk/fidl/fuchsia.inspect/tree.fidl
index fb0abd9..549e155 100644
--- a/sdk/fidl/fuchsia.inspect/tree.fidl
+++ b/sdk/fidl/fuchsia.inspect/tree.fidl
@@ -9,6 +9,7 @@
 const MAX_TREE_NAME_LENGTH uint64 = 2040;
 
 /// Maximum number of children returned by a single read of the tree name iterator.
+@available(removed=9)
 const MAX_TREE_NAME_LIST_SIZE uint64 = 64;
 
 alias TreeName = string:MAX_TREE_NAME_LENGTH;
@@ -25,6 +26,16 @@
     ///
     /// Returns an empty vector and closes the channel when no more names are present.
     /// Implementors may eagerly close the channel after sending the last batch.
+    @available(added=9)
+    GetNext() -> (struct {
+        name vector<TreeName>:MAX;
+    });
+
+    /// Get the next batch of names.
+    ///
+    /// Returns an empty vector and closes the channel when no more names are present.
+    /// Implementors may eagerly close the channel after sending the last batch.
+    @available(removed=9)
     GetNext() -> (struct {
         name vector<TreeName>:MAX_TREE_NAME_LIST_SIZE;
     });
diff --git a/sdk/fidl/fuchsia.io/BUILD.gn b/sdk/fidl/fuchsia.io/BUILD.gn
index 6f67862..172c649 100644
--- a/sdk/fidl/fuchsia.io/BUILD.gn
+++ b/sdk/fidl/fuchsia.io/BUILD.gn
@@ -13,7 +13,6 @@
     "connection-options.fidl",
     "directory.fidl",
     "directory2.fidl",
-    "file.fidl",
     "file2.fidl",
     "inotify.fidl",
     "io.fidl",
diff --git a/sdk/fidl/fuchsia.io/connection-info.fidl b/sdk/fidl/fuchsia.io/connection-info.fidl
index c8b45b8..961ed30 100644
--- a/sdk/fidl/fuchsia.io/connection-info.fidl
+++ b/sdk/fidl/fuchsia.io/connection-info.fidl
@@ -98,23 +98,12 @@
             1: buffer fuchsia.mem.Range;
         };
 
-        /// The pipe representation of a node.
-        /// A pipe is a data streaming interface, commonly used for standard in/out.
-        /// There is no universal requirement as to if it is uni- or bi-directional.
-        /// The selection of this variant in [`Representation`] implies that the
-        /// connection speaks the [`Pipe`] protocol.
+        /// The connection composes [`fuchsia.hardware.block.BlockAndNode`] OR
+        /// [`fuchsia.hardware.block.volume.VolumeAndNode`].
         ///
-        /// See [`NodeProtocols.PIPE`].
-        5: pipe @generated_name("PipeInfo") resource table {
-            /// The backing socket transport for the pipe.
-            /// The rights on this socket should correspond to the rights on the
-            /// node connection.
-            1: socket zx.handle:SOCKET;
-        };
-
-        /// The object may be cast to the shared interface of devices.
-        @deprecated("devices will be services in the future")
-        6: device @generated_name("DeviceInfo") resource table {
+        /// TODO(https://fxbug.dev/103827): Document why this is needed or
+        /// remove it.
+        5: device @generated_name("DeviceInfo") resource table {
             /// An optional event which transmits information about a device's state.
             ///
             /// The [`DeviceSignal`] values may be observed on this event.
@@ -123,7 +112,7 @@
 
         /// The object may be cast to a Tty interface.
         @deprecated("tty functionalities may be covered by a tty service")
-        7: tty @generated_name("TtyInfo") resource table {
+        6: tty @generated_name("TtyInfo") resource table {
             /// An optional event which transmits information about a device's state.
             ///
             /// The [`DeviceSignal`] values may be observed on this event.
@@ -133,7 +122,7 @@
         /// The connection composes [`fuchsia.posix.socket/SynchronousDatagramSocket`].
         ///
         /// See [`NodeProtocols.SYNCHRONOUS_DATAGRAM_SOCKET`].
-        8: synchronous_datagram_socket
+        7: synchronous_datagram_socket
                 @generated_name("SynchronousDatagramSocketInfo")
                 resource table {
             1: event zx.handle:EVENTPAIR;
@@ -142,7 +131,7 @@
         /// The connection composes [`fuchsia.posix.socket/DatagramSocket`].
         ///
         /// See [`NodeProtocols.DATAGRAM_SOCKET`].
-        9: datagram_socket @generated_name("DatagramSocketInfo") resource table {
+        8: datagram_socket @generated_name("DatagramSocketInfo") resource table {
             /// See [`fuchsia.posix.socket.DatagramSocket`] for details.
             1: socket zx.handle:SOCKET;
             /// Size of the buffer used to receive Tx metadata.
@@ -154,21 +143,21 @@
         /// The connection composes [`fuchsia.posix.socket/StreamSocket`].
         ///
         /// See [`NodeProtocols.STREAM_SOCKET`].
-       10: stream_socket @generated_name("StreamSocketInfo") resource table {
+        9: stream_socket @generated_name("StreamSocketInfo") resource table {
             1: socket zx.handle:SOCKET;
         };
 
         /// The connection composes [`fuchsia.posix.socket.raw/Socket`].
         ///
         /// See [`NodeProtocols.RAW_SOCKET`].
-       11: raw_socket @generated_name("RawSocketInfo") resource table {
+       10: raw_socket @generated_name("RawSocketInfo") resource table {
             1: event zx.handle:EVENTPAIR;
         };
 
         /// The connection composes [`fuchsia.posix.socket.packet/Socket`].
         ///
         /// See [`NodeProtocols.PACKET_SOCKET`].
-       12: packet_socket @generated_name("PacketSocketInfo") resource table {
+       11: packet_socket @generated_name("PacketSocketInfo") resource table {
             /// See [`fuchsia.posix.socket.packet.Socket`] for details.
             1: event zx.handle:EVENTPAIR;
         };
diff --git a/sdk/fidl/fuchsia.io/file.fidl b/sdk/fidl/fuchsia.io/file.fidl
deleted file mode 100644
index 2879ac4..0000000
--- a/sdk/fidl/fuchsia.io/file.fidl
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2018 The Fuchsia Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-library fuchsia.io;
-
-using zx;
-
-protocol File {
-    compose File1;
-    compose File2;
-};
-
-/// File defines the interface of a node which contains a flat layout of data.
-protocol File1 {
-    compose Node1;
-
-    /// Writes data at the seek offset.
-    /// The seek offset is moved forward by the number of bytes written.
-    ///
-    /// This method requires following rights: `OpenFlags.RIGHT_WRITABLE`, otherwise returns
-    /// `ZX_ERR_BAD_HANDLE`.
-    // TODO(https://fxbug.dev/88872): Remove this method when pkgfs has been deleted.
-    @transitional("only implemented by pkgfs")
-    @selector("fuchsia.io1/File.Write")
-    @deprecated("replaced by File2.Write")
-    WriteDeprecated(struct {
-        data vector<uint8>:MAX_BUF;
-    }) -> (struct {
-        s zx.status;
-        actual uint64;
-    });
-};
diff --git a/sdk/fidl/fuchsia.io/file2.fidl b/sdk/fidl/fuchsia.io/file2.fidl
index 6789aed..6db960e 100644
--- a/sdk/fidl/fuchsia.io/file2.fidl
+++ b/sdk/fidl/fuchsia.io/file2.fidl
@@ -14,9 +14,14 @@
 /// The byte vector type used for read/write operations.
 alias Transfer = bytes:MAX_TRANSFER_SIZE;
 
+protocol File {
+    compose File2;
+};
+
 /// A [`Node2`] which contains a sequence of bytes of definite
 /// length.
 protocol File2 {
+    compose Node1;
     compose Node2;
     compose AdvisoryLocking;
 
@@ -173,34 +178,32 @@
     /// * [`Rights.EXECUTE`] if `flags` includes [`VmoFlags.EXECUTE`].
     @selector("fuchsia.io/File.GetBackingMemory")
     GetBackingMemory(struct {
-        flags VmoFlags;
+        flags @generated_name("VmoFlags") strict bits : uint32 {
+            /// Requests that the VMO be readable.
+            READ = 0x00000001;
+
+            /// Requests that the VMO be writable.
+            WRITE = 0x00000002;
+
+            /// Request that the VMO be executable.
+            EXECUTE = 0x00000004;
+
+            /// Require a copy-on-write clone of the underlying VMO.
+            /// The request should fail if the VMO cannot be cloned.
+            /// May not be supplied with `SHARED_BUFFER`.
+            PRIVATE_CLONE = 0x00010000;
+
+            /// Require an exact (non-cloned) handle to the underlying VMO.
+            /// All clients using this flag would get a VMO with the same koid.
+            /// The request should fail if a handle to the exact VMO cannot be returned.
+            /// May not be supplied with `PRIVATE_CLONE`.
+            SHARED_BUFFER = 0x00020000;
+        };
     }) -> (resource struct {
         vmo zx.handle:VMO;
     }) error zx.status;
 };
 
-type VmoFlags = strict bits : uint32 {
-    /// Requests that the VMO be readable.
-    READ = 0x00000001;
-
-    /// Requests that the VMO be writable.
-    WRITE = 0x00000002;
-
-    /// Request that the VMO be executable.
-    EXECUTE = 0x00000004;
-
-    /// Require a copy-on-write clone of the underlying VMO.
-    /// The request should fail if the VMO cannot be cloned.
-    /// May not be supplied with `SHARED_BUFFER`.
-    PRIVATE_CLONE = 0x00010000;
-
-    /// Require an exact (non-cloned) handle to the underlying VMO.
-    /// All clients using this flag would get a VMO with the same koid.
-    /// The request should fail if a handle to the exact VMO cannot be returned.
-    /// May not be supplied with `PRIVATE_CLONE`.
-    SHARED_BUFFER = 0x00020000;
-};
-
 /// The reference point for updating the seek offset. See [`File.Seek`].
 ///
 /// This enum matches the `zx_stream_seek_origin_t` enum.
diff --git a/sdk/fidl/fuchsia.io/fuchsia.io.api b/sdk/fidl/fuchsia.io/fuchsia.io.api
index 10c81a9..0afb70f 100644
--- a/sdk/fidl/fuchsia.io/fuchsia.io.api
+++ b/sdk/fidl/fuchsia.io/fuchsia.io.api
@@ -1,3 +1,3 @@
 {
-  "fidl/fuchsia.io": "ec52f4a380b648579be5ca0f9e51d85f"
+  "fidl/fuchsia.io": "dfd05899e8f7f066ef71daaa5fa5f274"
 }
\ No newline at end of file
diff --git a/sdk/fidl/fuchsia.io/fuchsia.io.api_summary.json b/sdk/fidl/fuchsia.io/fuchsia.io.api_summary.json
index a818ab2..0aaf433 100644
--- a/sdk/fidl/fuchsia.io/fuchsia.io.api_summary.json
+++ b/sdk/fidl/fuchsia.io/fuchsia.io.api_summary.json
@@ -817,114 +817,40 @@
         "name": "fuchsia.io/File.WriteAt"
     },
     {
-        "declaration": "(vector<uint8>:8192 data) -> (int32 s,uint64 actual)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File.WriteDeprecated"
-    },
-    {
         "kind": "protocol",
         "name": "fuchsia.io/File"
     },
     {
-        "declaration": "(fuchsia.io/OpenFlags flags,server_end:fuchsia.io/Node object)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.Clone"
-    },
-    {
-        "declaration": "() -> (fuchsia.io/Node2_Close_Result result)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.Close"
-    },
-    {
-        "declaration": "() -> (fuchsia.io/NodeInfo info)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.Describe"
-    },
-    {
-        "declaration": "(fuchsia.io/ConnectionInfoQuery query) -> (fuchsia.io/ConnectionInfo payload)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.Describe2"
-    },
-    {
-        "declaration": "() -> (int32 s,fuchsia.io/NodeAttributes attributes)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.GetAttr"
-    },
-    {
-        "declaration": "(fuchsia.io/NodeAttributesQuery query) -> (fuchsia.io/Node2_GetAttributes_Result result)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.GetAttributes"
-    },
-    {
-        "declaration": "() -> (int32 s,fuchsia.io/OpenFlags flags)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.GetFlags"
-    },
-    {
-        "declaration": " -> (fuchsia.io/ConnectionInfo payload)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.OnConnectionInfo"
-    },
-    {
-        "declaration": " -> (int32 s,fuchsia.io/NodeInfo:optional info)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.OnOpen"
-    },
-    {
-        "declaration": "() -> (int32 s,box<fuchsia.io/FilesystemInfo> info)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.QueryFilesystem"
-    },
-    {
-        "declaration": "(fuchsia.io/ConnectionOptions options,zx/handle:CHANNEL object_request)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.Reopen"
-    },
-    {
-        "declaration": "(fuchsia.io/NodeAttributeFlags flags,fuchsia.io/NodeAttributes attributes) -> (int32 s)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.SetAttr"
-    },
-    {
-        "declaration": "(fuchsia.io/OpenFlags flags) -> (int32 s)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.SetFlags"
-    },
-    {
-        "declaration": "() -> (fuchsia.io/Node2_Sync_Result result)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.Sync"
-    },
-    {
-        "declaration": "(fuchsia.io/MutableNodeAttributes payload) -> (fuchsia.io/Node2_UpdateAttributes_Result result)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.UpdateAttributes"
-    },
-    {
-        "declaration": "(vector<uint8>:8192 data) -> (int32 s,uint64 actual)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.WriteDeprecated"
-    },
-    {
-        "kind": "protocol",
-        "name": "fuchsia.io/File1"
-    },
-    {
         "declaration": "(fuchsia.io/AdvisoryLockRequest request) -> (fuchsia.io/AdvisoryLocking_AdvisoryLock_Result result)",
         "kind": "protocol/member",
         "name": "fuchsia.io/File2.AdvisoryLock"
     },
     {
+        "declaration": "(fuchsia.io/OpenFlags flags,server_end:fuchsia.io/Node object)",
+        "kind": "protocol/member",
+        "name": "fuchsia.io/File2.Clone"
+    },
+    {
         "declaration": "() -> (fuchsia.io/Node2_Close_Result result)",
         "kind": "protocol/member",
         "name": "fuchsia.io/File2.Close"
     },
     {
+        "declaration": "() -> (fuchsia.io/NodeInfo info)",
+        "kind": "protocol/member",
+        "name": "fuchsia.io/File2.Describe"
+    },
+    {
         "declaration": "(fuchsia.io/ConnectionInfoQuery query) -> (fuchsia.io/ConnectionInfo payload)",
         "kind": "protocol/member",
         "name": "fuchsia.io/File2.Describe2"
     },
     {
+        "declaration": "() -> (int32 s,fuchsia.io/NodeAttributes attributes)",
+        "kind": "protocol/member",
+        "name": "fuchsia.io/File2.GetAttr"
+    },
+    {
         "declaration": "(fuchsia.io/NodeAttributesQuery query) -> (fuchsia.io/Node2_GetAttributes_Result result)",
         "kind": "protocol/member",
         "name": "fuchsia.io/File2.GetAttributes"
@@ -935,11 +861,26 @@
         "name": "fuchsia.io/File2.GetBackingMemory"
     },
     {
+        "declaration": "() -> (int32 s,fuchsia.io/OpenFlags flags)",
+        "kind": "protocol/member",
+        "name": "fuchsia.io/File2.GetFlags"
+    },
+    {
         "declaration": " -> (fuchsia.io/ConnectionInfo payload)",
         "kind": "protocol/member",
         "name": "fuchsia.io/File2.OnConnectionInfo"
     },
     {
+        "declaration": " -> (int32 s,fuchsia.io/NodeInfo:optional info)",
+        "kind": "protocol/member",
+        "name": "fuchsia.io/File2.OnOpen"
+    },
+    {
+        "declaration": "() -> (int32 s,box<fuchsia.io/FilesystemInfo> info)",
+        "kind": "protocol/member",
+        "name": "fuchsia.io/File2.QueryFilesystem"
+    },
+    {
         "declaration": "(uint64 count) -> (fuchsia.io/File2_Read_Result result)",
         "kind": "protocol/member",
         "name": "fuchsia.io/File2.Read"
@@ -965,6 +906,16 @@
         "name": "fuchsia.io/File2.Seek"
     },
     {
+        "declaration": "(fuchsia.io/NodeAttributeFlags flags,fuchsia.io/NodeAttributes attributes) -> (int32 s)",
+        "kind": "protocol/member",
+        "name": "fuchsia.io/File2.SetAttr"
+    },
+    {
+        "declaration": "(fuchsia.io/OpenFlags flags) -> (int32 s)",
+        "kind": "protocol/member",
+        "name": "fuchsia.io/File2.SetFlags"
+    },
+    {
         "declaration": "() -> (fuchsia.io/Node2_Sync_Result result)",
         "kind": "protocol/member",
         "name": "fuchsia.io/File2.Sync"
@@ -1970,11 +1921,6 @@
         "name": "fuchsia.io/NodeInfo.packet_socket"
     },
     {
-        "declaration": "fuchsia.io/PipeObject",
-        "kind": "union/member",
-        "name": "fuchsia.io/NodeInfo.pipe"
-    },
-    {
         "declaration": "fuchsia.io/RawSocket",
         "kind": "union/member",
         "name": "fuchsia.io/NodeInfo.raw_socket"
@@ -2017,11 +1963,6 @@
     },
     {
         "kind": "bits/member",
-        "name": "fuchsia.io/NodeProtocols.DATAGRAM_SOCKET",
-        "value": "512"
-    },
-    {
-        "kind": "bits/member",
         "name": "fuchsia.io/NodeProtocols.DEVICE",
         "value": "268435456"
     },
@@ -2042,31 +1983,6 @@
     },
     {
         "kind": "bits/member",
-        "name": "fuchsia.io/NodeProtocols.PACKET_SOCKET",
-        "value": "256"
-    },
-    {
-        "kind": "bits/member",
-        "name": "fuchsia.io/NodeProtocols.PIPE",
-        "value": "16"
-    },
-    {
-        "kind": "bits/member",
-        "name": "fuchsia.io/NodeProtocols.RAW_SOCKET",
-        "value": "128"
-    },
-    {
-        "kind": "bits/member",
-        "name": "fuchsia.io/NodeProtocols.STREAM_SOCKET",
-        "value": "64"
-    },
-    {
-        "kind": "bits/member",
-        "name": "fuchsia.io/NodeProtocols.SYNCHRONOUS_DATAGRAM_SOCKET",
-        "value": "32"
-    },
-    {
-        "kind": "bits/member",
         "name": "fuchsia.io/NodeProtocols.TTY",
         "value": "536870912"
     },
@@ -2262,26 +2178,6 @@
         "resourceness": "resource"
     },
     {
-        "declaration": "zx/handle:SOCKET",
-        "kind": "table/member",
-        "name": "fuchsia.io/PipeInfo.socket"
-    },
-    {
-        "kind": "table",
-        "name": "fuchsia.io/PipeInfo",
-        "resourceness": "resource"
-    },
-    {
-        "declaration": "zx/handle:SOCKET",
-        "kind": "struct/member",
-        "name": "fuchsia.io/PipeObject.socket"
-    },
-    {
-        "kind": "struct",
-        "name": "fuchsia.io/PipeObject",
-        "resourceness": "resource"
-    },
-    {
         "declaration": "fuchsia.io/Operations",
         "kind": "const",
         "name": "fuchsia.io/RW_STAR_DIR",
@@ -2355,11 +2251,6 @@
         "name": "fuchsia.io/Representation.packet_socket"
     },
     {
-        "declaration": "fuchsia.io/PipeInfo",
-        "kind": "union/member",
-        "name": "fuchsia.io/Representation.pipe"
-    },
-    {
         "declaration": "fuchsia.io/RawSocketInfo",
         "kind": "union/member",
         "name": "fuchsia.io/Representation.raw_socket"
diff --git a/sdk/fidl/fuchsia.io/node-protocols.fidl b/sdk/fidl/fuchsia.io/node-protocols.fidl
index d3e2f1d..b796142 100644
--- a/sdk/fidl/fuchsia.io/node-protocols.fidl
+++ b/sdk/fidl/fuchsia.io/node-protocols.fidl
@@ -40,31 +40,12 @@
     /// representing the contents of the file.
     MEMORY = 0x8;
 
-    /// The pipe representation of a node.
-    /// The connection will speak the [`Pipe`] protocol.
-    PIPE = 0x10;
-
-    /// The datagram socket representation of a node.
-    /// The connection will speak the [`fuchsia.posix.socket/SynchronousDatagramSocket`] protocol.
-    SYNCHRONOUS_DATAGRAM_SOCKET = 0x20;
-
-    /// The stream socket representation of a node.
-    /// The connection will speak the [`fuchsia.posix.socket/StreamSocket`] protocol.
-    STREAM_SOCKET = 0x40;
-
-    /// The raw socket representation of a node.
-    /// The connection will speak the [`fuchsia.posix.socket.raw/Socket`] protocol.
-    RAW_SOCKET = 0x80;
-
-    /// The packet socket representation of a node.
-    /// The connection will speak the [`fuchsia.posix.socket.packet/Socket`] protocol.
-    PACKET_SOCKET = 0x100;
-
-    /// The datagram socket representation of a node.
-    /// The connection will speak the [`fuchsia.posix.socket/DatagramSocket`] protocol.
-    DATAGRAM_SOCKET = 0x200;
-
-    @deprecated("devices will be services in the future")
+    /// The device representation of a node.
+    ///
+    /// The connection will speak [`fuchsia.hardware.block.BlockAndNode`] OR
+    /// [`fuchsia.hardware.block.volume.VolumeAndNode`].
+    ///
+    /// TODO(https://fxbug.dev/103827): Document why this is needed or remove it.
     DEVICE = 0x10000000;
 
     @deprecated("tty functionalities may be covered by a tty service")
diff --git a/sdk/fidl/fuchsia.io/node.fidl b/sdk/fidl/fuchsia.io/node.fidl
index 60d115a..a723252 100644
--- a/sdk/fidl/fuchsia.io/node.fidl
+++ b/sdk/fidl/fuchsia.io/node.fidl
@@ -32,10 +32,7 @@
     /// The connection composes [`Directory`].
     3: directory @generated_name("DirectoryObject") struct {};
 
-    /// The connection composes [`Pipe`].
-    4: pipe @generated_name("PipeObject") resource struct {
-        socket zx.handle:SOCKET;
-    };
+    4: reserved;
 
     /// The connection composes [`File`]. Its implementation is backed by a VMO.
     5: vmofile resource struct {
@@ -46,6 +43,12 @@
         /// The number of bytes, starting at `offset`, which may be used to represent this file.
         length uint64;
     };
+
+    /// The connection composes [`fuchsia.hardware.block.BlockAndNode`] OR
+    /// [`fuchsia.hardware.block.volume.VolumeAndNode`].
+    ///
+    /// TODO(https://fxbug.dev/103827): Document why this is needed or remove
+    /// it.
     6: device resource struct {
         /// An optional event which transmits information about a device's state.
         ///
diff --git a/sdk/history/7/fuchsia.io.api_summary.json b/sdk/history/7/fuchsia.io.api_summary.json
index a818ab2..0aaf433 100644
--- a/sdk/history/7/fuchsia.io.api_summary.json
+++ b/sdk/history/7/fuchsia.io.api_summary.json
@@ -817,114 +817,40 @@
         "name": "fuchsia.io/File.WriteAt"
     },
     {
-        "declaration": "(vector<uint8>:8192 data) -> (int32 s,uint64 actual)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File.WriteDeprecated"
-    },
-    {
         "kind": "protocol",
         "name": "fuchsia.io/File"
     },
     {
-        "declaration": "(fuchsia.io/OpenFlags flags,server_end:fuchsia.io/Node object)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.Clone"
-    },
-    {
-        "declaration": "() -> (fuchsia.io/Node2_Close_Result result)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.Close"
-    },
-    {
-        "declaration": "() -> (fuchsia.io/NodeInfo info)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.Describe"
-    },
-    {
-        "declaration": "(fuchsia.io/ConnectionInfoQuery query) -> (fuchsia.io/ConnectionInfo payload)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.Describe2"
-    },
-    {
-        "declaration": "() -> (int32 s,fuchsia.io/NodeAttributes attributes)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.GetAttr"
-    },
-    {
-        "declaration": "(fuchsia.io/NodeAttributesQuery query) -> (fuchsia.io/Node2_GetAttributes_Result result)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.GetAttributes"
-    },
-    {
-        "declaration": "() -> (int32 s,fuchsia.io/OpenFlags flags)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.GetFlags"
-    },
-    {
-        "declaration": " -> (fuchsia.io/ConnectionInfo payload)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.OnConnectionInfo"
-    },
-    {
-        "declaration": " -> (int32 s,fuchsia.io/NodeInfo:optional info)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.OnOpen"
-    },
-    {
-        "declaration": "() -> (int32 s,box<fuchsia.io/FilesystemInfo> info)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.QueryFilesystem"
-    },
-    {
-        "declaration": "(fuchsia.io/ConnectionOptions options,zx/handle:CHANNEL object_request)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.Reopen"
-    },
-    {
-        "declaration": "(fuchsia.io/NodeAttributeFlags flags,fuchsia.io/NodeAttributes attributes) -> (int32 s)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.SetAttr"
-    },
-    {
-        "declaration": "(fuchsia.io/OpenFlags flags) -> (int32 s)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.SetFlags"
-    },
-    {
-        "declaration": "() -> (fuchsia.io/Node2_Sync_Result result)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.Sync"
-    },
-    {
-        "declaration": "(fuchsia.io/MutableNodeAttributes payload) -> (fuchsia.io/Node2_UpdateAttributes_Result result)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.UpdateAttributes"
-    },
-    {
-        "declaration": "(vector<uint8>:8192 data) -> (int32 s,uint64 actual)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.WriteDeprecated"
-    },
-    {
-        "kind": "protocol",
-        "name": "fuchsia.io/File1"
-    },
-    {
         "declaration": "(fuchsia.io/AdvisoryLockRequest request) -> (fuchsia.io/AdvisoryLocking_AdvisoryLock_Result result)",
         "kind": "protocol/member",
         "name": "fuchsia.io/File2.AdvisoryLock"
     },
     {
+        "declaration": "(fuchsia.io/OpenFlags flags,server_end:fuchsia.io/Node object)",
+        "kind": "protocol/member",
+        "name": "fuchsia.io/File2.Clone"
+    },
+    {
         "declaration": "() -> (fuchsia.io/Node2_Close_Result result)",
         "kind": "protocol/member",
         "name": "fuchsia.io/File2.Close"
     },
     {
+        "declaration": "() -> (fuchsia.io/NodeInfo info)",
+        "kind": "protocol/member",
+        "name": "fuchsia.io/File2.Describe"
+    },
+    {
         "declaration": "(fuchsia.io/ConnectionInfoQuery query) -> (fuchsia.io/ConnectionInfo payload)",
         "kind": "protocol/member",
         "name": "fuchsia.io/File2.Describe2"
     },
     {
+        "declaration": "() -> (int32 s,fuchsia.io/NodeAttributes attributes)",
+        "kind": "protocol/member",
+        "name": "fuchsia.io/File2.GetAttr"
+    },
+    {
         "declaration": "(fuchsia.io/NodeAttributesQuery query) -> (fuchsia.io/Node2_GetAttributes_Result result)",
         "kind": "protocol/member",
         "name": "fuchsia.io/File2.GetAttributes"
@@ -935,11 +861,26 @@
         "name": "fuchsia.io/File2.GetBackingMemory"
     },
     {
+        "declaration": "() -> (int32 s,fuchsia.io/OpenFlags flags)",
+        "kind": "protocol/member",
+        "name": "fuchsia.io/File2.GetFlags"
+    },
+    {
         "declaration": " -> (fuchsia.io/ConnectionInfo payload)",
         "kind": "protocol/member",
         "name": "fuchsia.io/File2.OnConnectionInfo"
     },
     {
+        "declaration": " -> (int32 s,fuchsia.io/NodeInfo:optional info)",
+        "kind": "protocol/member",
+        "name": "fuchsia.io/File2.OnOpen"
+    },
+    {
+        "declaration": "() -> (int32 s,box<fuchsia.io/FilesystemInfo> info)",
+        "kind": "protocol/member",
+        "name": "fuchsia.io/File2.QueryFilesystem"
+    },
+    {
         "declaration": "(uint64 count) -> (fuchsia.io/File2_Read_Result result)",
         "kind": "protocol/member",
         "name": "fuchsia.io/File2.Read"
@@ -965,6 +906,16 @@
         "name": "fuchsia.io/File2.Seek"
     },
     {
+        "declaration": "(fuchsia.io/NodeAttributeFlags flags,fuchsia.io/NodeAttributes attributes) -> (int32 s)",
+        "kind": "protocol/member",
+        "name": "fuchsia.io/File2.SetAttr"
+    },
+    {
+        "declaration": "(fuchsia.io/OpenFlags flags) -> (int32 s)",
+        "kind": "protocol/member",
+        "name": "fuchsia.io/File2.SetFlags"
+    },
+    {
         "declaration": "() -> (fuchsia.io/Node2_Sync_Result result)",
         "kind": "protocol/member",
         "name": "fuchsia.io/File2.Sync"
@@ -1970,11 +1921,6 @@
         "name": "fuchsia.io/NodeInfo.packet_socket"
     },
     {
-        "declaration": "fuchsia.io/PipeObject",
-        "kind": "union/member",
-        "name": "fuchsia.io/NodeInfo.pipe"
-    },
-    {
         "declaration": "fuchsia.io/RawSocket",
         "kind": "union/member",
         "name": "fuchsia.io/NodeInfo.raw_socket"
@@ -2017,11 +1963,6 @@
     },
     {
         "kind": "bits/member",
-        "name": "fuchsia.io/NodeProtocols.DATAGRAM_SOCKET",
-        "value": "512"
-    },
-    {
-        "kind": "bits/member",
         "name": "fuchsia.io/NodeProtocols.DEVICE",
         "value": "268435456"
     },
@@ -2042,31 +1983,6 @@
     },
     {
         "kind": "bits/member",
-        "name": "fuchsia.io/NodeProtocols.PACKET_SOCKET",
-        "value": "256"
-    },
-    {
-        "kind": "bits/member",
-        "name": "fuchsia.io/NodeProtocols.PIPE",
-        "value": "16"
-    },
-    {
-        "kind": "bits/member",
-        "name": "fuchsia.io/NodeProtocols.RAW_SOCKET",
-        "value": "128"
-    },
-    {
-        "kind": "bits/member",
-        "name": "fuchsia.io/NodeProtocols.STREAM_SOCKET",
-        "value": "64"
-    },
-    {
-        "kind": "bits/member",
-        "name": "fuchsia.io/NodeProtocols.SYNCHRONOUS_DATAGRAM_SOCKET",
-        "value": "32"
-    },
-    {
-        "kind": "bits/member",
         "name": "fuchsia.io/NodeProtocols.TTY",
         "value": "536870912"
     },
@@ -2262,26 +2178,6 @@
         "resourceness": "resource"
     },
     {
-        "declaration": "zx/handle:SOCKET",
-        "kind": "table/member",
-        "name": "fuchsia.io/PipeInfo.socket"
-    },
-    {
-        "kind": "table",
-        "name": "fuchsia.io/PipeInfo",
-        "resourceness": "resource"
-    },
-    {
-        "declaration": "zx/handle:SOCKET",
-        "kind": "struct/member",
-        "name": "fuchsia.io/PipeObject.socket"
-    },
-    {
-        "kind": "struct",
-        "name": "fuchsia.io/PipeObject",
-        "resourceness": "resource"
-    },
-    {
         "declaration": "fuchsia.io/Operations",
         "kind": "const",
         "name": "fuchsia.io/RW_STAR_DIR",
@@ -2355,11 +2251,6 @@
         "name": "fuchsia.io/Representation.packet_socket"
     },
     {
-        "declaration": "fuchsia.io/PipeInfo",
-        "kind": "union/member",
-        "name": "fuchsia.io/Representation.pipe"
-    },
-    {
         "declaration": "fuchsia.io/RawSocketInfo",
         "kind": "union/member",
         "name": "fuchsia.io/Representation.raw_socket"
diff --git a/sdk/history/8/fuchsia.io.api_summary.json b/sdk/history/8/fuchsia.io.api_summary.json
index a818ab2..0aaf433 100644
--- a/sdk/history/8/fuchsia.io.api_summary.json
+++ b/sdk/history/8/fuchsia.io.api_summary.json
@@ -817,114 +817,40 @@
         "name": "fuchsia.io/File.WriteAt"
     },
     {
-        "declaration": "(vector<uint8>:8192 data) -> (int32 s,uint64 actual)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File.WriteDeprecated"
-    },
-    {
         "kind": "protocol",
         "name": "fuchsia.io/File"
     },
     {
-        "declaration": "(fuchsia.io/OpenFlags flags,server_end:fuchsia.io/Node object)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.Clone"
-    },
-    {
-        "declaration": "() -> (fuchsia.io/Node2_Close_Result result)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.Close"
-    },
-    {
-        "declaration": "() -> (fuchsia.io/NodeInfo info)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.Describe"
-    },
-    {
-        "declaration": "(fuchsia.io/ConnectionInfoQuery query) -> (fuchsia.io/ConnectionInfo payload)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.Describe2"
-    },
-    {
-        "declaration": "() -> (int32 s,fuchsia.io/NodeAttributes attributes)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.GetAttr"
-    },
-    {
-        "declaration": "(fuchsia.io/NodeAttributesQuery query) -> (fuchsia.io/Node2_GetAttributes_Result result)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.GetAttributes"
-    },
-    {
-        "declaration": "() -> (int32 s,fuchsia.io/OpenFlags flags)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.GetFlags"
-    },
-    {
-        "declaration": " -> (fuchsia.io/ConnectionInfo payload)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.OnConnectionInfo"
-    },
-    {
-        "declaration": " -> (int32 s,fuchsia.io/NodeInfo:optional info)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.OnOpen"
-    },
-    {
-        "declaration": "() -> (int32 s,box<fuchsia.io/FilesystemInfo> info)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.QueryFilesystem"
-    },
-    {
-        "declaration": "(fuchsia.io/ConnectionOptions options,zx/handle:CHANNEL object_request)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.Reopen"
-    },
-    {
-        "declaration": "(fuchsia.io/NodeAttributeFlags flags,fuchsia.io/NodeAttributes attributes) -> (int32 s)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.SetAttr"
-    },
-    {
-        "declaration": "(fuchsia.io/OpenFlags flags) -> (int32 s)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.SetFlags"
-    },
-    {
-        "declaration": "() -> (fuchsia.io/Node2_Sync_Result result)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.Sync"
-    },
-    {
-        "declaration": "(fuchsia.io/MutableNodeAttributes payload) -> (fuchsia.io/Node2_UpdateAttributes_Result result)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.UpdateAttributes"
-    },
-    {
-        "declaration": "(vector<uint8>:8192 data) -> (int32 s,uint64 actual)",
-        "kind": "protocol/member",
-        "name": "fuchsia.io/File1.WriteDeprecated"
-    },
-    {
-        "kind": "protocol",
-        "name": "fuchsia.io/File1"
-    },
-    {
         "declaration": "(fuchsia.io/AdvisoryLockRequest request) -> (fuchsia.io/AdvisoryLocking_AdvisoryLock_Result result)",
         "kind": "protocol/member",
         "name": "fuchsia.io/File2.AdvisoryLock"
     },
     {
+        "declaration": "(fuchsia.io/OpenFlags flags,server_end:fuchsia.io/Node object)",
+        "kind": "protocol/member",
+        "name": "fuchsia.io/File2.Clone"
+    },
+    {
         "declaration": "() -> (fuchsia.io/Node2_Close_Result result)",
         "kind": "protocol/member",
         "name": "fuchsia.io/File2.Close"
     },
     {
+        "declaration": "() -> (fuchsia.io/NodeInfo info)",
+        "kind": "protocol/member",
+        "name": "fuchsia.io/File2.Describe"
+    },
+    {
         "declaration": "(fuchsia.io/ConnectionInfoQuery query) -> (fuchsia.io/ConnectionInfo payload)",
         "kind": "protocol/member",
         "name": "fuchsia.io/File2.Describe2"
     },
     {
+        "declaration": "() -> (int32 s,fuchsia.io/NodeAttributes attributes)",
+        "kind": "protocol/member",
+        "name": "fuchsia.io/File2.GetAttr"
+    },
+    {
         "declaration": "(fuchsia.io/NodeAttributesQuery query) -> (fuchsia.io/Node2_GetAttributes_Result result)",
         "kind": "protocol/member",
         "name": "fuchsia.io/File2.GetAttributes"
@@ -935,11 +861,26 @@
         "name": "fuchsia.io/File2.GetBackingMemory"
     },
     {
+        "declaration": "() -> (int32 s,fuchsia.io/OpenFlags flags)",
+        "kind": "protocol/member",
+        "name": "fuchsia.io/File2.GetFlags"
+    },
+    {
         "declaration": " -> (fuchsia.io/ConnectionInfo payload)",
         "kind": "protocol/member",
         "name": "fuchsia.io/File2.OnConnectionInfo"
     },
     {
+        "declaration": " -> (int32 s,fuchsia.io/NodeInfo:optional info)",
+        "kind": "protocol/member",
+        "name": "fuchsia.io/File2.OnOpen"
+    },
+    {
+        "declaration": "() -> (int32 s,box<fuchsia.io/FilesystemInfo> info)",
+        "kind": "protocol/member",
+        "name": "fuchsia.io/File2.QueryFilesystem"
+    },
+    {
         "declaration": "(uint64 count) -> (fuchsia.io/File2_Read_Result result)",
         "kind": "protocol/member",
         "name": "fuchsia.io/File2.Read"
@@ -965,6 +906,16 @@
         "name": "fuchsia.io/File2.Seek"
     },
     {
+        "declaration": "(fuchsia.io/NodeAttributeFlags flags,fuchsia.io/NodeAttributes attributes) -> (int32 s)",
+        "kind": "protocol/member",
+        "name": "fuchsia.io/File2.SetAttr"
+    },
+    {
+        "declaration": "(fuchsia.io/OpenFlags flags) -> (int32 s)",
+        "kind": "protocol/member",
+        "name": "fuchsia.io/File2.SetFlags"
+    },
+    {
         "declaration": "() -> (fuchsia.io/Node2_Sync_Result result)",
         "kind": "protocol/member",
         "name": "fuchsia.io/File2.Sync"
@@ -1970,11 +1921,6 @@
         "name": "fuchsia.io/NodeInfo.packet_socket"
     },
     {
-        "declaration": "fuchsia.io/PipeObject",
-        "kind": "union/member",
-        "name": "fuchsia.io/NodeInfo.pipe"
-    },
-    {
         "declaration": "fuchsia.io/RawSocket",
         "kind": "union/member",
         "name": "fuchsia.io/NodeInfo.raw_socket"
@@ -2017,11 +1963,6 @@
     },
     {
         "kind": "bits/member",
-        "name": "fuchsia.io/NodeProtocols.DATAGRAM_SOCKET",
-        "value": "512"
-    },
-    {
-        "kind": "bits/member",
         "name": "fuchsia.io/NodeProtocols.DEVICE",
         "value": "268435456"
     },
@@ -2042,31 +1983,6 @@
     },
     {
         "kind": "bits/member",
-        "name": "fuchsia.io/NodeProtocols.PACKET_SOCKET",
-        "value": "256"
-    },
-    {
-        "kind": "bits/member",
-        "name": "fuchsia.io/NodeProtocols.PIPE",
-        "value": "16"
-    },
-    {
-        "kind": "bits/member",
-        "name": "fuchsia.io/NodeProtocols.RAW_SOCKET",
-        "value": "128"
-    },
-    {
-        "kind": "bits/member",
-        "name": "fuchsia.io/NodeProtocols.STREAM_SOCKET",
-        "value": "64"
-    },
-    {
-        "kind": "bits/member",
-        "name": "fuchsia.io/NodeProtocols.SYNCHRONOUS_DATAGRAM_SOCKET",
-        "value": "32"
-    },
-    {
-        "kind": "bits/member",
         "name": "fuchsia.io/NodeProtocols.TTY",
         "value": "536870912"
     },
@@ -2262,26 +2178,6 @@
         "resourceness": "resource"
     },
     {
-        "declaration": "zx/handle:SOCKET",
-        "kind": "table/member",
-        "name": "fuchsia.io/PipeInfo.socket"
-    },
-    {
-        "kind": "table",
-        "name": "fuchsia.io/PipeInfo",
-        "resourceness": "resource"
-    },
-    {
-        "declaration": "zx/handle:SOCKET",
-        "kind": "struct/member",
-        "name": "fuchsia.io/PipeObject.socket"
-    },
-    {
-        "kind": "struct",
-        "name": "fuchsia.io/PipeObject",
-        "resourceness": "resource"
-    },
-    {
         "declaration": "fuchsia.io/Operations",
         "kind": "const",
         "name": "fuchsia.io/RW_STAR_DIR",
@@ -2355,11 +2251,6 @@
         "name": "fuchsia.io/Representation.packet_socket"
     },
     {
-        "declaration": "fuchsia.io/PipeInfo",
-        "kind": "union/member",
-        "name": "fuchsia.io/Representation.pipe"
-    },
-    {
         "declaration": "fuchsia.io/RawSocketInfo",
         "kind": "union/member",
         "name": "fuchsia.io/Representation.raw_socket"
diff --git a/sdk/lib/driver2/BUILD.gn b/sdk/lib/driver2/BUILD.gn
index d12bf4a..becde21 100644
--- a/sdk/lib/driver2/BUILD.gn
+++ b/sdk/lib/driver2/BUILD.gn
@@ -39,7 +39,7 @@
   ]
 
   public_deps = [
-    "//sdk/lib/sys/component/llcpp",
+    "//sdk/lib/sys/component/cpp",
     "//src/lib/storage/vfs/cpp",
     "//zircon/system/ulib/inspect",
   ]
@@ -102,7 +102,7 @@
     "//sdk/fidl/fuchsia.logger",
     "//sdk/lib/driver_runtime:driver_runtime_cpp",
     "//sdk/lib/fidl/cpp",
-    "//sdk/lib/sys/component/llcpp",
+    "//sdk/lib/sys/component/cpp",
     "//src/devices/lib/driver:driver_runtime",
     "//src/diagnostics/lib/cpp-log-decoder:lib",
     "//src/lib/diagnostics/accessor2logger",
diff --git a/sdk/lib/driver2/devfs_exporter_test.cc b/sdk/lib/driver2/devfs_exporter_test.cc
index 54764c3..b871911 100644
--- a/sdk/lib/driver2/devfs_exporter_test.cc
+++ b/sdk/lib/driver2/devfs_exporter_test.cc
@@ -10,7 +10,7 @@
 #include <lib/driver2/devfs_exporter.h>
 #include <lib/driver2/test_base.h>
 #include <lib/fidl/cpp/binding.h>
-#include <lib/sys/component/llcpp/outgoing_directory.h>
+#include <lib/sys/component/cpp/outgoing_directory.h>
 
 #include "src/lib/testing/loop_fixture/test_loop_fixture.h"
 
diff --git a/sdk/lib/driver2/inspect.cc b/sdk/lib/driver2/inspect.cc
index 107060c..48ec4fa 100644
--- a/sdk/lib/driver2/inspect.cc
+++ b/sdk/lib/driver2/inspect.cc
@@ -5,7 +5,7 @@
 #include <fidl/fuchsia.io/cpp/wire_types.h>
 #include <lib/driver2/inspect.h>
 #include <lib/fidl/llcpp/channel.h>
-#include <lib/sys/component/llcpp/outgoing_directory.h>
+#include <lib/sys/component/cpp/outgoing_directory.h>
 
 #include <memory>
 
diff --git a/sdk/lib/driver2/inspect.h b/sdk/lib/driver2/inspect.h
index ef75ac7..044cf15 100644
--- a/sdk/lib/driver2/inspect.h
+++ b/sdk/lib/driver2/inspect.h
@@ -7,7 +7,7 @@
 
 #include <lib/inspect/cpp/inspect.h>
 #include <lib/inspect/cpp/inspector.h>
-#include <lib/sys/component/llcpp/outgoing_directory.h>
+#include <lib/sys/component/cpp/outgoing_directory.h>
 
 #include <memory>
 
diff --git a/sdk/lib/driver2/runtime_connector_impl_test.cc b/sdk/lib/driver2/runtime_connector_impl_test.cc
index b64a161..9186e28 100644
--- a/sdk/lib/driver2/runtime_connector_impl_test.cc
+++ b/sdk/lib/driver2/runtime_connector_impl_test.cc
@@ -5,7 +5,7 @@
 #include <lib/driver2/runtime.h>
 #include <lib/driver2/runtime_connector_impl.h>
 #include <lib/service/llcpp/service.h>
-#include <lib/sys/component/llcpp/outgoing_directory.h>
+#include <lib/sys/component/cpp/outgoing_directory.h>
 
 #include <set>
 
diff --git a/sdk/lib/driver_test_realm/meta/driver_test_realm.cml b/sdk/lib/driver_test_realm/meta/driver_test_realm.cml
index 69cf170..39e817f 100644
--- a/sdk/lib/driver_test_realm/meta/driver_test_realm.cml
+++ b/sdk/lib/driver_test_realm/meta/driver_test_realm.cml
@@ -48,7 +48,10 @@
             to: "#driver_manager",
         },
         {
-            protocol: [ "fuchsia.device.fs.Exporter" ],
+            protocol: [
+                "fuchsia.device.composite.DeprecatedCompositeCreator",
+                "fuchsia.device.fs.Exporter",
+            ],
             from: "#driver_manager",
             to: [
                 "#boot-drivers",
diff --git a/sdk/lib/fdio/tests/fdio_handle_fd.cc b/sdk/lib/fdio/tests/fdio_handle_fd.cc
index da50562..a4d395a 100644
--- a/sdk/lib/fdio/tests/fdio_handle_fd.cc
+++ b/sdk/lib/fdio/tests/fdio_handle_fd.cc
@@ -70,7 +70,8 @@
   for (const auto& fd : fds()) {
     struct stat st;
     ASSERT_SUCCESS(fstat(fd.get(), &st));
-    ASSERT_EQ(st.st_mode & S_IFMT, unsigned(S_IFIFO));
+    // TODO(https://fxbug.dev/103850): correctly report the mode.
+    ASSERT_EQ(st.st_mode & S_IFMT, 0 /* unsigned(S_IFIFO) */);
   }
 
   constexpr char message[] = "hello";
diff --git a/sdk/lib/fdio/unistd.cc b/sdk/lib/fdio/unistd.cc
index d0ca4aa..a11ed79 100644
--- a/sdk/lib/fdio/unistd.cc
+++ b/sdk/lib/fdio/unistd.cc
@@ -1617,16 +1617,6 @@
         return DT_REG;
       if (protocols & ZXIO_NODE_PROTOCOL_MEMORY)
         return DT_REG;
-      if (protocols & ZXIO_NODE_PROTOCOL_PIPE)
-        return DT_FIFO;
-      if (protocols & ZXIO_NODE_PROTOCOL_STREAM_SOCKET)
-        return DT_SOCK;
-      if (protocols & ZXIO_NODE_PROTOCOL_SYNCHRONOUS_DATAGRAM_SOCKET)
-        return DT_SOCK;
-      if (protocols & ZXIO_NODE_PROTOCOL_RAW_SOCKET)
-        return DT_SOCK;
-      if (protocols & ZXIO_NODE_PROTOCOL_DATAGRAM_SOCKET)
-        return DT_SOCK;
       if (protocols & ZXIO_NODE_PROTOCOL_DEVICE)
         return DT_BLK;
       if (protocols & ZXIO_NODE_PROTOCOL_TTY)
diff --git a/sdk/lib/sys/component/cpp/BUILD.gn b/sdk/lib/sys/component/cpp/BUILD.gn
new file mode 100644
index 0000000..f118587
--- /dev/null
+++ b/sdk/lib/sys/component/cpp/BUILD.gn
@@ -0,0 +1,29 @@
+# Copyright 2022 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/cpp/sdk_source_set.gni")
+
+sdk_source_set("cpp") {
+  category = "experimental"
+  sdk_name = "sys_component_cpp"
+  include_base = "//sdk"
+
+  sources = [
+    "constants.h",
+    "handlers.h",
+    "outgoing_directory.cc",
+    "outgoing_directory.h",
+  ]
+
+  public_deps = [
+    "//sdk/fidl/fuchsia.io:fuchsia.io_llcpp",
+    "//sdk/lib/fit",
+    "//sdk/lib/stdcompat",
+    "//sdk/lib/svc",
+    "//zircon/system/ulib/async",
+    "//zircon/system/ulib/zx",
+  ]
+
+  public_configs = [ "//sdk/config" ]
+}
diff --git a/sdk/lib/sys/component/cpp/constants.h b/sdk/lib/sys/component/cpp/constants.h
new file mode 100644
index 0000000..bd3520a
--- /dev/null
+++ b/sdk/lib/sys/component/cpp/constants.h
@@ -0,0 +1,21 @@
+// Copyright 2022 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef LIB_SYS_COMPONENT_CPP_CONSTANTS_H_
+#define LIB_SYS_COMPONENT_CPP_CONSTANTS_H_
+
+namespace component {
+
+// The name of the default FIDL Service instance.
+constexpr const char kDefaultInstance[] = "default";
+
+// The path referencing the incoming services directory.
+constexpr const char kServiceDirectory[] = "svc";
+
+// Path delimiter used by svc library.
+constexpr const char kSvcPathDelimiter[] = "/";
+
+}  // namespace component
+
+#endif  // LIB_SYS_COMPONENT_CPP_CONSTANTS_H_
diff --git a/sdk/lib/sys/component/cpp/handlers.h b/sdk/lib/sys/component/cpp/handlers.h
new file mode 100644
index 0000000..ab0e724
--- /dev/null
+++ b/sdk/lib/sys/component/cpp/handlers.h
@@ -0,0 +1,76 @@
+// Copyright 2022 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef LIB_SYS_COMPONENT_CPP_HANDLERS_H_
+#define LIB_SYS_COMPONENT_CPP_HANDLERS_H_
+
+#include <lib/fidl/llcpp/service_handler_interface.h>
+#include <lib/svc/dir.h>
+#include <lib/zx/channel.h>
+#include <lib/zx/status.h>
+#include <zircon/errors.h>
+
+#include <map>
+
+namespace component {
+
+// Callback invoked when a request is made to a FIDL protocol server end.
+using AnyHandler = fit::function<void(zx::channel)>;
+
+// Same as |AnyHandler| except that it's typesafe.
+template <typename Protocol>
+using TypedHandler = fit::function<void(fidl::ServerEnd<Protocol> request)>;
+
+// A handler for an instance of a FIDL Service.
+class ServiceInstanceHandler final : public fidl::ServiceHandlerInterface {
+ public:
+  ServiceInstanceHandler() = default;
+
+  // Disable copying.
+  ServiceInstanceHandler(const ServiceInstanceHandler&) = delete;
+  ServiceInstanceHandler& operator=(const ServiceInstanceHandler&) = delete;
+
+  // Enable moving.
+  ServiceInstanceHandler(ServiceInstanceHandler&&) = default;
+  ServiceInstanceHandler& operator=(ServiceInstanceHandler&&) = default;
+
+ private:
+  friend class OutgoingDirectory;
+
+  // Return all registered member handlers. Key contains member name. Value
+  // contains |Connector| func.
+  //
+  // Once taken, the `ServiceInstanceHandler` is no longer safe to use.
+  std::map<std::string, AnyHandler> TakeMemberHandlers() { return std::move(handlers_); }
+
+  // Add a |member| to the instance, whose connection will be handled by |handler|.
+  //
+  // # Errors
+  //
+  // ZX_ERR_ALREADY_EXISTS: The member already exists.
+  zx::status<> AddAnyMember(std::string_view member, AnyMemberHandler handler) override {
+    std::string owned_member = std::string(member);
+    if (handlers_.count(owned_member) != 0) {
+      return zx::make_status(ZX_ERR_ALREADY_EXISTS);
+    }
+
+    // Since AnyMemberHandler is a protected type of this class' parent class,
+    // |fidl::ServiceHandlerInterface|, wrap the type into a public one.
+    AnyHandler bridge_func = [handler = std::move(handler)](zx::channel request_channel) {
+      handler(std::move(request_channel));
+    };
+    handlers_[owned_member] = std::move(bridge_func);
+    return zx::ok();
+  }
+
+  std::map<std::string, AnyHandler> handlers_ = {};
+};
+
+// Temporary alias until clients are migrated.
+// TODO(http://fxbug.dev/103207): Remove this.
+using ServiceHandler = ServiceInstanceHandler;
+
+}  // namespace component
+
+#endif  // LIB_SYS_COMPONENT_CPP_HANDLERS_H_
diff --git a/sdk/lib/sys/component/cpp/outgoing_directory.cc b/sdk/lib/sys/component/cpp/outgoing_directory.cc
new file mode 100644
index 0000000..5cab171
--- /dev/null
+++ b/sdk/lib/sys/component/cpp/outgoing_directory.cc
@@ -0,0 +1,238 @@
+// Copyright 2022 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <lib/svc/dir.h>
+#include <lib/sys/component/cpp/constants.h>
+#include <lib/sys/component/cpp/handlers.h>
+#include <lib/sys/component/cpp/outgoing_directory.h>
+#include <lib/zx/status.h>
+#include <zircon/assert.h>
+#include <zircon/errors.h>
+#include <zircon/process.h>
+#include <zircon/processargs.h>
+#include <zircon/types.h>
+
+#include <memory>
+#include <sstream>
+
+namespace component {
+
+OutgoingDirectory OutgoingDirectory::Create(async_dispatcher_t* dispatcher) {
+  ZX_ASSERT_MSG(dispatcher != nullptr, "OutgoingDirectory::Create received nullptr |dispatcher|.");
+
+  svc_dir_t* root = nullptr;
+  // It's safe to ignore return value here since the function always returns
+  // ZX_OK.
+  (void)svc_dir_create_without_serve(&root);
+
+  return OutgoingDirectory(dispatcher, root);
+}
+
+OutgoingDirectory::OutgoingDirectory(async_dispatcher_t* dispatcher, svc_dir_t* root)
+    : dispatcher_(dispatcher), root_(root) {}
+
+OutgoingDirectory::OutgoingDirectory(OutgoingDirectory&& other) noexcept
+    : dispatcher_(other.dispatcher_), root_(other.root_) {
+  other.dispatcher_ = nullptr;
+  other.root_ = nullptr;
+}
+
+OutgoingDirectory& OutgoingDirectory::operator=(OutgoingDirectory&& other) noexcept {
+  dispatcher_ = other.dispatcher_;
+  root_ = other.root_;
+
+  other.dispatcher_ = nullptr;
+  other.root_ = nullptr;
+
+  return *this;
+}
+
+OutgoingDirectory::~OutgoingDirectory() {
+  if (root_) {
+    svc_dir_destroy(root_);
+  }
+}
+
+zx::status<> OutgoingDirectory::Serve(fidl::ServerEnd<fuchsia_io::Directory> directory_server_end) {
+  if (!directory_server_end.is_valid()) {
+    return zx::error_status(ZX_ERR_BAD_HANDLE);
+  }
+
+  zx_status_t status =
+      svc_dir_serve(root_, dispatcher_, directory_server_end.TakeHandle().release());
+  if (status != ZX_OK) {
+    return zx::error_status(status);
+  }
+
+  return zx::make_status(status);
+}
+
+zx::status<> OutgoingDirectory::ServeFromStartupInfo() {
+  fidl::ServerEnd<fuchsia_io::Directory> directory_request(
+      zx::channel(zx_take_startup_handle(PA_DIRECTORY_REQUEST)));
+  return Serve(std::move(directory_request));
+}
+
+zx::status<> OutgoingDirectory::AddProtocol(AnyHandler handler, cpp17::string_view name) {
+  return AddProtocolAt(std::move(handler), kServiceDirectory, name);
+}
+
+zx::status<> OutgoingDirectory::AddProtocolAt(AnyHandler handler, cpp17::string_view path,
+                                              cpp17::string_view name) {
+  // More thorough path validation is done in |svc_add_service|.
+  if (path.empty() || name.empty()) {
+    return zx::error_status(ZX_ERR_INVALID_ARGS);
+  }
+
+  std::string directory_entry(path);
+  std::string protocol_entry(name);
+  if (registered_handlers_.count(directory_entry) != 0 &&
+      registered_handlers_[directory_entry].count(protocol_entry) != 0) {
+    return zx::make_status(ZX_ERR_ALREADY_EXISTS);
+  }
+
+  // |svc_dir_add_service_by_path| takes in a void* |context| that is passed to
+  // the |handler| callback passed as the last argument to the function call.
+  // The context will first be stored in the heap for this path, then a pointer
+  // to it will be passed to this function. The callback, in this case |OnConnect|,
+  // will then cast the void* type to OnConnectContext*.
+  auto context = std::make_unique<OnConnectContext>(
+      OnConnectContext{.handler = std::move(handler), .self = this});
+  zx_status_t status =
+      svc_dir_add_service(root_, directory_entry.c_str(), name.data(), context.get(), OnConnect);
+
+  auto& directory_handlers = registered_handlers_[directory_entry];
+  directory_handlers[protocol_entry] = std::move(context);
+
+  return zx::make_status(status);
+}
+
+zx::status<> OutgoingDirectory::AddDirectory(fidl::ClientEnd<fuchsia_io::Directory> remote_dir,
+                                             cpp17::string_view directory_name) {
+  if (!remote_dir.is_valid()) {
+    return zx::error_status(ZX_ERR_BAD_HANDLE);
+  }
+  if (directory_name.empty()) {
+    return zx::error_status(ZX_ERR_INVALID_ARGS);
+  }
+
+  zx_status_t status =
+      svc_dir_add_directory(root_, directory_name.data(), remote_dir.TakeChannel().release());
+  return zx::make_status(status);
+}
+
+zx::status<> OutgoingDirectory::AddService(ServiceHandler handler, cpp17::string_view service,
+                                           cpp17::string_view instance) {
+  if (service.empty() || instance.empty()) {
+    return zx::error_status(ZX_ERR_INVALID_ARGS);
+  }
+
+  auto handlers = handler.TakeMemberHandlers();
+  if (handlers.empty()) {
+    return zx::make_status(ZX_ERR_INVALID_ARGS);
+  }
+
+  std::string basepath = MakePath(service, instance);
+  for (auto& [member_name, member_handler] : handlers) {
+    zx::status<> status = AddProtocolAt(std::move(member_handler), basepath, member_name);
+    if (status.is_error()) {
+      // If we encounter an error with any of the instance members, scrub entire
+      // directory entry.
+      registered_handlers_.erase(basepath);
+      return status;
+    }
+  }
+
+  return zx::ok();
+}
+
+zx::status<> OutgoingDirectory::RemoveProtocol(cpp17::string_view name) {
+  return RemoveProtocolAt(kServiceDirectory, name);
+}
+
+zx::status<> OutgoingDirectory::RemoveProtocolAt(cpp17::string_view directory,
+                                                 cpp17::string_view name) {
+  std::string key(directory);
+
+  if (registered_handlers_.count(key) == 0) {
+    return zx::make_status(ZX_ERR_NOT_FOUND);
+  }
+
+  auto& svc_root_handlers = registered_handlers_[key];
+  std::string entry_key = std::string(name);
+  if (svc_root_handlers.count(entry_key) == 0) {
+    return zx::make_status(ZX_ERR_NOT_FOUND);
+  }
+
+  // Remove svc_dir_t entry first so that no new connections are attempted on
+  // handler after we remove the pointer to it in |svc_root_handlers|.
+  zx_status_t status = svc_dir_remove_service(root_, kServiceDirectory, name.data());
+  if (status != ZX_OK) {
+    return zx::make_status(status);
+  }
+
+  // If teardown is managed, e.g. through |AddProtocol| overload for `fidl::Server<T>*`,
+  // then close all active connections.
+  UnbindAllConnections(name);
+
+  // Now that all active connections have been closed, and no new connections
+  // are being accepted under |name|, it's safe to remove the handlers.
+  svc_root_handlers.erase(entry_key);
+
+  return zx::ok();
+}
+
+zx::status<> OutgoingDirectory::RemoveService(cpp17::string_view service,
+                                              cpp17::string_view instance) {
+  std::string path = MakePath(service, instance);
+  if (registered_handlers_.count(path) == 0) {
+    return zx::make_status(ZX_ERR_NOT_FOUND);
+  }
+
+  // Remove svc_dir_t entry first so that channels close _before_ we remove
+  // pointer values out from underneath handlers.
+  std::string service_path =
+      std::string(kServiceDirectory) + std::string(kSvcPathDelimiter) + std::string(service);
+  zx_status_t status = svc_dir_remove_service_by_path(root_, service_path.c_str(), instance.data());
+
+  // Now it's safe to remove entry from map.
+  registered_handlers_.erase(path);
+
+  return zx::make_status(status);
+}
+
+zx::status<> OutgoingDirectory::RemoveDirectory(cpp17::string_view directory_name) {
+  zx_status_t status = svc_dir_remove_directory(root_, directory_name.data());
+  return zx::make_status(status);
+}
+
+void OutgoingDirectory::OnConnect(void* raw_context, const char* service_name, zx_handle_t handle) {
+  OnConnectContext* context = reinterpret_cast<OnConnectContext*>(raw_context);
+  (context->handler)(zx::channel(handle));
+}
+
+void OutgoingDirectory::AppendUnbindConnectionCallback(const std::string& name,
+                                                       UnbindConnectionCallback callback) {
+  unbind_protocol_callbacks_[name].emplace_back(std::move(callback));
+}
+
+void OutgoingDirectory::UnbindAllConnections(cpp17::string_view name) {
+  auto key = std::string(name);
+  auto iterator = unbind_protocol_callbacks_.find(key);
+  if (iterator != unbind_protocol_callbacks_.end()) {
+    std::vector<UnbindConnectionCallback>& callbacks = iterator->second;
+    for (auto& cb : callbacks) {
+      cb();
+    }
+    unbind_protocol_callbacks_.erase(iterator);
+  }
+}
+
+std::string OutgoingDirectory::MakePath(cpp17::string_view service, cpp17::string_view instance) {
+  std::stringstream path;
+  path << kServiceDirectory << kSvcPathDelimiter << service << kSvcPathDelimiter << instance;
+  return path.str();
+}
+
+}  // namespace component
diff --git a/sdk/lib/sys/component/cpp/outgoing_directory.h b/sdk/lib/sys/component/cpp/outgoing_directory.h
new file mode 100644
index 0000000..434a730
--- /dev/null
+++ b/sdk/lib/sys/component/cpp/outgoing_directory.h
@@ -0,0 +1,413 @@
+// Copyright 2022 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef LIB_SYS_COMPONENT_CPP_OUTGOING_DIRECTORY_H_
+#define LIB_SYS_COMPONENT_CPP_OUTGOING_DIRECTORY_H_
+
+#include <fidl/fuchsia.io/cpp/wire.h>
+#include <lib/async/dispatcher.h>
+#include <lib/fit/function.h>
+#include <lib/stdcompat/string_view.h>
+#include <lib/svc/dir.h>
+#include <lib/sys/component/cpp/constants.h>
+#include <lib/sys/component/cpp/handlers.h>
+#include <zircon/assert.h>
+#include <zircon/errors.h>
+#include <zircon/types.h>
+
+#include <map>
+#include <memory>
+#include <stack>
+
+namespace component {
+
+// The directory containing handles to capabilities this component provides.
+// Entries served from this outgoing directory should correspond to the
+// component manifest's `capabilities` declarations.
+//
+// The outgoing directory contains one special subdirectory, named `svc`. This
+// directory contains the FIDL Services and Protocols offered by this component
+// to other components. For example the FIDL Protocol `fuchsia.foo.Bar` will be
+// hosted under the path `/svc/fuchsia.foo.Bar`.
+//
+// This class is thread-hostile with respect to its interface. However, its
+// |async_dispatcher_t| may be multithreaded as long as it does not service
+// requests concurrently with any operations on the object's interface or its
+// destruction.
+//
+// # Simple usage
+//
+// Instances of this class should be owned and managed on the same thread
+// that services their connections.
+//
+// # Advanced usage
+//
+// You can use a background thread to service connections provided:
+// async_dispatcher_t for the background thread is stopped or suspended
+// prior to destroying the class object.
+//
+// # Maintainer's Note
+//
+// This class' API is semantically identical to the one found in
+// `//sdk/lib/sys/cpp`. This exists in order to offer equivalent facilities to
+// the new C++ bindings. The other class is designed for the older, HLCPP
+// (High-Level C++) FIDL bindings. It is expected that once
+// all clients of HLCPP are migrated to this unified bindings, that library will
+// be removed.
+class OutgoingDirectory final {
+ public:
+  // Creates an OutgoingDirectory which will serve requests when
+  // |Serve| or |ServeFromStartupInfo()| is called.
+  //
+  // |dispatcher| must not be nullptr. If it is, this method will panic.
+  static OutgoingDirectory Create(async_dispatcher_t* dispatcher);
+
+  OutgoingDirectory() = delete;
+
+  // OutgoingDirectory can be moved. Once moved, invoking a method on an
+  // instance will yield undefined behavior.
+  OutgoingDirectory(OutgoingDirectory&&) noexcept;
+  OutgoingDirectory& operator=(OutgoingDirectory&&) noexcept;
+
+  // OutgoingDirectory cannot be copied.
+  OutgoingDirectory(const OutgoingDirectory&) = delete;
+  OutgoingDirectory& operator=(const OutgoingDirectory&) = delete;
+
+  ~OutgoingDirectory();
+
+  // Starts serving the outgoing directory on the given channel. This should
+  // be invoked after the outgoing directory has been populated, e.g. via
+  // |AddProtocol|.
+  //
+  // This object will implement the |fuchsia.io.Directory| interface using this
+  // channel. Note that this method returns immediately and that the |dispatcher|
+  // provided to the constructor will be responsible for processing messages
+  // sent to the server endpoint.
+  //
+  // # Errors
+  //
+  // ZX_ERR_BAD_HANDLE: |directory_server_end| is not a valid handle.
+  //
+  // ZX_ERR_ACCESS_DENIED: |directory_server_end| has insufficient rights.
+  zx::status<> Serve(fidl::ServerEnd<fuchsia_io::Directory> directory_server_end);
+
+  // Starts serving the outgoing directory on the channel provided to this
+  // process at startup as |PA_DIRECTORY_REQUEST|.
+  //
+  // This object will implement the |fuchsia.io.Directory| interface using this
+  // channel.
+  //
+  // If |dispatcher| is nullptr, then the global dispatcher set via
+  // |async_get_default_dispatcher| will be used.
+  //
+  // # Errors
+  //
+  // ZX_ERR_BAD_HANDLE: the process did not receive a |PA_DIRECTORY_REQUEST|
+  // startup handle or it was already taken.
+  //
+  // ZX_ERR_ACCESS_DENIED: The |PA_DIRECTORY_REQUEST| handle has insufficient
+  // rights.
+  zx::status<> ServeFromStartupInfo();
+
+  // Adds a FIDL Protocol instance.
+  //
+  // |impl| will be used to handle requests for this protocol.
+  // |name| is used to determine where to host the protocol. This protocol will
+  // be hosted under the path /svc/{name} where name is the discoverable name
+  // of the protocol by default.
+  //
+  // Note, if and when |RemoveProtocol| is called for the provided |name|, this
+  // object will asynchronously close down the associated server end channel and
+  // stop receiving requests. This method provides no facilities for waiting
+  // until teardown is complete. If such control is desired, then the
+  // |TypedHandler| overload of this method listed below ought to be used.
+  //
+  // # Errors
+  //
+  // ZX_ERR_ALREADY_EXISTS: An entry already exists for this protocol.
+  //
+  // ZX_ERR_INVALID_ARGS: |impl| is nullptr or |name| is an empty string.
+  //
+  // # Examples
+  //
+  // See sample use cases in test case(s) located at
+  // //sdk/lib/sys/component/llcpp/outgoing_directory_test.cc
+  template <typename Protocol>
+  zx::status<> AddProtocol(fidl::WireServer<Protocol>* impl,
+                           cpp17::string_view name = fidl::DiscoverableProtocolName<Protocol>) {
+    return AddProtocolAt(kServiceDirectory, impl, name);
+  }
+
+  // Same as above but overloaded to support servers implementations speaking
+  // FIDL C++ natural types: |fidl::Server<P>|, part of the unified bindings.
+  template <typename Protocol>
+  zx::status<> AddProtocol(fidl::Server<Protocol>* impl,
+                           cpp17::string_view name = fidl::DiscoverableProtocolName<Protocol>) {
+    if (impl == nullptr || dispatcher_ == nullptr) {
+      return zx::make_status(ZX_ERR_INVALID_ARGS);
+    }
+
+    return AddProtocol<Protocol>(
+        [=](fidl::ServerEnd<Protocol> request) {
+          // This object is safe to drop. Server will still begin to operate
+          // past its lifetime.
+          auto _server = fidl::BindServer(dispatcher_, std::move(request), impl);
+        },
+        name);
+  }
+
+  // Adds a FIDL Protocol instance.
+  //
+  // A |handler| is added to handle connection requests for the this particular
+  // protocol. |name| is used to determine where to host the protocol.
+  // This protocol will be hosted under the path /svc/{name} where name
+  // is the discoverable name of the protocol.
+  //
+  // # Note
+  //
+  // Active connections are never torn down when/if |RemoveProtocol| is invoked
+  // with the same |name|. Users of this method should manage teardown of
+  // all active connections.
+  //
+  // # Errors
+  //
+  // ZX_ERR_ALREADY_EXISTS: An entry already exists for this protocol.
+  //
+  // ZX_ERR_INVALID_ARGS: |name| is an empty string.
+  //
+  // # Examples
+  //
+  // See sample use cases in test case(s) located at
+  // //sdk/lib/sys/component/llcpp/outgoing_directory_test.cc
+  template <typename Protocol>
+  zx::status<> AddProtocol(TypedHandler<Protocol> handler,
+                           cpp17::string_view name = fidl::DiscoverableProtocolName<Protocol>) {
+    return AddProtocolAt<Protocol>(kServiceDirectory, std::move(handler), name);
+  }
+
+  // Same as above but is untyped. This method is generally discouraged but
+  // is made available if a generic handler needs to be provided.
+  zx::status<> AddProtocol(AnyHandler handler, cpp17::string_view name);
+
+  // Same as above but allows overriding the parent directory in which the
+  // protocol will be hosted.
+  //
+  // |path| is used as the parent directory for the protocol, e.g. `svc`.
+  // |name| is the name under |path| at which the protocol will be hosted. By
+  // default, the FIDL protocol name, e.g. `fuchsia.logger.LogSink`, is used.
+  //
+  // # Errors
+  //
+  // ZX_ERR_ALREADY_EXISTS: An entry already exists for this protocol.
+  //
+  // ZX_ERR_INVALID_ARGS: |impl| is nullptr. |path| or |name| is an empty
+  // string.
+  template <typename Protocol>
+  zx::status<> AddProtocolAt(cpp17::string_view path, fidl::WireServer<Protocol>* impl,
+                             cpp17::string_view name = fidl::DiscoverableProtocolName<Protocol>) {
+    if (impl == nullptr || dispatcher_ == nullptr) {
+      return zx::make_status(ZX_ERR_INVALID_ARGS);
+    }
+
+    return AddProtocolAt<Protocol>(
+        path,
+        [=, name = std::string(name)](fidl::ServerEnd<Protocol> request) {
+          fidl::ServerBindingRef<Protocol> server =
+              fidl::BindServer(dispatcher_, std::move(request), impl);
+
+          auto cb = [server = std::move(server)]() mutable { server.Unbind(); };
+          // We don't have to check for entry existing because the |AddProtocol|
+          // overload being invoked here will do that internally.
+          AppendUnbindConnectionCallback(name, std::move(cb));
+        },
+        name);
+  }
+
+  template <typename Protocol>
+  zx::status<> AddProtocolAt(cpp17::string_view path, TypedHandler<Protocol> handler,
+                             cpp17::string_view name = fidl::DiscoverableProtocolName<Protocol>) {
+    auto bridge_func = [handler = std::move(handler)](zx::channel request) {
+      fidl::ServerEnd<Protocol> server_end(std::move(request));
+      (void)handler(std::move(server_end));
+    };
+
+    return AddProtocolAt(std::move(bridge_func), path, name);
+  }
+
+  // Same as |AddProtocol| but is untyped and allows the usage of setting the
+  // parent directory in which the protocol will be installed.
+  zx::status<> AddProtocolAt(AnyHandler handler, cpp17::string_view path, cpp17::string_view name);
+
+  // Adds an instance of a FIDL Service.
+  //
+  // A |handler| is added to provide an |instance| of a service.
+  //
+  // The template type |Service| must be the generated type representing a FIDL Service.
+  // The generated class |Service::Handler| helps the caller populate a
+  // |ServiceInstanceHandler|.
+  //
+  // # Errors
+  //
+  // ZX_ERR_ALREADY_EXISTS: The instance already exists.
+  //
+  // ZX_ERR_INVALID_ARGS: |instance| is an empty string or |handler| is empty.
+  //
+  // # Example
+  //
+  // See sample use cases in test case(s) located at
+  // //sdk/lib/sys/component/llcpp/outgoing_directory_test.cc
+  template <typename Service>
+  zx::status<> AddService(ServiceInstanceHandler handler,
+                          cpp17::string_view instance = kDefaultInstance) {
+    return AddService(std::move(handler), Service::Name, instance);
+  }
+
+  // Same as above but is untyped.
+  zx::status<> AddService(ServiceInstanceHandler handler, cpp17::string_view service,
+                          cpp17::string_view instance = kDefaultInstance);
+
+  // Serve a subdirectory at the root of this outgoing directory.
+  //
+  // The directory will be installed under the path |directory_name|. When
+  // a request is received under this path, then it will be forwarded to
+  // |remote_dir|.
+  //
+  // # Errors
+  //
+  // ZX_ERR_ALREADY_EXISTS: An entry with the provided name already exists.
+  //
+  // ZX_ERR_BAD_HANDLE: |remote_dir| is an invalid handle.
+  //
+  // ZX_ERR_INVALID_ARGS: |directory_name| is an empty string.
+  zx::status<> AddDirectory(fidl::ClientEnd<fuchsia_io::Directory> remote_dir,
+                            cpp17::string_view directory_name);
+
+  // Removes a FIDL Protocol entry with the path `/svc/{name}`.
+  //
+  // # Errors
+  //
+  // ZX_ERR_NOT_FOUND: The protocol entry was not found.
+  //
+  // # Example
+  //
+  // ```
+  // outgoing.RemoveProtocol<lib_example::MyProtocol>();
+  // ```
+  template <typename Protocol>
+  zx::status<> RemoveProtocol(cpp17::string_view name = fidl::DiscoverableProtocolName<Protocol>) {
+    return RemoveProtocol(name);
+  }
+
+  // Same as above but untyped.
+  zx::status<> RemoveProtocol(cpp17::string_view name);
+
+  // Removes a FIDL Protocol entry located in the provided |directory|.
+  // Unlike |RemoveProtocol| which looks for the protocol to remove in the
+  // path `/svc/{name}`, this method uses the directory name provided, e.g.
+  // `/{path}/{name}`.
+  //
+  // # Errors
+  //
+  // ZX_ERR_NOT_FOUND: The protocol entry was not found.
+  //
+  // # Example
+  //
+  // ```
+  // outgoing.RemoveProtocolAt<lib_example::MyProtocol>("diagnostics");
+  // ```
+  template <typename Protocol>
+  zx::status<> RemoveProtocolAt(
+      cpp17::string_view path, cpp17::string_view name = fidl::DiscoverableProtocolName<Protocol>) {
+    return RemoveProtocolAt(path, name);
+  }
+
+  // Same as above but untyped.
+  zx::status<> RemoveProtocolAt(cpp17::string_view directory, cpp17::string_view name);
+
+  // Removes an instance of a FIDL Service.
+  //
+  // # Errors
+  //
+  // ZX_ERR_NOT_FOUND: The instance was not found.
+  //
+  // # Example
+  //
+  // ```
+  // outgoing.RemoveService<lib_example::MyService>("my-instance");
+  // ```
+  template <typename Service>
+  zx::status<> RemoveService(cpp17::string_view instance = kDefaultInstance) {
+    return RemoveService(Service::Name, instance);
+  }
+
+  // Same as above but untyped.
+  zx::status<> RemoveService(cpp17::string_view service,
+                             cpp17::string_view instance = kDefaultInstance);
+
+  // Removes the subdirectory on the provided |directory_name|.
+  //
+  // # Errors
+  //
+  // ZX_ERR_NOT_FOUND: No entry was found with provided name.
+  zx::status<> RemoveDirectory(cpp17::string_view directory_name);
+
+ private:
+  OutgoingDirectory(async_dispatcher_t* dispatcher, svc_dir_t* root);
+
+  // |svc_dir_add_service_by_path| takes in a void* |context| that is passed to
+  // the |handler| callback passed as the last argument to the function call.
+  // This library will pass in a casted void* pointer to this object, and when
+  // the `svc` library invokes this library's connection handler, the |context|
+  // will be casted back to |OnConnectContext*|.
+  struct OnConnectContext {
+    AnyHandler handler;
+    OutgoingDirectory* self;
+  };
+
+  // Function pointer that matches type of |svc_dir_add_service| handler.
+  // Internally, it calls the |AnyMemberHandler| instance populated via |AddAnyMember|.
+  static void OnConnect(void* context, const char* service_name, zx_handle_t handle);
+
+  // Callback invoked during teardown of a FIDL protocol entry. This callback
+  // will close all active connections on the associated channel.
+  using UnbindConnectionCallback = fit::callback<void()>;
+
+  void AppendUnbindConnectionCallback(const std::string& name, UnbindConnectionCallback callback);
+
+  void UnbindAllConnections(cpp17::string_view name);
+
+  static std::string MakePath(cpp17::string_view service, cpp17::string_view instance);
+
+  async_dispatcher_t* dispatcher_ = nullptr;
+
+  svc_dir_t* root_ = nullptr;
+
+  // Mapping of all registered protocol handlers. Key represents a path to
+  // the directory in which the protocol ought to be installed. For example,
+  // a path may look like `svc/fuchsia.FooService/some_instance`.
+  // The value contains a map of each of the entry's handlers.
+  //
+  // For FIDL Protocols, entries will be stored under "svc" entry
+  // of this type, and then their name will be used as a key for the internal
+  // map.
+  //
+  // For FIDL Services, entries will be stored by instance,
+  // e.g. `svc/fuchsia.FooService/default`, and then the member names will be
+  // used as the keys for the internal maps.
+  //
+  // The OnConnectContext has to be stored in the heap because its pointer
+  // is used by |OnConnect|, a static function, during channel connection attempt.
+  std::map<std::string, std::map<std::string, std::unique_ptr<OnConnectContext>>>
+      registered_handlers_ = {};
+
+  // Protocol bindings used to initiate teardown when protocol is removed. We
+  // store this in a callback as opposed to a map of fidl::ServerBindingRef<T>
+  // because that object is template parameterized and therefore can't be
+  // stored in a homogeneous container.
+  std::map<std::string, std::vector<UnbindConnectionCallback>> unbind_protocol_callbacks_ = {};
+};
+
+}  // namespace component
+
+#endif  // LIB_SYS_COMPONENT_CPP_OUTGOING_DIRECTORY_H_
diff --git a/sdk/lib/sys/component/cpp/testing/realm_builder.cc b/sdk/lib/sys/component/cpp/testing/realm_builder.cc
index bb93b98..2053f21 100644
--- a/sdk/lib/sys/component/cpp/testing/realm_builder.cc
+++ b/sdk/lib/sys/component/cpp/testing/realm_builder.cc
@@ -128,12 +128,11 @@
   return *this;
 }
 
-Realm& Realm::ReplaceConfigValue(const std::string& name, const std::string& key,
-                                 ConfigValue value) {
-  fuchsia::component::test::Realm_ReplaceConfigValue_Result result;
+Realm& Realm::SetConfigValue(const std::string& name, const std::string& key, ConfigValue value) {
+  fuchsia::component::test::Realm_SetConfigValue_Result result;
   ZX_COMPONENT_ASSERT_STATUS_AND_RESULT_OK(
-      "Realm/ReplaceConfigValue",
-      realm_proxy_->ReplaceConfigValue(name, key, value.TakeAsFidl(), &result), result);
+      "Realm/SetConfigValue", realm_proxy_->SetConfigValue(name, key, value.TakeAsFidl(), &result),
+      result);
   return *this;
 }
 
@@ -276,9 +275,9 @@
   return *this;
 }
 
-RealmBuilder& RealmBuilder::ReplaceConfigValue(const std::string& name, const std::string& key,
-                                               ConfigValue value) {
-  root_.ReplaceConfigValue(name, key, std::move(value));
+RealmBuilder& RealmBuilder::SetConfigValue(const std::string& name, const std::string& key,
+                                           ConfigValue value) {
+  root_.SetConfigValue(name, key, std::move(value));
   return *this;
 }
 
diff --git a/sdk/lib/sys/component/cpp/testing/realm_builder.h b/sdk/lib/sys/component/cpp/testing/realm_builder.h
index 5d6d7d9..01d75e0 100644
--- a/sdk/lib/sys/component/cpp/testing/realm_builder.h
+++ b/sdk/lib/sys/component/cpp/testing/realm_builder.h
@@ -151,7 +151,7 @@
                                 DirectoryContents directory);
 
   /// Replaces the value of a given configuration field
-  Realm& ReplaceConfigValue(const std::string& name, const std::string& key, ConfigValue value);
+  Realm& SetConfigValue(const std::string& name, const std::string& key, ConfigValue value);
 
   /// Updates the Component decl of the given child.
   void ReplaceComponentDecl(const std::string& child_name,
@@ -229,8 +229,7 @@
                                        DirectoryContents directory);
 
   /// Replaces the value of a given configuration field for the root realm.
-  RealmBuilder& ReplaceConfigValue(const std::string& name, const std::string& key,
-                                   ConfigValue value);
+  RealmBuilder& SetConfigValue(const std::string& name, const std::string& key, ConfigValue value);
 
   /// Updates the Component decl of the given child of the root realm.
   void ReplaceComponentDecl(const std::string& child_name,
diff --git a/sdk/lib/sys/component/cpp/testing/realm_builder_types.h b/sdk/lib/sys/component/cpp/testing/realm_builder_types.h
index b8c48576..fae251b 100644
--- a/sdk/lib/sys/component/cpp/testing/realm_builder_types.h
+++ b/sdk/lib/sys/component/cpp/testing/realm_builder_types.h
@@ -200,7 +200,7 @@
 // # Example
 //
 // ```
-// realm_builder.ReplaceConfigValue(echo_server, "echo_string", ConfigValue::String("Hi!"));
+// realm_builder.SetConfigValue(echo_server, "echo_string", ConfigValue::String("Hi!"));
 // ```
 class ConfigValue {
  public:
diff --git a/sdk/lib/sys/component/cpp/testing/sys_component_cpp_testing.api b/sdk/lib/sys/component/cpp/testing/sys_component_cpp_testing.api
index 47eb627..acbcc07 100644
--- a/sdk/lib/sys/component/cpp/testing/sys_component_cpp_testing.api
+++ b/sdk/lib/sys/component/cpp/testing/sys_component_cpp_testing.api
@@ -3,7 +3,7 @@
   "pkg/sys_component_cpp_testing/include/lib/sys/component/cpp/testing/internal/errors.h": "d060a2c890a9392e4ac8b5a4665ef351",
   "pkg/sys_component_cpp_testing/include/lib/sys/component/cpp/testing/internal/local_component_runner.h": "c64e16d6d13ddec60edc5dfd9c94fdb2",
   "pkg/sys_component_cpp_testing/include/lib/sys/component/cpp/testing/internal/realm.h": "8d8150024af382008ebca0fe86269056",
-  "pkg/sys_component_cpp_testing/include/lib/sys/component/cpp/testing/realm_builder.h": "78f484178985d92f65b613df1726596a",
-  "pkg/sys_component_cpp_testing/include/lib/sys/component/cpp/testing/realm_builder_types.h": "0456b68c6539469a01f45d502158b8e3",
+  "pkg/sys_component_cpp_testing/include/lib/sys/component/cpp/testing/realm_builder.h": "4fd9db29a12c077480f765be7a04617b",
+  "pkg/sys_component_cpp_testing/include/lib/sys/component/cpp/testing/realm_builder_types.h": "f7713bb47eaa26fb8d7458366b363565",
   "pkg/sys_component_cpp_testing/include/lib/sys/component/cpp/testing/scoped_child.h": "9186348509c271f0e88ba6a288d8aaf1"
 }
\ No newline at end of file
diff --git a/sdk/lib/sys/component/cpp/tests/BUILD.gn b/sdk/lib/sys/component/cpp/tests/BUILD.gn
index 88b4d07..428fa99 100644
--- a/sdk/lib/sys/component/cpp/tests/BUILD.gn
+++ b/sdk/lib/sys/component/cpp/tests/BUILD.gn
@@ -9,7 +9,10 @@
 group("tests") {
   testonly = true
 
-  deps = [ ":component_cpp_tests" ]
+  deps = [
+    ":component_cpp_outgoing_directory_tests",
+    ":component_cpp_testing_realm_builder_tests",
+  ]
 }
 
 source_set("test_utils") {
@@ -67,10 +70,9 @@
   sources = [ "echo_service_server.cc" ]
 
   deps = [
-    "//examples/fidl/fuchsia.examples",
-    "//sdk/lib/fidl/cpp",
-    "//sdk/lib/sys/cpp",
-    "//sdk/lib/sys/service/cpp",
+    "//examples/fidl/fuchsia.examples:fuchsia.examples_llcpp",
+    "//sdk/lib/fidl",
+    "//sdk/lib/sys/component/cpp",
     "//sdk/lib/syslog/cpp",
     "//zircon/system/ulib/async-loop:async-loop-cpp",
     "//zircon/system/ulib/async-loop:async-loop-default",
@@ -227,7 +229,7 @@
   deps = [ ":internal_test_bin" ]
 }
 
-fuchsia_test_package("component_cpp_tests") {
+fuchsia_test_package("component_cpp_testing_realm_builder_tests") {
   test_components = [
     ":realm_builder_test",
     ":internal_test",
@@ -241,3 +243,64 @@
     ":pre_populated_realm",
   ]
 }
+
+executable("cpp_outgoing_directory_test_bin") {
+  testonly = true
+
+  sources = [ "outgoing_directory_test.cc" ]
+
+  deps = [
+    "//examples/fidl/fuchsia.examples:fuchsia.examples_cpp",
+    "//sdk/fidl/fuchsia.io:fuchsia.io_llcpp",
+    "//sdk/lib/sys/component/cpp",
+    "//src/lib/fidl/cpp",
+    "//src/lib/fxl/test:gtest_main",
+    "//src/lib/testing/loop_fixture",
+    "//third_party/googletest:gmock",
+    "//zircon/system/ulib/service:service-llcpp",
+  ]
+}
+
+executable("cpp_outgoing_directory_integration_test_bin") {
+  testonly = true
+
+  sources = [ "outgoing_directory_integration_test.cc" ]
+
+  deps = [
+    "//examples/fidl/fuchsia.examples:fuchsia.examples",
+    "//sdk/fidl/fuchsia.component",
+    "//sdk/fidl/fuchsia.logger:fuchsia.logger",
+    "//sdk/lib/fidl/cpp",
+    "//sdk/lib/sys/component/cpp/testing:cpp",
+    "//sdk/lib/sys/cpp",
+    "//src/lib/fxl/test:gtest_main",
+    "//src/lib/storage/vfs/cpp",
+    "//src/lib/testing/loop_fixture",
+  ]
+}
+
+fuchsia_component("cpp_echo_service_server") {
+  testonly = true
+  deps = [ ":echo_service_server_bin" ]
+  manifest = "meta/cpp_echo_service_server.cml"
+}
+
+fuchsia_component("outgoing_directory_test") {
+  testonly = true
+  deps = [ ":cpp_outgoing_directory_test_bin" ]
+  manifest = "meta/outgoing_directory_test.cml"
+}
+
+fuchsia_component("outgoing_directory_integration_test") {
+  testonly = true
+  deps = [ ":cpp_outgoing_directory_integration_test_bin" ]
+  manifest = "meta/outgoing_directory_integration_test.cml"
+}
+
+fuchsia_test_package("component_cpp_outgoing_directory_tests") {
+  test_components = [
+    ":outgoing_directory_test",
+    ":outgoing_directory_integration_test",
+  ]
+  deps = [ ":cpp_echo_service_server" ]
+}
diff --git a/sdk/lib/sys/component/cpp/tests/echo_service_server.cc b/sdk/lib/sys/component/cpp/tests/echo_service_server.cc
index 0c03626..a0e5a91 100644
--- a/sdk/lib/sys/component/cpp/tests/echo_service_server.cc
+++ b/sdk/lib/sys/component/cpp/tests/echo_service_server.cc
@@ -2,27 +2,54 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <fuchsia/examples/cpp/fidl.h>
+#include <fidl/fuchsia.examples/cpp/wire.h>
 #include <lib/async-loop/cpp/loop.h>
 #include <lib/async-loop/default.h>
-#include <lib/fidl/cpp/binding_set.h>
-#include <lib/sys/cpp/component_context.h>
-#include <lib/sys/cpp/outgoing_directory.h>
-#include <lib/sys/service/cpp/service_handler.h>
+#include <lib/sys/component/cpp/outgoing_directory.h>
 #include <lib/syslog/cpp/log_settings.h>
 #include <lib/syslog/cpp/macros.h>
+#include <stdlib.h>
 
-class EchoImpl : public fuchsia::examples::Echo {
+// An implementation of the Echo protocol. Protocols are implemented in LLCPP by
+// creating a subclass of the ::Interface class for the protocol.
+class EchoImpl final : public fidl::WireServer<fuchsia_examples::Echo> {
  public:
   explicit EchoImpl(bool reverse) : reverse_(reverse) {}
 
-  void SendString(std::string value) override {}
-  void EchoString(std::string value, EchoStringCallback callback) override {
+  // Bind this implementation to a channel.
+  void Bind(std::unique_ptr<EchoImpl> self, async_dispatcher_t* dispatcher,
+            fidl::ServerEnd<fuchsia_examples::Echo> request) {
+    // Destroy |self| when the unbound handler completes.
+    fidl::OnUnboundFn<EchoImpl> unbound_handler =
+        [self = std::move(self)](EchoImpl* impl, fidl::UnbindInfo info,
+                                 fidl::ServerEnd<fuchsia_examples::Echo> server_end) {
+          if (info.is_user_initiated()) {
+            return;
+          }
+          if (info.is_peer_closed()) {
+            FX_LOGS(INFO) << "Client disconnected";
+            return;
+          }
+          FX_LOGS(INFO) << "Server error: " << info;
+        };
+    fidl::BindServer(dispatcher, std::move(request), this, std::move(unbound_handler));
+  }
+
+  // Handle a SendString request by sending on OnString event with the request value. For
+  // fire and forget methods, the completer can be used to close the channel with an epitaph.
+  void SendString(SendStringRequestView request, SendStringCompleter::Sync& completer) override {}
+
+  // Handle an EchoString request by responding with the request value. For two-way
+  // methods, the completer is also used to send a response.
+  void EchoString(EchoStringRequestView request, EchoStringCompleter::Sync& completer) override {
+    std::string value(request->value.get());
     FX_LOGS(INFO) << "Got echo request: " << value;
     if (reverse_) {
       std::reverse(value.begin(), value.end());
     }
-    callback(value);
+    FX_LOGS(INFO) << "Sending response: " << value;
+    auto reply = fidl::StringView::FromExternal(value);
+    completer.Reply(reply);
   }
 
  private:
@@ -30,23 +57,41 @@
 };
 
 int main(int argc, const char** argv) {
-  FX_LOGS(INFO) << "Starting Echo service server";
+  FX_LOGS(INFO) << "Starting echo service server";
   async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread);
-  auto context = sys::ComponentContext::CreateAndServeOutgoingDirectory();
-  sys::ServiceHandler handler;
-  fuchsia::examples::EchoService::Handler my_service(&handler);
+  auto outgoing = component::OutgoingDirectory::Create(loop.dispatcher());
 
-  EchoImpl regular_impl(false);
-  fidl::BindingSet<fuchsia::examples::Echo> regular_echo_bindings;
-  my_service.add_regular_echo(regular_echo_bindings.GetHandler(&regular_impl));
+  component::ServiceHandler handler;
+  fuchsia_examples::EchoService::Handler my_service(&handler);
 
-  EchoImpl reversed_impl(true);
-  fidl::BindingSet<fuchsia::examples::Echo> reversed_echo_bindings;
-  my_service.add_reversed_echo(reversed_echo_bindings.GetHandler(&reversed_impl));
+  auto add_regular_result =
+      my_service.add_regular_echo([&loop](fidl::ServerEnd<fuchsia_examples::Echo> request_channel) {
+        std::unique_ptr regular_impl = std::make_unique<EchoImpl>(false);
+        regular_impl->Bind(std::move(regular_impl), loop.dispatcher(), std::move(request_channel));
+      });
+  ZX_ASSERT(add_regular_result.is_ok());
 
-  context->outgoing()->AddService<fuchsia::examples::EchoService>(std::move(handler));
+  auto add_reversed_result = my_service.add_reversed_echo(
+      [&loop](fidl::ServerEnd<fuchsia_examples::Echo> request_channel) {
+        std::unique_ptr reversed_impl = std::make_unique<EchoImpl>(true);
+        reversed_impl->Bind(std::move(reversed_impl), loop.dispatcher(),
+                            std::move(request_channel));
+      });
+  ZX_ASSERT(add_reversed_result.is_ok());
 
-  FX_LOGS(INFO) << "Running Echo service server";
+  auto result = outgoing.ServeFromStartupInfo();
+  if (result.is_error()) {
+    FX_LOGS(ERROR) << "Failed to Serve OutgoingDirectory: " << result.status_string();
+    return result.status_value();
+  }
+
+  result = outgoing.AddService<fuchsia_examples::EchoService>(std::move(handler));
+  if (result.is_error()) {
+    FX_LOGS(ERROR) << "Failed to add EchoService: " << result.status_string();
+    return result.status_value();
+  }
+
+  FX_LOGS(INFO) << "Running echo service server";
   loop.Run();
   return 0;
 }
diff --git a/sdk/lib/sys/component/cpp/tests/meta/cpp_echo_service_server.cml b/sdk/lib/sys/component/cpp/tests/meta/cpp_echo_service_server.cml
new file mode 100644
index 0000000..d66736f
--- /dev/null
+++ b/sdk/lib/sys/component/cpp/tests/meta/cpp_echo_service_server.cml
@@ -0,0 +1,19 @@
+// Copyright 2022 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+{
+    include: [ "syslog/client.shard.cml" ],
+    program: {
+        runner: "elf",
+        binary: "bin/echo_service_server_bin",
+    },
+    capabilities: [
+        { service: "fuchsia.examples.EchoService" },
+    ],
+    expose: [
+        {
+            service: "fuchsia.examples.EchoService",
+            from: "self",
+        },
+    ],
+}
diff --git a/sdk/lib/sys/component/cpp/tests/meta/outgoing_directory_integration_test.cml b/sdk/lib/sys/component/cpp/tests/meta/outgoing_directory_integration_test.cml
new file mode 100644
index 0000000..1ca64ba
--- /dev/null
+++ b/sdk/lib/sys/component/cpp/tests/meta/outgoing_directory_integration_test.cml
@@ -0,0 +1,12 @@
+// Copyright 2022 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+{
+    include: [
+        "//src/sys/test_runners/gtest/default.shard.cml",
+        "sys/component/realm_builder.shard.cml",
+    ],
+    program: {
+        binary: "bin/cpp_outgoing_directory_integration_test_bin",
+    },
+}
diff --git a/sdk/lib/sys/component/cpp/tests/meta/outgoing_directory_test.cml b/sdk/lib/sys/component/cpp/tests/meta/outgoing_directory_test.cml
new file mode 100644
index 0000000..9901efe
--- /dev/null
+++ b/sdk/lib/sys/component/cpp/tests/meta/outgoing_directory_test.cml
@@ -0,0 +1,16 @@
+// Copyright 2022 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+{
+    include: [
+        "//src/sys/test_runners/gtest/default.shard.cml",
+        "syslog/client.shard.cml",
+    ],
+    program: {
+        binary: "bin/cpp_outgoing_directory_test_bin",
+    },
+    use: [
+        // Needed for gTest Death Tests.
+        { protocol: "fuchsia.process.Launcher" },
+    ],
+}
diff --git a/sdk/lib/sys/component/cpp/tests/outgoing_directory_integration_test.cc b/sdk/lib/sys/component/cpp/tests/outgoing_directory_integration_test.cc
new file mode 100644
index 0000000..229ffed
--- /dev/null
+++ b/sdk/lib/sys/component/cpp/tests/outgoing_directory_integration_test.cc
@@ -0,0 +1,60 @@
+// Copyright 2022 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <fuchsia/examples/cpp/fidl.h>
+#include <fuchsia/logger/cpp/fidl.h>
+#include <lib/async-loop/cpp/loop.h>
+#include <lib/async/dispatcher.h>
+#include <lib/fidl/cpp/binding_set.h>
+#include <lib/fidl/cpp/string.h>
+#include <lib/fit/function.h>
+#include <lib/sys/component/cpp/testing/realm_builder.h>
+#include <lib/sys/component/cpp/testing/realm_builder_types.h>
+#include <lib/sys/cpp/component_context.h>
+#include <zircon/status.h>
+
+#include <memory>
+
+#include <gtest/gtest.h>
+#include <src/lib/testing/loop_fixture/real_loop_fixture.h>
+
+#include "lib/sys/cpp/service_directory.h"
+
+namespace {
+
+using namespace component_testing;
+
+constexpr char kEchoServiceServer[] = "echo_service_server";
+constexpr char kEchoServiceServerUrl[] = "#meta/cpp_echo_service_server.cm";
+
+class OutgoingDirectoryTest : public gtest::RealLoopFixture {};
+
+TEST_F(OutgoingDirectoryTest, Connects) {
+  auto realm_builder = RealmBuilder::Create();
+  realm_builder.AddChild(kEchoServiceServer, kEchoServiceServerUrl);
+  realm_builder.AddRoute(Route{.capabilities = {Service{fuchsia::examples::EchoService::Name}},
+                               .source = ChildRef{kEchoServiceServer},
+                               .targets = {ParentRef()}});
+  realm_builder.AddRoute(Route{.capabilities = {Protocol{fuchsia::logger::LogSink::Name_}},
+                               .source = ParentRef(),
+                               .targets = {ChildRef{kEchoServiceServer}}});
+  auto realm = realm_builder.Build(dispatcher());
+
+  auto default_service = sys::OpenServiceAt<fuchsia::examples::EchoService>(realm.CloneRoot());
+  auto regular = default_service.regular_echo().Connect().Bind();
+
+  constexpr char kMessage[] = "Ping!";
+  bool message_replied = false;
+  regular->EchoString(kMessage, [expected_reply = kMessage, &message_replied,
+                                 quit_loop = QuitLoopClosure()](fidl::StringPtr value) {
+    EXPECT_EQ(value, expected_reply);
+    message_replied = true;
+    quit_loop();
+  });
+
+  RunLoop();
+  EXPECT_TRUE(message_replied);
+}
+
+}  // namespace
diff --git a/sdk/lib/sys/component/cpp/tests/outgoing_directory_test.cc b/sdk/lib/sys/component/cpp/tests/outgoing_directory_test.cc
new file mode 100644
index 0000000..ca53f4b
--- /dev/null
+++ b/sdk/lib/sys/component/cpp/tests/outgoing_directory_test.cc
@@ -0,0 +1,669 @@
+// Copyright 2022 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fidl/fuchsia.examples/cpp/fidl.h>
+#include <fidl/fuchsia.io/cpp/fidl.h>
+#include <lib/async-loop/cpp/loop.h>
+#include <lib/fdio/fd.h>
+#include <lib/fdio/namespace.h>
+#include <lib/fidl/llcpp/client.h>
+#include <lib/service/llcpp/service.h>
+#include <lib/sys/component/cpp/constants.h>
+#include <lib/sys/component/cpp/outgoing_directory.h>
+#include <lib/zx/channel.h>
+#include <lib/zx/handle.h>
+#include <unistd.h>
+#include <zircon/assert.h>
+#include <zircon/errors.h>
+
+#include <algorithm>
+#include <array>
+#include <memory>
+#include <thread>
+#include <utility>
+
+#include <fbl/unique_fd.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <src/lib/storage/vfs/cpp/managed_vfs.h>
+#include <src/lib/storage/vfs/cpp/pseudo_dir.h>
+#include <src/lib/storage/vfs/cpp/pseudo_file.h>
+#include <src/lib/testing/loop_fixture/real_loop_fixture.h>
+
+namespace {
+
+// Expected path of directory hosting FIDL Services & Protocols.
+constexpr char kSvcDirectoryPath[] = "svc";
+
+constexpr char kTestString[] = "FizzBuzz";
+constexpr char kTestStringReversed[] = "zzuBzziF";
+
+class EchoImpl final : public fidl::WireServer<fuchsia_examples::Echo> {
+ public:
+  explicit EchoImpl(bool reversed) : reversed_(reversed) {}
+
+  void SendString(SendStringRequestView request, SendStringCompleter::Sync& completer) override {}
+
+  void EchoString(EchoStringRequestView request, EchoStringCompleter::Sync& completer) override {
+    std::string value(request->value.get());
+    if (reversed_) {
+      std::reverse(value.begin(), value.end());
+    }
+    auto reply = fidl::StringView::FromExternal(value);
+    completer.Reply(reply);
+  }
+
+ private:
+  bool reversed_ = false;
+};
+
+class NaturalEchoImpl final : public fidl::Server<fuchsia_examples::Echo> {
+ public:
+  explicit NaturalEchoImpl(bool reversed) : reversed_(reversed) {}
+
+  void SendString(SendStringRequest& request, SendStringCompleter::Sync& completer) override {}
+
+  void EchoString(EchoStringRequest& request, EchoStringCompleter::Sync& completer) override {
+    std::string value(request.value());
+    if (reversed_) {
+      std::reverse(value.begin(), value.end());
+    }
+    completer.Reply(value);
+  }
+
+ private:
+  bool reversed_ = false;
+};
+
+class OutgoingDirectoryTest : public gtest::RealLoopFixture {
+ public:
+  void SetUp() override {
+    outgoing_directory_ = std::make_unique<component::OutgoingDirectory>(
+        component::OutgoingDirectory::Create(dispatcher()));
+    auto endpoints = fidl::CreateEndpoints<fuchsia_io::Directory>();
+    ZX_ASSERT(outgoing_directory_->Serve(std::move(endpoints->server)).is_ok());
+    client_end_ = std::move(endpoints->client);
+  }
+
+  component::OutgoingDirectory* GetOutgoingDirectory() { return outgoing_directory_.get(); }
+
+  fidl::ClientEnd<fuchsia_io::Directory> TakeSvcClientEnd(
+      fidl::ClientEnd<fuchsia_io::Directory> root, fidl::StringView path = kSvcDirectoryPath) {
+    zx::channel server_end, client_end;
+    ZX_ASSERT(ZX_OK == zx::channel::create(0, &server_end, &client_end));
+    // Check if this has already be initialized.
+    if (!svc_client_.is_valid()) {
+      svc_client_ = fidl::WireClient<fuchsia_io::Directory>(std::move(root), dispatcher());
+    }
+
+    auto status = svc_client_->Open(
+        fuchsia_io::wire::OpenFlags::kRightWritable | fuchsia_io::wire::OpenFlags::kRightReadable,
+        fuchsia_io::wire::kModeTypeDirectory, path,
+        fidl::ServerEnd<fuchsia_io::Node>(std::move(server_end)));
+    ZX_ASSERT_MSG(status.ok(), "Failed to open /%s client: %s", path.data(),
+                  status.status_string());
+    return fidl::ClientEnd<fuchsia_io::Directory>(std::move(client_end));
+  }
+
+  fidl::ClientEnd<fuchsia_io::Directory> TakeRootClientEnd() { return std::move(client_end_); }
+
+ protected:
+  void InstallServiceHandler(fuchsia_examples::EchoService::Handler& service_handler,
+                             EchoImpl* impl, bool reversed) {
+    auto handler = [dispatcher = dispatcher(),
+                    impl = impl](fidl::ServerEnd<fuchsia_examples::Echo> request) -> void {
+      // This is invoked during handler unbound. We're not testing for that
+      // here so we just provide a no-op callback.
+      auto _on_unbound = [](EchoImpl* impl, fidl::UnbindInfo info,
+                            fidl::ServerEnd<fuchsia_examples::Echo> server_end) {};
+      fidl::BindServer(dispatcher, std::move(request), impl, std::move(_on_unbound));
+    };
+    auto result = reversed ? service_handler.add_reversed_echo(std::move(handler))
+                           : service_handler.add_regular_echo(std::move(handler));
+    ZX_ASSERT(result.is_ok());
+  }
+
+  fidl::WireClient<fuchsia_examples::Echo> ConnectToServiceMember(
+      fuchsia_examples::EchoService::ServiceClient& service, bool reversed) {
+    auto connect_result =
+        reversed ? service.connect_reversed_echo() : service.connect_regular_echo();
+    ZX_ASSERT(connect_result.is_ok());
+    return fidl::WireClient<fuchsia_examples::Echo>(std::move(connect_result.value()),
+                                                    dispatcher());
+  }
+
+  // Service handler that is pre-populated. This is only used for tests that
+  // want to test failure paths.
+  component::ServiceHandler CreateNonEmptyServiceHandler() {
+    // Setup service handler.
+    component::ServiceHandler service_handler;
+    fuchsia_examples::EchoService::Handler echo_service_handler(&service_handler);
+
+    // First, install the regular Echo server in this service handler.
+    EchoImpl regular_impl(/*reversed=*/false);
+    InstallServiceHandler(echo_service_handler, &regular_impl, /*reversed=*/false);
+    return service_handler;
+  }
+
+ private:
+  std::unique_ptr<component::OutgoingDirectory> outgoing_directory_ = nullptr;
+  fidl::ClientEnd<fuchsia_io::Directory> client_end_;
+  fidl::WireClient<fuchsia_io::Directory> svc_client_;
+};
+
+TEST_F(OutgoingDirectoryTest, AddProtocolWireServer) {
+  // Setup fuchsia.examples.Echo server.
+  EchoImpl impl(/*reversed=*/false);
+  ASSERT_EQ(GetOutgoingDirectory()->AddProtocol<fuchsia_examples::Echo>(&impl).status_value(),
+            ZX_OK);
+
+  // Setup fuchsia.examples.Echo client.
+  auto client_end =
+      service::ConnectAt<fuchsia_examples::Echo>(TakeSvcClientEnd(TakeRootClientEnd()));
+  ASSERT_EQ(client_end.status_value(), ZX_OK);
+  fidl::WireClient<fuchsia_examples::Echo> client(std::move(*client_end), dispatcher());
+
+  std::string reply_received;
+  client->EchoString(kTestString)
+      .ThenExactlyOnce([&reply_received, quit_loop = QuitLoopClosure()](
+                           fidl::WireUnownedResult<fuchsia_examples::Echo::EchoString>& result) {
+        ASSERT_TRUE(result.ok()) << "EchoString failed: " << result.error();
+        auto* response = result.Unwrap();
+        reply_received = std::string(response->response.data(), response->response.size());
+        quit_loop();
+      });
+  RunLoop();
+
+  EXPECT_EQ(reply_received, kTestString);
+}
+
+TEST_F(OutgoingDirectoryTest, AddProtocolNaturalServer) {
+  // Setup fuchsia.examples.Echo server.
+  NaturalEchoImpl impl(/*reversed=*/false);
+  ASSERT_EQ(GetOutgoingDirectory()->AddProtocol<fuchsia_examples::Echo>(&impl).status_value(),
+            ZX_OK);
+
+  // Setup fuchsia.examples.Echo client.
+  auto client_end =
+      service::ConnectAt<fuchsia_examples::Echo>(TakeSvcClientEnd(TakeRootClientEnd()));
+  ASSERT_EQ(client_end.status_value(), ZX_OK);
+  fidl::Client<fuchsia_examples::Echo> client(std::move(*client_end), dispatcher());
+
+  std::string reply_received;
+  client->EchoString({kTestString})
+      .ThenExactlyOnce([&reply_received, quit_loop = QuitLoopClosure()](
+                           fidl::Result<fuchsia_examples::Echo::EchoString>& result) {
+        ASSERT_TRUE(result.is_ok()) << "EchoString failed: " << result.error_value();
+        reply_received = result->response();
+        quit_loop();
+      });
+  RunLoop();
+
+  EXPECT_EQ(reply_received, kTestString);
+}
+
+// Test that outgoing directory is able to serve multiple service members. In
+// this case, the directory will host the `fuchsia.examples.EchoService` which
+// contains two `fuchsia.examples.Echo` member. One regular, and one reversed.
+TEST_F(OutgoingDirectoryTest, AddServiceServesAllMembers) {
+  // Setup service handler.
+  component::ServiceHandler service_handler;
+  fuchsia_examples::EchoService::Handler echo_service_handler(&service_handler);
+
+  // First, install the regular Echo server in this service handler.
+  EchoImpl regular_impl(/*reversed=*/false);
+  InstallServiceHandler(echo_service_handler, &regular_impl, /*reversed=*/false);
+
+  // Then, install the reverse Echo server. This instance will reverse the string
+  // received in calls to EchoString.
+  EchoImpl reversed_impl(/*reversed=*/true);
+  InstallServiceHandler(echo_service_handler, &reversed_impl, /*reversed=*/true);
+
+  ZX_ASSERT(GetOutgoingDirectory()
+                ->AddService<fuchsia_examples::EchoService>(std::move(service_handler))
+                .is_ok());
+
+  // Setup test client.
+  auto open_result =
+      service::OpenServiceAt<fuchsia_examples::EchoService>(TakeSvcClientEnd(TakeRootClientEnd()));
+  ZX_ASSERT(open_result.is_ok());
+
+  fuchsia_examples::EchoService::ServiceClient service = std::move(open_result.value());
+
+  // Assert that service is connected and that proper impl returns expected reply.
+  for (bool reversed : {true, false}) {
+    bool message_echoed = false;
+    auto client = ConnectToServiceMember(service, reversed);
+    auto expected_reply = reversed ? kTestStringReversed : kTestString;
+    client->EchoString(kTestString)
+        .ThenExactlyOnce(
+            [quit_loop = QuitLoopClosure(), &message_echoed, expected_reply = expected_reply](
+                fidl::WireUnownedResult<fuchsia_examples::Echo::EchoString>& reply) {
+              EXPECT_TRUE(reply.ok()) << "Reply failed with: " << reply.error().status_string();
+              EXPECT_EQ(reply.value().response.get(), cpp17::string_view(expected_reply));
+              message_echoed = true;
+              quit_loop();
+            });
+
+    RunLoop();
+
+    EXPECT_TRUE(message_echoed);
+  }
+
+  // Next, assert that after removing the service, the client end yields ZX_ERR_PEER_CLOSED.
+  ZX_ASSERT(GetOutgoingDirectory()->RemoveService<fuchsia_examples::EchoService>().is_ok());
+  for (bool reversed : {true, false}) {
+    auto connect_result =
+        reversed ? service.connect_reversed_echo() : service.connect_regular_echo();
+    ZX_ASSERT(connect_result.is_error());
+    EXPECT_EQ(connect_result.status_value(), ZX_ERR_PEER_CLOSED);
+  }
+}
+
+// Test that serving a FIDL Protocol works as expected.
+TEST_F(OutgoingDirectoryTest, AddProtocolCanServeMultipleProtocols) {
+  constexpr static std::array<std::pair<bool, const char*>, 2> kIsReversedAndPaths = {
+      {{false, "fuchsia.examples.Echo"}, {true, "fuchsia.examples.Ohce"}}};
+
+  // Setup fuchsia.examples.Echo servers
+  EchoImpl regular_impl(/*reversed=*/false);
+  EchoImpl reversed_impl(/*reversed=*/true);
+  for (auto [reversed, path] : kIsReversedAndPaths) {
+    auto* impl = reversed ? &reversed_impl : &regular_impl;
+    ASSERT_EQ(
+        GetOutgoingDirectory()->AddProtocol<fuchsia_examples::Echo>(impl, path).status_value(),
+        ZX_OK);
+  }
+
+  // Setup fuchsia.examples.Echo client
+  for (auto [reversed, path] : kIsReversedAndPaths) {
+    auto client_end =
+        service::ConnectAt<fuchsia_examples::Echo>(TakeSvcClientEnd(TakeRootClientEnd()), path);
+    ASSERT_EQ(client_end.status_value(), ZX_OK);
+    fidl::WireClient<fuchsia_examples::Echo> client(std::move(*client_end), dispatcher());
+
+    std::string reply_received;
+    client->EchoString(kTestString)
+        .ThenExactlyOnce([&reply_received, quit_loop = QuitLoopClosure()](
+                             fidl::WireUnownedResult<fuchsia_examples::Echo::EchoString>& result) {
+          ASSERT_TRUE(result.ok()) << "EchoString failed: " << result.error();
+          auto* response = result.Unwrap();
+          reply_received = std::string(response->response.data(), response->response.size());
+          quit_loop();
+        });
+    RunLoop();
+
+    auto expected_reply = reversed ? kTestStringReversed : kTestString;
+    EXPECT_EQ(reply_received, expected_reply);
+  }
+}
+
+// Test that after removing protocol, all clients are unable to make a call on
+// the channel.
+TEST_F(OutgoingDirectoryTest, RemoveProtocolClosesAllConnections) {
+  // For this test case, 3 clients will connect to one Echo protocol.
+  static constexpr size_t kNumClients = 3;
+
+  class EventHandler : public fidl::AsyncEventHandler<fuchsia_examples::Echo> {
+   public:
+    EventHandler() = default;
+
+    void on_fidl_error(fidl::UnbindInfo error) override { errors_.emplace_back(error); }
+
+    std::vector<fidl::UnbindInfo> GetErrors() { return errors_; }
+
+   private:
+    std::vector<fidl::UnbindInfo> errors_;
+  };
+
+  EventHandler event_handler;
+  EchoImpl regular_impl(/*reversed=*/false);
+  ASSERT_EQ(
+      GetOutgoingDirectory()->AddProtocol<fuchsia_examples::Echo>(&regular_impl).status_value(),
+      ZX_OK);
+
+  fidl::ClientEnd<fuchsia_io::Directory> svc_directory = TakeSvcClientEnd(TakeRootClientEnd());
+  std::vector<fidl::Client<fuchsia_examples::Echo>> clients = {};
+  for (size_t i = 0; i < kNumClients; ++i) {
+    auto client_end = service::ConnectAt<fuchsia_examples::Echo>(svc_directory.borrow());
+
+    ASSERT_EQ(client_end.status_value(), ZX_OK);
+
+    fidl::Client<fuchsia_examples::Echo> client(std::move(*client_end), dispatcher(),
+                                                &event_handler);
+    client->EchoString(std::string(kTestString))
+        .ThenExactlyOnce([quit_loop = QuitLoopClosure()](
+                             fidl::Result<fuchsia_examples::Echo::EchoString>& result) {
+          ASSERT_TRUE(result.is_ok());
+          ASSERT_EQ(result->response(), kTestString);
+          quit_loop();
+        });
+    RunLoop();
+
+    clients.emplace_back(std::move(client));
+  }
+
+  ASSERT_EQ(GetOutgoingDirectory()->RemoveProtocol<fuchsia_examples::Echo>().status_value(), ZX_OK);
+  RunLoopUntilIdle();
+
+  ASSERT_EQ(event_handler.GetErrors().size(), kNumClients);
+  for (auto& error : event_handler.GetErrors()) {
+    EXPECT_TRUE(error.is_peer_closed())
+        << "Expected peer_closed. Got : " << error.FormatDescription();
+  }
+}
+
+// Test that serving a FIDL Protocol from a non-svc directory works as expected.
+TEST_F(OutgoingDirectoryTest, AddProtocolAtServesProtocol) {
+  constexpr static char kDirectory[] = "test";
+
+  // Setup fuchsia.examples.Echo servers
+  EchoImpl regular_impl(/*reversed=*/false);
+  ASSERT_EQ(GetOutgoingDirectory()
+                ->AddProtocolAt<fuchsia_examples::Echo>(kDirectory, &regular_impl)
+                .status_value(),
+            ZX_OK);
+
+  auto client_end = service::ConnectAt<fuchsia_examples::Echo>(
+      TakeSvcClientEnd(TakeRootClientEnd(), /*path=*/kDirectory));
+  ASSERT_EQ(client_end.status_value(), ZX_OK);
+  fidl::WireClient<fuchsia_examples::Echo> client(std::move(*client_end), dispatcher());
+
+  std::string reply_received;
+  client->EchoString(kTestString)
+      .ThenExactlyOnce([&reply_received, quit_loop = QuitLoopClosure()](
+                           fidl::WireUnownedResult<fuchsia_examples::Echo::EchoString>& result) {
+        ASSERT_TRUE(result.ok()) << "EchoString failed: " << result.error().FormatDescription();
+        auto* response = result.Unwrap();
+        reply_received = std::string(response->response.data(), response->response.size());
+        quit_loop();
+      });
+  RunLoop();
+
+  EXPECT_EQ(reply_received, kTestString);
+}
+
+TEST_F(OutgoingDirectoryTest, AddDirectoryCanServeADirectory) {
+  static constexpr char kTestDirectory[] = "diagnostics";
+  static constexpr char kTestFile[] = "sample.txt";
+  static constexpr char kTestContent[] = "Hello World!";
+
+  fs::ManagedVfs vfs(dispatcher());
+  auto endpoints = fidl::CreateEndpoints<fuchsia_io::Directory>();
+  auto text_file = fbl::MakeRefCounted<fs::BufferedPseudoFile>(
+      /*read_handler=*/[](fbl::String* output) -> zx_status_t {
+        *output = kTestContent;
+        return ZX_OK;
+      });
+  auto diagnostics = fbl::MakeRefCounted<fs::PseudoDir>();
+  diagnostics->AddEntry(kTestFile, text_file);
+  vfs.ServeDirectory(diagnostics, std::move(endpoints->server));
+  ASSERT_EQ(GetOutgoingDirectory()
+                ->AddDirectory(std::move(endpoints->client), kTestDirectory)
+                .status_value(),
+            ZX_OK);
+
+  std::thread([client_end = TakeRootClientEnd().TakeChannel().release(),
+               quit_loop = QuitLoopClosure()]() {
+    fbl::unique_fd root_fd;
+    ASSERT_EQ(fdio_fd_create(client_end, root_fd.reset_and_get_address()), ZX_OK);
+    ZX_ASSERT_MSG(root_fd.is_valid(), "Failed to open root ns as a file descriptor: %s",
+                  strerror(errno));
+
+    fbl::unique_fd dir_fd(openat(root_fd.get(), kTestDirectory, O_DIRECTORY));
+    ZX_ASSERT_MSG(dir_fd.is_valid(), "Failed to open directory \"%s\": %s", kTestDirectory,
+                  strerror(errno));
+
+    fbl::unique_fd filefd(openat(dir_fd.get(), kTestFile, O_RDONLY));
+    ZX_ASSERT_MSG(filefd.is_valid(), "Failed to open file \"%s\": %s", kTestFile, strerror(errno));
+    static constexpr size_t kMaxBufferSize = 1024;
+    static char kReadBuffer[kMaxBufferSize];
+    size_t bytes_read = read(filefd.get(), reinterpret_cast<void*>(kReadBuffer), kMaxBufferSize);
+    ZX_ASSERT_MSG(bytes_read > 0, "Read 0 bytes from file at \"%s\": %s", kTestFile,
+                  strerror(errno));
+
+    std::string actual_content(kReadBuffer, bytes_read);
+    EXPECT_EQ(actual_content, kTestContent);
+    quit_loop();
+  }).detach();
+
+  RunLoop();
+
+  vfs.Shutdown([quit_loop = QuitLoopClosure()](zx_status_t status) {
+    ASSERT_EQ(status, ZX_OK);
+    quit_loop();
+  });
+  RunLoop();
+
+  EXPECT_EQ(GetOutgoingDirectory()->RemoveDirectory(kTestDirectory).status_value(), ZX_OK);
+}
+
+// Test that we can connect to the outgoing directory via multiple connections.
+TEST_F(OutgoingDirectoryTest, ServeCanYieldMultipleConnections) {
+  // Setup fuchsia.examples.Echo server
+  EchoImpl regular_impl(/*reversed=*/false);
+  ASSERT_EQ(
+      GetOutgoingDirectory()->AddProtocol<fuchsia_examples::Echo>(&regular_impl).status_value(),
+      ZX_OK);
+
+  // Setup fuchsia.examples.Echo client
+  auto endpoints = fidl::CreateEndpoints<fuchsia_io::Directory>();
+  // First |Serve| is invoked as part of test setup, so we'll assert that a
+  // subsequent invocation is allowed.
+  ASSERT_EQ(GetOutgoingDirectory()->Serve(std::move(endpoints->server)).status_value(), ZX_OK);
+
+  std::vector<fidl::ClientEnd<fuchsia_io::Directory>> root_client_ends;
+  // Take client end for channel used during invocation of |Serve| during setup.
+  root_client_ends.emplace_back(TakeRootClientEnd());
+  // Take client end for channel used during invocation of |Serve| in this function.
+  root_client_ends.emplace_back(std::move(endpoints->client));
+
+  while (!root_client_ends.empty()) {
+    auto root = std::move(root_client_ends.back());
+    root_client_ends.pop_back();
+
+    auto client_end =
+        service::ConnectAt<fuchsia_examples::Echo>(TakeSvcClientEnd(/*root=*/std::move(root)));
+    ASSERT_EQ(client_end.status_value(), ZX_OK);
+    fidl::WireClient<fuchsia_examples::Echo> client(std::move(*client_end), dispatcher());
+
+    std::string reply_received;
+    client->EchoString(kTestString)
+        .ThenExactlyOnce([&reply_received, quit_loop = QuitLoopClosure()](
+                             fidl::WireUnownedResult<fuchsia_examples::Echo::EchoString>& result) {
+          ASSERT_TRUE(result.ok()) << "EchoString failed: " << result.error();
+          auto* response = result.Unwrap();
+          reply_received = std::string(response->response.data(), response->response.size());
+          quit_loop();
+        });
+    RunLoop();
+
+    EXPECT_EQ(reply_received, kTestString);
+  }
+}
+
+TEST_F(OutgoingDirectoryTest, CreateFailsIfDispatcherIsNullptr) {
+  ASSERT_DEATH(
+      { auto outgoing_directory = component::OutgoingDirectory::Create(/*dispatcher=*/nullptr); },
+      "");
+}
+
+TEST_F(OutgoingDirectoryTest, ServeFailsIfHandleInvalid) {
+  auto outgoing_directory = component::OutgoingDirectory::Create(dispatcher());
+  auto endpoints = fidl::CreateEndpoints<fuchsia_io::Directory>();
+  // Close server end in order to  invalidate channel.
+  endpoints->server.reset();
+  EXPECT_EQ(outgoing_directory.Serve(std::move(endpoints->server)).status_value(),
+            ZX_ERR_BAD_HANDLE);
+}
+
+TEST_F(OutgoingDirectoryTest, AddServiceFailsIfInstanceNameIsEmpty) {
+  EXPECT_EQ(GetOutgoingDirectory()
+                ->AddService<fuchsia_examples::EchoService>(CreateNonEmptyServiceHandler(),
+                                                            /*instance=*/"")
+                .status_value(),
+            ZX_ERR_INVALID_ARGS);
+}
+
+TEST_F(OutgoingDirectoryTest, AddServiceFailsIfEntryExists) {
+  ASSERT_EQ(GetOutgoingDirectory()
+                ->AddService<fuchsia_examples::EchoService>(CreateNonEmptyServiceHandler())
+                .status_value(),
+            ZX_OK);
+
+  EXPECT_EQ(GetOutgoingDirectory()
+                ->AddService<fuchsia_examples::EchoService>(CreateNonEmptyServiceHandler())
+                .status_value(),
+            ZX_ERR_ALREADY_EXISTS);
+}
+
+TEST_F(OutgoingDirectoryTest, AddServiceFailsIfServiceHandlerEmpty) {
+  EXPECT_EQ(GetOutgoingDirectory()
+                ->AddService<fuchsia_examples::EchoService>(component::ServiceHandler())
+                .status_value(),
+            ZX_ERR_INVALID_ARGS);
+}
+
+TEST_F(OutgoingDirectoryTest, AddServiceFailsIfServiceNameIsEmpty) {
+  EXPECT_EQ(GetOutgoingDirectory()
+                ->AddService(CreateNonEmptyServiceHandler(), /*service=*/"",
+                             /*instance=*/component::kDefaultInstance)
+                .status_value(),
+            ZX_ERR_INVALID_ARGS);
+}
+
+TEST_F(OutgoingDirectoryTest, AddProtocolFailsIfImplIsNullptr) {
+  EXPECT_EQ(GetOutgoingDirectory()
+                ->AddProtocol<fuchsia_examples::Echo>(
+                    /*impl*/ static_cast<fidl::WireServer<fuchsia_examples::Echo>*>(nullptr))
+                .status_value(),
+            ZX_ERR_INVALID_ARGS);
+  EXPECT_EQ(GetOutgoingDirectory()
+                ->AddProtocol<fuchsia_examples::Echo>(
+                    /*impl*/ static_cast<fidl::Server<fuchsia_examples::Echo>*>(nullptr))
+                .status_value(),
+            ZX_ERR_INVALID_ARGS);
+}
+
+TEST_F(OutgoingDirectoryTest, AddProtocolFailsIfNameIsEmpty) {
+  EchoImpl regular_impl(/*reversed=*/false);
+  EXPECT_EQ(GetOutgoingDirectory()
+                ->AddProtocol<fuchsia_examples::Echo>(&regular_impl, /*name=*/"")
+                .status_value(),
+            ZX_ERR_INVALID_ARGS);
+}
+
+TEST_F(OutgoingDirectoryTest, AddProtocolFailsIfEntryExists) {
+  EchoImpl regular_impl(/*reversed=*/false);
+  ASSERT_EQ(
+      GetOutgoingDirectory()->AddProtocol<fuchsia_examples::Echo>(&regular_impl).status_value(),
+      ZX_OK);
+
+  EXPECT_EQ(
+      GetOutgoingDirectory()->AddProtocol<fuchsia_examples::Echo>(&regular_impl).status_value(),
+      ZX_ERR_ALREADY_EXISTS);
+}
+
+TEST_F(OutgoingDirectoryTest, AddProtocolAtFailsIfDirectoryIsEmpty) {
+  EchoImpl regular_impl(/*reversed=*/false);
+  EXPECT_EQ(GetOutgoingDirectory()
+                ->AddProtocolAt<fuchsia_examples::Echo>(/*directory=*/"", &regular_impl)
+                .status_value(),
+            ZX_ERR_INVALID_ARGS);
+}
+
+TEST_F(OutgoingDirectoryTest, AddDirectoryFailsIfRemoteDirInvalid) {
+  fidl::ClientEnd<fuchsia_io::Directory> dangling_client_end;
+  ASSERT_FALSE(dangling_client_end.is_valid());
+
+  EXPECT_EQ(GetOutgoingDirectory()
+                ->AddDirectory(std::move(dangling_client_end), "AValidName")
+                .status_value(),
+            ZX_ERR_BAD_HANDLE);
+}
+
+TEST_F(OutgoingDirectoryTest, AddDirectoryFailsIfDirectoryNameIsEmpty) {
+  auto endpoints = fidl::CreateEndpoints<fuchsia_io::Directory>();
+
+  EXPECT_EQ(GetOutgoingDirectory()
+                ->AddDirectory(std::move(endpoints->client), /*directory_name=*/"")
+                .status_value(),
+            ZX_ERR_INVALID_ARGS);
+}
+
+TEST_F(OutgoingDirectoryTest, AddDirectoryFailsIfEntryExists) {
+  constexpr char kDirectoryName[] = "test";
+
+  for (auto expected_status : {ZX_OK, ZX_ERR_ALREADY_EXISTS}) {
+    auto endpoints = fidl::CreateEndpoints<fuchsia_io::Directory>();
+    EXPECT_EQ(GetOutgoingDirectory()
+                  ->AddDirectory(std::move(endpoints->client), kDirectoryName)
+                  .status_value(),
+              expected_status);
+  }
+}
+
+TEST_F(OutgoingDirectoryTest, AddDirectoryFailsIfNameUsedForAddProtocolAt) {
+  constexpr char kDirectoryName[] = "diagnostics";
+
+  EchoImpl regular_impl(/*reversed=*/false);
+  ASSERT_EQ(GetOutgoingDirectory()
+                ->AddProtocolAt<fuchsia_examples::Echo>(kDirectoryName, &regular_impl)
+                .status_value(),
+            ZX_OK);
+
+  auto endpoints = fidl::CreateEndpoints<fuchsia_io::Directory>();
+  EXPECT_EQ(GetOutgoingDirectory()
+                ->AddDirectory(std::move(endpoints->client), kDirectoryName)
+                .status_value(),
+            ZX_ERR_ALREADY_EXISTS);
+}
+
+TEST_F(OutgoingDirectoryTest, RemoveServiceFailsIfEntryDoesNotExist) {
+  EXPECT_EQ(GetOutgoingDirectory()->RemoveService<fuchsia_examples::EchoService>().status_value(),
+            ZX_ERR_NOT_FOUND);
+}
+
+TEST_F(OutgoingDirectoryTest, RemoveProtocolFailsIfEntryDoesNotExist) {
+  EXPECT_EQ(GetOutgoingDirectory()->RemoveProtocol<fuchsia_examples::Echo>().status_value(),
+            ZX_ERR_NOT_FOUND);
+}
+
+TEST_F(OutgoingDirectoryTest, RemoveDirectoryFailsIfEntryDoesNotExist) {
+  EXPECT_EQ(GetOutgoingDirectory()->RemoveDirectory(/*directory_name=*/"test").status_value(),
+            ZX_ERR_NOT_FOUND);
+}
+
+class OutgoingDirectoryPathParameterizedFixture
+    : public testing::TestWithParam<std::pair<std::string, std::string>> {};
+
+TEST_P(OutgoingDirectoryPathParameterizedFixture, BadServicePaths) {
+  async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread);
+  auto outgoing_directory = component::OutgoingDirectory::Create(loop.dispatcher());
+  auto endpoints = fidl::CreateEndpoints<fuchsia_io::Directory>();
+  ZX_ASSERT(outgoing_directory.Serve(std::move(endpoints->server)).is_ok());
+  component::ServiceHandler service_handler;
+  fuchsia_examples::EchoService::Handler echo_service_handler(&service_handler);
+  EchoImpl regular_impl(/*reversed=*/false);
+  auto noop_handler = [](fidl::ServerEnd<fuchsia_examples::Echo> _request) -> void {};
+  ZX_ASSERT(echo_service_handler.add_regular_echo(std::move(noop_handler)).is_ok());
+
+  auto service_and_instance_names = GetParam();
+  EXPECT_EQ(outgoing_directory
+                .AddService(component::ServiceHandler(), service_and_instance_names.first,
+                            service_and_instance_names.second)
+                .status_value(),
+            ZX_ERR_INVALID_ARGS);
+}
+
+INSTANTIATE_TEST_SUITE_P(OutgoingDirectoryTestPathTest, OutgoingDirectoryPathParameterizedFixture,
+                         testing::Values(std::make_pair("", component::kDefaultInstance),
+                                         std::make_pair(".", component::kDefaultInstance),
+                                         std::make_pair("fuchsia.examples.EchoService", ""),
+                                         std::make_pair("fuchsia.examples.EchoService", "")));
+
+}  // namespace
diff --git a/sdk/lib/sys/component/cpp/tests/realm_builder_test.cc b/sdk/lib/sys/component/cpp/tests/realm_builder_test.cc
index 95a2263..220287f 100644
--- a/sdk/lib/sys/component/cpp/tests/realm_builder_test.cc
+++ b/sdk/lib/sys/component/cpp/tests/realm_builder_test.cc
@@ -46,10 +46,10 @@
 namespace fdecl = fuchsia::component::decl;
 
 constexpr char kEchoServerUrl[] =
-    "fuchsia-pkg://fuchsia.com/component_cpp_tests#meta/echo_server.cm";
+    "fuchsia-pkg://fuchsia.com/component_cpp_testing_realm_builder_tests#meta/echo_server.cm";
 constexpr char kEchoServerScUrl[] = "#meta/echo_server_sc.cm";
 constexpr char kEchoServerLegacyUrl[] =
-    "fuchsia-pkg://fuchsia.com/component_cpp_tests#meta/echo_server.cmx";
+    "fuchsia-pkg://fuchsia.com/component_cpp_testing_realm_builder_tests#meta/echo_server.cmx";
 constexpr char kEchoServerRelativeUrl[] = "#meta/echo_server.cm";
 constexpr char kEchoServiceServerUrl[] = "#meta/echo_service_server.cm";
 
@@ -92,39 +92,32 @@
                 "hello][1,0,][1,2,][2,3,][3,4,][4,5,][-1,-2,][-2,-3,][-3,-4,][-4,-5,][foo,bar,]"));
 }
 
-TEST_F(RealmBuilderTest, ReplaceConfigValue) {
+TEST_F(RealmBuilderTest, SetConfigValue) {
   static constexpr char kEchoServerSc[] = "echo_server_sc";
 
   auto realm_builder = RealmBuilder::Create();
   realm_builder.AddChild(kEchoServerSc, kEchoServerScUrl);
-  realm_builder.ReplaceConfigValue(kEchoServerSc, "my_flag", ConfigValue::Bool(true));
-  realm_builder.ReplaceConfigValue(kEchoServerSc, "my_uint8", ConfigValue::Uint8(1));
-  realm_builder.ReplaceConfigValue(kEchoServerSc, "my_uint16", ConfigValue::Uint16(1));
-  realm_builder.ReplaceConfigValue(kEchoServerSc, "my_uint32", ConfigValue::Uint32(1));
-  realm_builder.ReplaceConfigValue(kEchoServerSc, "my_uint64", ConfigValue::Uint64(1));
-  realm_builder.ReplaceConfigValue(kEchoServerSc, "my_int8", ConfigValue::Int8(-1));
-  realm_builder.ReplaceConfigValue(kEchoServerSc, "my_int16", ConfigValue::Int16(-1));
-  realm_builder.ReplaceConfigValue(kEchoServerSc, "my_int32", ConfigValue::Int32(-1));
-  realm_builder.ReplaceConfigValue(kEchoServerSc, "my_int64", ConfigValue::Int64(-1));
-  realm_builder.ReplaceConfigValue(kEchoServerSc, "my_string", "foo");
-  realm_builder.ReplaceConfigValue(kEchoServerSc, "my_vector_of_flag",
-                                   std::vector<bool>{false, true});
-  realm_builder.ReplaceConfigValue(kEchoServerSc, "my_vector_of_uint8", std::vector<uint8_t>{1, 1});
-  realm_builder.ReplaceConfigValue(kEchoServerSc, "my_vector_of_uint16",
-                                   std::vector<uint16_t>{1, 1});
-  realm_builder.ReplaceConfigValue(kEchoServerSc, "my_vector_of_uint32",
-                                   std::vector<uint32_t>{1, 1});
-  realm_builder.ReplaceConfigValue(kEchoServerSc, "my_vector_of_uint64",
-                                   std::vector<uint64_t>{1, 1});
-  realm_builder.ReplaceConfigValue(kEchoServerSc, "my_vector_of_int8", std::vector<int8_t>{-1, 1});
-  realm_builder.ReplaceConfigValue(kEchoServerSc, "my_vector_of_int16",
-                                   std::vector<int16_t>{-1, 1});
-  realm_builder.ReplaceConfigValue(kEchoServerSc, "my_vector_of_int32",
-                                   std::vector<int32_t>{-1, 1});
-  realm_builder.ReplaceConfigValue(kEchoServerSc, "my_vector_of_int64",
-                                   std::vector<int64_t>{-1, 1});
-  realm_builder.ReplaceConfigValue(kEchoServerSc, "my_vector_of_string",
-                                   std::vector<std::string>{"bar", "foo"});
+  realm_builder.SetConfigValue(kEchoServerSc, "my_flag", ConfigValue::Bool(true));
+  realm_builder.SetConfigValue(kEchoServerSc, "my_uint8", ConfigValue::Uint8(1));
+  realm_builder.SetConfigValue(kEchoServerSc, "my_uint16", ConfigValue::Uint16(1));
+  realm_builder.SetConfigValue(kEchoServerSc, "my_uint32", ConfigValue::Uint32(1));
+  realm_builder.SetConfigValue(kEchoServerSc, "my_uint64", ConfigValue::Uint64(1));
+  realm_builder.SetConfigValue(kEchoServerSc, "my_int8", ConfigValue::Int8(-1));
+  realm_builder.SetConfigValue(kEchoServerSc, "my_int16", ConfigValue::Int16(-1));
+  realm_builder.SetConfigValue(kEchoServerSc, "my_int32", ConfigValue::Int32(-1));
+  realm_builder.SetConfigValue(kEchoServerSc, "my_int64", ConfigValue::Int64(-1));
+  realm_builder.SetConfigValue(kEchoServerSc, "my_string", "foo");
+  realm_builder.SetConfigValue(kEchoServerSc, "my_vector_of_flag", std::vector<bool>{false, true});
+  realm_builder.SetConfigValue(kEchoServerSc, "my_vector_of_uint8", std::vector<uint8_t>{1, 1});
+  realm_builder.SetConfigValue(kEchoServerSc, "my_vector_of_uint16", std::vector<uint16_t>{1, 1});
+  realm_builder.SetConfigValue(kEchoServerSc, "my_vector_of_uint32", std::vector<uint32_t>{1, 1});
+  realm_builder.SetConfigValue(kEchoServerSc, "my_vector_of_uint64", std::vector<uint64_t>{1, 1});
+  realm_builder.SetConfigValue(kEchoServerSc, "my_vector_of_int8", std::vector<int8_t>{-1, 1});
+  realm_builder.SetConfigValue(kEchoServerSc, "my_vector_of_int16", std::vector<int16_t>{-1, 1});
+  realm_builder.SetConfigValue(kEchoServerSc, "my_vector_of_int32", std::vector<int32_t>{-1, 1});
+  realm_builder.SetConfigValue(kEchoServerSc, "my_vector_of_int64", std::vector<int64_t>{-1, 1});
+  realm_builder.SetConfigValue(kEchoServerSc, "my_vector_of_string",
+                               std::vector<std::string>{"bar", "foo"});
   realm_builder.AddRoute(Route{.capabilities = {Protocol{test::placeholders::Echo::Name_}},
                                .source = ChildRef{kEchoServerSc},
                                .targets = {ParentRef()}});
@@ -140,13 +133,13 @@
                                       "[1,1,][-1,1,][-1,1,][-1,1,][-1,1,][bar,foo,]"));
 }
 
-TEST_F(RealmBuilderTest, ReplaceConfigValueFails) {
+TEST_F(RealmBuilderTest, SetConfigValueFails) {
   ASSERT_DEATH(
       {
         static constexpr char kEchoServer[] = "echo_server";
         auto realm_builder = RealmBuilder::Create();
         realm_builder.AddChild(kEchoServer, kEchoServerRelativeUrl);
-        realm_builder.ReplaceConfigValue(kEchoServer, "my_flag", ConfigValue::Bool(true));
+        realm_builder.SetConfigValue(kEchoServer, "my_flag", ConfigValue::Bool(true));
       },
       "");
   ASSERT_DEATH(
@@ -154,7 +147,7 @@
         static constexpr char kEchoServerSc[] = "echo_server_sc";
         auto realm_builder = RealmBuilder::Create();
         realm_builder.AddChild(kEchoServerSc, kEchoServerScUrl);
-        realm_builder.ReplaceConfigValue(kEchoServerSc, "doesnt_exist", ConfigValue::Bool(true));
+        realm_builder.SetConfigValue(kEchoServerSc, "doesnt_exist", ConfigValue::Bool(true));
       },
       "");
   ASSERT_DEATH(
@@ -162,7 +155,7 @@
         static constexpr char kEchoServerSc[] = "echo_server_sc";
         auto realm_builder = RealmBuilder::Create();
         realm_builder.AddChild(kEchoServerSc, kEchoServerScUrl);
-        realm_builder.ReplaceConfigValue(kEchoServerSc, "my_string", ConfigValue::Bool(true));
+        realm_builder.SetConfigValue(kEchoServerSc, "my_string", ConfigValue::Bool(true));
       },
       "");
   ASSERT_DEATH(
@@ -170,7 +163,7 @@
         static constexpr char kEchoServerSc[] = "echo_server_sc";
         auto realm_builder = RealmBuilder::Create();
         realm_builder.AddChild(kEchoServerSc, kEchoServerScUrl);
-        realm_builder.ReplaceConfigValue(kEchoServerSc, "my_string", "abccdefghijklmnop");
+        realm_builder.SetConfigValue(kEchoServerSc, "my_string", "abccdefghijklmnop");
       },
       "");
   ASSERT_DEATH(
@@ -178,9 +171,8 @@
         static constexpr char kEchoServerSc[] = "echo_server_sc";
         auto realm_builder = RealmBuilder::Create();
         realm_builder.AddChild(kEchoServerSc, kEchoServerScUrl);
-        realm_builder.ReplaceConfigValue(
-            kEchoServerSc, "my_string",
-            std::vector<std::string>{"abcdefghijklmnopqrstuvwxyz", "abc"});
+        realm_builder.SetConfigValue(kEchoServerSc, "my_string",
+                                     std::vector<std::string>{"abcdefghijklmnopqrstuvwxyz", "abc"});
       },
       "");
 }
diff --git a/sdk/lib/sys/component/llcpp/BUILD.gn b/sdk/lib/sys/component/llcpp/BUILD.gn
index 4939b76..07716c4 100644
--- a/sdk/lib/sys/component/llcpp/BUILD.gn
+++ b/sdk/lib/sys/component/llcpp/BUILD.gn
@@ -4,6 +4,10 @@
 
 import("//build/cpp/sdk_source_set.gni")
 
+# DO NOT USE.
+# This library is deprecated in favor of //sdk/lib/sys/component/cpp.
+# TODO(http://fxbug.dev/103203): Remove this directory.
+
 sdk_source_set("llcpp") {
   category = "experimental"
   sdk_name = "sys_component_llcpp"
diff --git a/sdk/lib/sys/component/llcpp/handlers.h b/sdk/lib/sys/component/llcpp/handlers.h
index 54bcbe5..f2d30a0 100644
--- a/sdk/lib/sys/component/llcpp/handlers.h
+++ b/sdk/lib/sys/component/llcpp/handlers.h
@@ -23,17 +23,17 @@
 using TypedHandler = fit::function<void(fidl::ServerEnd<Protocol> request)>;
 
 // A handler for an instance of a FIDL Service.
-class ServiceHandler final : public fidl::ServiceHandlerInterface {
+class ServiceInstanceHandler final : public fidl::ServiceHandlerInterface {
  public:
-  ServiceHandler() = default;
+  ServiceInstanceHandler() = default;
 
   // Disable copying.
-  ServiceHandler(const ServiceHandler&) = delete;
-  ServiceHandler& operator=(const ServiceHandler&) = delete;
+  ServiceInstanceHandler(const ServiceInstanceHandler&) = delete;
+  ServiceInstanceHandler& operator=(const ServiceInstanceHandler&) = delete;
 
   // Enable moving.
-  ServiceHandler(ServiceHandler&&) = default;
-  ServiceHandler& operator=(ServiceHandler&&) = default;
+  ServiceInstanceHandler(ServiceInstanceHandler&&) = default;
+  ServiceInstanceHandler& operator=(ServiceInstanceHandler&&) = default;
 
  private:
   friend class OutgoingDirectory;
@@ -41,8 +41,8 @@
   // Return all registered member handlers. Key contains member name. Value
   // contains |Connector| func.
   //
-  // Once taken, the service handler is no longer safe to use.
-  std::map<std::string, AnyHandler> GetMemberHandlers() { return std::move(handlers_); }
+  // Once taken, the `ServiceInstanceHandler` is no longer safe to use.
+  std::map<std::string, AnyHandler> TakeMemberHandlers() { return std::move(handlers_); }
 
   // Add a |member| to the instance, whose connection will be handled by |handler|.
   //
@@ -67,6 +67,10 @@
   std::map<std::string, AnyHandler> handlers_ = {};
 };
 
+// Temporary alias until clients are migrated.
+// TODO(http://fxbug.dev/103207): Remove this.
+using ServiceHandler = ServiceInstanceHandler;
+
 }  // namespace component
 
 #endif  // LIB_SYS_COMPONENT_LLCPP_HANDLERS_H_
diff --git a/sdk/lib/sys/component/llcpp/outgoing_directory.cc b/sdk/lib/sys/component/llcpp/outgoing_directory.cc
index 8390a5d..bea42e4 100644
--- a/sdk/lib/sys/component/llcpp/outgoing_directory.cc
+++ b/sdk/lib/sys/component/llcpp/outgoing_directory.cc
@@ -54,12 +54,13 @@
   }
 }
 
-zx::status<> OutgoingDirectory::Serve(fidl::ServerEnd<fuchsia_io::Directory> directory_request) {
-  if (!directory_request.is_valid()) {
+zx::status<> OutgoingDirectory::Serve(fidl::ServerEnd<fuchsia_io::Directory> directory_server_end) {
+  if (!directory_server_end.is_valid()) {
     return zx::error_status(ZX_ERR_BAD_HANDLE);
   }
 
-  zx_status_t status = svc_dir_serve(root_, dispatcher_, directory_request.TakeHandle().release());
+  zx_status_t status =
+      svc_dir_serve(root_, dispatcher_, directory_server_end.TakeHandle().release());
   if (status != ZX_OK) {
     return zx::error_status(status);
   }
@@ -73,12 +74,12 @@
   return Serve(std::move(directory_request));
 }
 
-zx::status<> OutgoingDirectory::AddNamedProtocol(AnyHandler handler, cpp17::string_view name) {
-  return AddNamedProtocolAt(kServiceDirectory, name, std::move(handler));
+zx::status<> OutgoingDirectory::AddProtocol(AnyHandler handler, cpp17::string_view name) {
+  return AddProtocolAt(std::move(handler), kServiceDirectory, name);
 }
 
-zx::status<> OutgoingDirectory::AddNamedProtocolAt(cpp17::string_view path, cpp17::string_view name,
-                                                   AnyHandler handler) {
+zx::status<> OutgoingDirectory::AddProtocolAt(AnyHandler handler, cpp17::string_view path,
+                                              cpp17::string_view name) {
   // More thorough path validation is done in |svc_add_service|.
   if (path.empty() || name.empty()) {
     return zx::error_status(ZX_ERR_INVALID_ARGS);
@@ -121,20 +122,20 @@
   return zx::make_status(status);
 }
 
-zx::status<> OutgoingDirectory::AddNamedService(ServiceHandler handler, cpp17::string_view service,
-                                                cpp17::string_view instance) {
+zx::status<> OutgoingDirectory::AddService(ServiceHandler handler, cpp17::string_view service,
+                                           cpp17::string_view instance) {
   if (service.empty() || instance.empty()) {
     return zx::error_status(ZX_ERR_INVALID_ARGS);
   }
 
-  auto handlers = handler.GetMemberHandlers();
+  auto handlers = handler.TakeMemberHandlers();
   if (handlers.empty()) {
     return zx::make_status(ZX_ERR_INVALID_ARGS);
   }
 
   std::string basepath = MakePath(service, instance);
   for (auto& [member_name, member_handler] : handlers) {
-    zx::status<> status = AddNamedProtocolAt(basepath, member_name, std::move(member_handler));
+    zx::status<> status = AddProtocolAt(std::move(member_handler), basepath, member_name);
     if (status.is_error()) {
       // If we encounter an error with any of the instance members, scrub entire
       // directory entry.
@@ -146,12 +147,12 @@
   return zx::ok();
 }
 
-zx::status<> OutgoingDirectory::RemoveNamedProtocol(cpp17::string_view name) {
-  return RemoveNamedProtocolAt(kServiceDirectory, name);
+zx::status<> OutgoingDirectory::RemoveProtocol(cpp17::string_view name) {
+  return RemoveProtocolAt(kServiceDirectory, name);
 }
 
-zx::status<> OutgoingDirectory::RemoveNamedProtocolAt(cpp17::string_view directory,
-                                                      cpp17::string_view name) {
+zx::status<> OutgoingDirectory::RemoveProtocolAt(cpp17::string_view directory,
+                                                 cpp17::string_view name) {
   std::string key(directory);
 
   if (registered_handlers_.count(key) == 0) {
@@ -182,8 +183,8 @@
   return zx::ok();
 }
 
-zx::status<> OutgoingDirectory::RemoveNamedService(cpp17::string_view service,
-                                                   cpp17::string_view instance) {
+zx::status<> OutgoingDirectory::RemoveService(cpp17::string_view service,
+                                              cpp17::string_view instance) {
   std::string path = MakePath(service, instance);
   if (registered_handlers_.count(path) == 0) {
     return zx::make_status(ZX_ERR_NOT_FOUND);
@@ -211,10 +212,9 @@
   (context->handler)(zx::channel(handle));
 }
 
-void OutgoingDirectory::AppendUnbindConnectionCallback(cpp17::string_view name,
+void OutgoingDirectory::AppendUnbindConnectionCallback(const std::string& name,
                                                        UnbindConnectionCallback callback) {
-  auto key = std::string(name);
-  unbind_protocol_callbacks_[key].emplace_back(std::move(callback));
+  unbind_protocol_callbacks_[name].emplace_back(std::move(callback));
 }
 
 void OutgoingDirectory::UnbindAllConnections(cpp17::string_view name) {
diff --git a/sdk/lib/sys/component/llcpp/outgoing_directory.h b/sdk/lib/sys/component/llcpp/outgoing_directory.h
index ef2d0b5..69a44b6 100644
--- a/sdk/lib/sys/component/llcpp/outgoing_directory.h
+++ b/sdk/lib/sys/component/llcpp/outgoing_directory.h
@@ -31,7 +31,10 @@
 // to other components. For example the FIDL Protocol `fuchsia.foo.Bar` will be
 // hosted under the path `/svc/fuchsia.foo.Bar`.
 //
-// This class is thread-hostile.
+// This class is thread-hostile with respect to its interface. However, its
+// |async_dispatcher_t| may be multithreaded as long as it does not service
+// requests concurrently with any operations on the object's interface or its
+// destruction.
 //
 // # Simple usage
 //
@@ -48,12 +51,14 @@
 //
 // This class' API is semantically identical to the one found in
 // `//sdk/lib/sys/cpp`. This exists in order to offer equivalent facilities to
-// LLCPP (Low-level C++) FIDL bindings support. The other class is designed for
-// HLCPP( High-Level C++) FIDL bindings.
+// the new C++ bindings. The other class is designed for the older, HLCPP
+// (High-Level C++) FIDL bindings. It is expected that once
+// all clients of HLCPP are migrated to this unified bindings, that library will
+// be removed.
 class OutgoingDirectory final {
  public:
   // Creates an OutgoingDirectory which will serve requests when
-  // |Serve(zx::channel)| or |ServeFromStartupInfo()| is called.
+  // |Serve| or |ServeFromStartupInfo()| is called.
   //
   // |dispatcher| must not be nullptr. If it is, this method will panic.
   static OutgoingDirectory Create(async_dispatcher_t* dispatcher);
@@ -76,14 +81,16 @@
   // |AddProtocol|.
   //
   // This object will implement the |fuchsia.io.Directory| interface using this
-  // channel.
+  // channel. Note that this method returns immediately and that the |dispatcher|
+  // provided to the constructor will be responsible for processing messages
+  // sent to the server endpoint.
   //
   // # Errors
   //
-  // ZX_ERR_BAD_HANDLE: |directory_request| is not a valid handle.
+  // ZX_ERR_BAD_HANDLE: |directory_server_end| is not a valid handle.
   //
-  // ZX_ERR_ACCESS_DENIED: |directory_request| has insufficient rights.
-  zx::status<> Serve(fidl::ServerEnd<fuchsia_io::Directory> directory_request);
+  // ZX_ERR_ACCESS_DENIED: |directory_server_end| has insufficient rights.
+  zx::status<> Serve(fidl::ServerEnd<fuchsia_io::Directory> directory_server_end);
 
   // Starts serving the outgoing directory on the channel provided to this
   // process at startup as |PA_DIRECTORY_REQUEST|.
@@ -99,7 +106,8 @@
   // ZX_ERR_BAD_HANDLE: the process did not receive a |PA_DIRECTORY_REQUEST|
   // startup handle or it was already taken.
   //
-  // ZX_ERR_ACCESS_DENIED: |directory_request| has insufficient rights.
+  // ZX_ERR_ACCESS_DENIED: The |PA_DIRECTORY_REQUEST| handle has insufficient
+  // rights.
   zx::status<> ServeFromStartupInfo();
 
   // Adds a FIDL Protocol instance.
@@ -107,7 +115,7 @@
   // |impl| will be used to handle requests for this protocol.
   // |name| is used to determine where to host the protocol. This protocol will
   // be hosted under the path /svc/{name} where name is the discoverable name
-  // of the protocol.
+  // of the protocol by default.
   //
   // Note, if and when |RemoveProtocol| is called for the provided |name|, this
   // object will asynchronously close down the associated server end channel and
@@ -131,29 +139,6 @@
     return AddProtocolAt(kServiceDirectory, impl, name);
   }
 
-  // Same as above but allows overriding the parent directory in which the
-  // protocol will be hosted.
-  template <typename Protocol>
-  zx::status<> AddProtocolAt(cpp17::string_view path, fidl::WireServer<Protocol>* impl,
-                             cpp17::string_view name = fidl::DiscoverableProtocolName<Protocol>) {
-    if (impl == nullptr || dispatcher_ == nullptr) {
-      return zx::make_status(ZX_ERR_INVALID_ARGS);
-    }
-
-    return AddProtocolAt<Protocol>(
-        path,
-        [=](fidl::ServerEnd<Protocol> request) {
-          fidl::ServerBindingRef<Protocol> server =
-              fidl::BindServer(dispatcher_, std::move(request), impl);
-
-          auto cb = [server = std::move(server)]() mutable { server.Unbind(); };
-          // We don't have to check for entry existing because the |AddProtocol|
-          // overload being invoked here will do that internally.
-          AppendUnbindConnectionCallback(name, std::move(cb));
-        },
-        name);
-  }
-
   // Same as above but overloaded to support servers implementations speaking
   // FIDL C++ natural types: |fidl::Server<P>|, part of the unified bindings.
   template <typename Protocol>
@@ -201,6 +186,44 @@
     return AddProtocolAt<Protocol>(kServiceDirectory, std::move(handler), name);
   }
 
+  // Same as above but is untyped. This method is generally discouraged but
+  // is made available if a generic handler needs to be provided.
+  zx::status<> AddProtocol(AnyHandler handler, cpp17::string_view name);
+
+  // Same as above but allows overriding the parent directory in which the
+  // protocol will be hosted.
+  //
+  // |path| is used as the parent directory for the protocol, e.g. `svc`.
+  // |name| is the name under |path| at which the protocol will be hosted. By
+  // default, the FIDL protocol name, e.g. `fuchsia.logger.LogSink`, is used.
+  //
+  // # Errors
+  //
+  // ZX_ERR_ALREADY_EXISTS: An entry already exists for this protocol.
+  //
+  // ZX_ERR_INVALID_ARGS: |impl| is nullptr. |path| or |name| is an empty
+  // string.
+  template <typename Protocol>
+  zx::status<> AddProtocolAt(cpp17::string_view path, fidl::WireServer<Protocol>* impl,
+                             cpp17::string_view name = fidl::DiscoverableProtocolName<Protocol>) {
+    if (impl == nullptr || dispatcher_ == nullptr) {
+      return zx::make_status(ZX_ERR_INVALID_ARGS);
+    }
+
+    return AddProtocolAt<Protocol>(
+        path,
+        [=, name = std::string(name)](fidl::ServerEnd<Protocol> request) {
+          fidl::ServerBindingRef<Protocol> server =
+              fidl::BindServer(dispatcher_, std::move(request), impl);
+
+          auto cb = [server = std::move(server)]() mutable { server.Unbind(); };
+          // We don't have to check for entry existing because the |AddProtocol|
+          // overload being invoked here will do that internally.
+          AppendUnbindConnectionCallback(name, std::move(cb));
+        },
+        name);
+  }
+
   template <typename Protocol>
   zx::status<> AddProtocolAt(cpp17::string_view path, TypedHandler<Protocol> handler,
                              cpp17::string_view name = fidl::DiscoverableProtocolName<Protocol>) {
@@ -209,22 +232,20 @@
       (void)handler(std::move(server_end));
     };
 
-    return AddNamedProtocolAt(path, name, std::move(bridge_func));
+    return AddProtocolAt(std::move(bridge_func), path, name);
   }
 
-  // Same as above but is untyped. This method is generally discouraged but
-  // is made available if a generic handler needs to be provided.
-  zx::status<> AddNamedProtocol(AnyHandler handler, cpp17::string_view name);
-
-  zx::status<> AddNamedProtocolAt(cpp17::string_view path, cpp17::string_view name,
-                                  AnyHandler handler);
+  // Same as |AddProtocol| but is untyped and allows the usage of setting the
+  // parent directory in which the protocol will be installed.
+  zx::status<> AddProtocolAt(AnyHandler handler, cpp17::string_view path, cpp17::string_view name);
 
   // Adds an instance of a FIDL Service.
   //
   // A |handler| is added to provide an |instance| of a service.
   //
   // The template type |Service| must be the generated type representing a FIDL Service.
-  // The generated class |Service::Handler| helps the caller populate a |ServiceHandler|.
+  // The generated class |Service::Handler| helps the caller populate a
+  // |ServiceInstanceHandler|.
   //
   // # Errors
   //
@@ -237,27 +258,14 @@
   // See sample use cases in test case(s) located at
   // //sdk/lib/sys/component/llcpp/outgoing_directory_test.cc
   template <typename Service>
-  zx::status<> AddService(ServiceHandler handler, cpp17::string_view instance = kDefaultInstance) {
-    return AddNamedService(std::move(handler), Service::Name, instance);
+  zx::status<> AddService(ServiceInstanceHandler handler,
+                          cpp17::string_view instance = kDefaultInstance) {
+    return AddService(std::move(handler), Service::Name, instance);
   }
 
-  // Adds an instance of a FIDL Service.
-  //
-  // A |handler| is added to provide an |instance| of a |service|.
-  //
-  // # Errors
-  //
-  // ZX_ERR_ALREADY_EXISTS: The instance already exists.
-  //
-  // ZX_ERR_INVALID_ARGS: |service| or |instance| is an empty string. Also,
-  // if |handler| is empty.
-  //
-  // # Example
-  //
-  // See sample use cases in test case(s) located at
-  // //sdk/lib/sys/component/llcpp/outgoing_directory_test.cc
-  zx::status<> AddNamedService(ServiceHandler handler, cpp17::string_view service,
-                               cpp17::string_view instance = kDefaultInstance);
+  // Same as above but is untyped.
+  zx::status<> AddService(ServiceInstanceHandler handler, cpp17::string_view service,
+                          cpp17::string_view instance = kDefaultInstance);
 
   // Serve a subdirectory at the root of this outgoing directory.
   //
@@ -275,7 +283,7 @@
   zx::status<> AddDirectory(fidl::ClientEnd<fuchsia_io::Directory> remote_dir,
                             cpp17::string_view directory_name);
 
-  // Removes a FIDL Protocol entry.
+  // Removes a FIDL Protocol entry with the path `/svc/{name}`.
   //
   // # Errors
   //
@@ -288,20 +296,34 @@
   // ```
   template <typename Protocol>
   zx::status<> RemoveProtocol(cpp17::string_view name = fidl::DiscoverableProtocolName<Protocol>) {
-    return RemoveNamedProtocol(name);
+    return RemoveProtocol(name);
   }
 
   // Same as above but untyped.
-  zx::status<> RemoveNamedProtocol(cpp17::string_view name);
+  zx::status<> RemoveProtocol(cpp17::string_view name);
 
+  // Removes a FIDL Protocol entry located in the provided |directory|.
+  // Unlike |RemoveProtocol| which looks for the protocol to remove in the
+  // path `/svc/{name}`, this method uses the directory name provided, e.g.
+  // `/{path}/{name}`.
+  //
+  // # Errors
+  //
+  // ZX_ERR_NOT_FOUND: The protocol entry was not found.
+  //
+  // # Example
+  //
+  // ```
+  // outgoing.RemoveProtocolAt<lib_example::MyProtocol>("diagnostics");
+  // ```
   template <typename Protocol>
   zx::status<> RemoveProtocolAt(
-      cpp17::string_view directory,
-      cpp17::string_view name = fidl::DiscoverableProtocolName<Protocol>) {
-    return RemoveNamedProtocolAt(directory, name);
+      cpp17::string_view path, cpp17::string_view name = fidl::DiscoverableProtocolName<Protocol>) {
+    return RemoveProtocolAt(path, name);
   }
 
-  zx::status<> RemoveNamedProtocolAt(cpp17::string_view directory, cpp17::string_view name);
+  // Same as above but untyped.
+  zx::status<> RemoveProtocolAt(cpp17::string_view directory, cpp17::string_view name);
 
   // Removes an instance of a FIDL Service.
   //
@@ -316,16 +338,12 @@
   // ```
   template <typename Service>
   zx::status<> RemoveService(cpp17::string_view instance = kDefaultInstance) {
-    return RemoveNamedService(Service::Name, instance);
+    return RemoveService(Service::Name, instance);
   }
 
-  // Removes an instance of a FIDL Service.
-  //
-  // # Errors
-  //
-  // ZX_ERR_NOT_FOUND: The instance was not found.
-  zx::status<> RemoveNamedService(cpp17::string_view service,
-                                  cpp17::string_view instance = kDefaultInstance);
+  // Same as above but untyped.
+  zx::status<> RemoveService(cpp17::string_view service,
+                             cpp17::string_view instance = kDefaultInstance);
 
   // Removes the subdirectory on the provided |directory_name|.
   //
@@ -355,7 +373,7 @@
   // will close all active connections on the associated channel.
   using UnbindConnectionCallback = fit::callback<void()>;
 
-  void AppendUnbindConnectionCallback(cpp17::string_view name, UnbindConnectionCallback callback);
+  void AppendUnbindConnectionCallback(const std::string& name, UnbindConnectionCallback callback);
 
   void UnbindAllConnections(cpp17::string_view name);
 
@@ -367,7 +385,7 @@
 
   // Mapping of all registered protocol handlers. Key represents a path to
   // the directory in which the protocol ought to be installed. For example,
-  // a path may look like "svc/fuchsia.FooService/some_instance".
+  // a path may look like `svc/fuchsia.FooService/some_instance`.
   // The value contains a map of each of the entry's handlers.
   //
   // For FIDL Protocols, entries will be stored under "svc" entry
diff --git a/sdk/lib/sys/component/llcpp/tests/outgoing_directory_test.cc b/sdk/lib/sys/component/llcpp/tests/outgoing_directory_test.cc
index 8fae39d..644e916 100644
--- a/sdk/lib/sys/component/llcpp/tests/outgoing_directory_test.cc
+++ b/sdk/lib/sys/component/llcpp/tests/outgoing_directory_test.cc
@@ -528,10 +528,10 @@
             ZX_ERR_INVALID_ARGS);
 }
 
-TEST_F(OutgoingDirectoryTest, AddNamedServiceFailsIfServiceNameIsEmpty) {
+TEST_F(OutgoingDirectoryTest, AddServiceFailsIfServiceNameIsEmpty) {
   EXPECT_EQ(GetOutgoingDirectory()
-                ->AddNamedService(CreateNonEmptyServiceHandler(), /*service=*/"",
-                                  /*instance=*/component::kDefaultInstance)
+                ->AddService(CreateNonEmptyServiceHandler(), /*service=*/"",
+                             /*instance=*/component::kDefaultInstance)
                 .status_value(),
             ZX_ERR_INVALID_ARGS);
 }
@@ -654,8 +654,8 @@
 
   auto service_and_instance_names = GetParam();
   EXPECT_EQ(outgoing_directory
-                .AddNamedService(component::ServiceHandler(), service_and_instance_names.first,
-                                 service_and_instance_names.second)
+                .AddService(component::ServiceHandler(), service_and_instance_names.first,
+                            service_and_instance_names.second)
                 .status_value(),
             ZX_ERR_INVALID_ARGS);
 }
diff --git a/sdk/lib/zxio/create.cc b/sdk/lib/zxio/create.cc
index 5358f53..559378a 100644
--- a/sdk/lib/zxio/create.cc
+++ b/sdk/lib/zxio/create.cc
@@ -185,17 +185,6 @@
       return zxio_file_init(storage, node.TakeChannel().release(), event.release(),
                             stream.release());
     }
-    case fio::wire::NodeInfo::Tag::kPipe: {
-      fio::wire::PipeObject& pipe = info.pipe();
-      zx::socket socket = std::move(pipe.socket);
-      zx_info_socket_t socket_info;
-      zx_status_t status =
-          socket.get_info(ZX_INFO_SOCKET, &socket_info, sizeof(socket_info), nullptr, nullptr);
-      if (status != ZX_OK) {
-        return status;
-      }
-      return zxio_pipe_init(storage, std::move(socket), socket_info);
-    }
     case fio::wire::NodeInfo::Tag::kService: {
       return zxio_remote_init(storage, node.TakeChannel().release(), ZX_HANDLE_INVALID);
     }
diff --git a/sdk/lib/zxio/inception.cc b/sdk/lib/zxio/inception.cc
index c1eb044..1d2f593 100644
--- a/sdk/lib/zxio/inception.cc
+++ b/sdk/lib/zxio/inception.cc
@@ -64,9 +64,6 @@
     case fio::wire::NodeInfo::Tag::kFile:
       type = ZXIO_OBJECT_TYPE_FILE;
       break;
-    case fio::wire::NodeInfo::Tag::kPipe:
-      type = ZXIO_OBJECT_TYPE_PIPE;
-      break;
     case fio::wire::NodeInfo::Tag::kService:
       type = ZXIO_OBJECT_TYPE_SERVICE;
       break;
diff --git a/sdk/lib/zxio/include/lib/zxio/types.h b/sdk/lib/zxio/include/lib/zxio/types.h
index 9eb2d93..e29d48a 100644
--- a/sdk/lib/zxio/include/lib/zxio/types.h
+++ b/sdk/lib/zxio/include/lib/zxio/types.h
@@ -157,20 +157,12 @@
 #define ZXIO_NODE_PROTOCOL_DIRECTORY ((zxio_node_protocols_t)1ul << 1)
 #define ZXIO_NODE_PROTOCOL_FILE ((zxio_node_protocols_t)1ul << 2)
 #define ZXIO_NODE_PROTOCOL_MEMORY ((zxio_node_protocols_t)1ul << 3)
-#define ZXIO_NODE_PROTOCOL_PIPE ((zxio_node_protocols_t)1ul << 4)
-#define ZXIO_NODE_PROTOCOL_SYNCHRONOUS_DATAGRAM_SOCKET ((zxio_node_protocols_t)1ul << 5)
-#define ZXIO_NODE_PROTOCOL_STREAM_SOCKET ((zxio_node_protocols_t)1ul << 6)
-#define ZXIO_NODE_PROTOCOL_RAW_SOCKET ((zxio_node_protocols_t)1ul << 7)
-#define ZXIO_NODE_PROTOCOL_DATAGRAM_SOCKET ((zxio_node_protocols_t)1ul << 8)
 #define ZXIO_NODE_PROTOCOL_DEVICE ((zxio_node_protocols_t)0x10000000ul)
 #define ZXIO_NODE_PROTOCOL_TTY ((zxio_node_protocols_t)0x20000000ul)
 
 #define ZXIO_NODE_PROTOCOL_ALL                                                             \
   (ZXIO_NODE_PROTOCOL_CONNECTOR | ZXIO_NODE_PROTOCOL_DIRECTORY | ZXIO_NODE_PROTOCOL_FILE | \
-   ZXIO_NODE_PROTOCOL_MEMORY | ZXIO_NODE_PROTOCOL_PIPE |                                   \
-   ZXIO_NODE_PROTOCOL_SYNCHRONOUS_DATAGRAM_SOCKET | ZXIO_NODE_PROTOCOL_STREAM_SOCKET |     \
-   ZXIO_NODE_PROTOCOL_RAW_SOCKET | ZXIO_NODE_PROTOCOL_DATAGRAM_SOCKET |                    \
-   ZXIO_NODE_PROTOCOL_DEVICE | ZXIO_NODE_PROTOCOL_TTY)
+   ZXIO_NODE_PROTOCOL_MEMORY | ZXIO_NODE_PROTOCOL_DEVICE | ZXIO_NODE_PROTOCOL_TTY)
 
 typedef uint64_t zxio_id_t;
 
diff --git a/sdk/lib/zxio/pipe.cc b/sdk/lib/zxio/pipe.cc
index 38d8f40..806a159 100644
--- a/sdk/lib/zxio/pipe.cc
+++ b/sdk/lib/zxio/pipe.cc
@@ -37,7 +37,6 @@
 
   ops.attr_get = [](zxio_t* io, zxio_node_attributes_t* out_attr) {
     zxio_node_attributes_t attr = {};
-    ZXIO_NODE_ATTR_SET(attr, protocols, ZXIO_NODE_PROTOCOL_PIPE);
     ZXIO_NODE_ATTR_SET(
         attr, abilities,
         ZXIO_OPERATION_READ_BYTES | ZXIO_OPERATION_WRITE_BYTES | ZXIO_OPERATION_GET_ATTRIBUTES);
diff --git a/sdk/lib/zxio/remote.cc b/sdk/lib/zxio/remote.cc
index 25807de..7a1955a 100644
--- a/sdk/lib/zxio/remote.cc
+++ b/sdk/lib/zxio/remote.cc
@@ -128,21 +128,14 @@
         return ZXIO_NODE_PROTOCOL_TTY;
       case DT_DIR:
         return ZXIO_NODE_PROTOCOL_DIRECTORY;
-      case DT_FIFO:
-        return ZXIO_NODE_PROTOCOL_PIPE;
-      case DT_LNK:
-        // Not supported.
-        return ZXIO_NODE_PROTOCOL_NONE;
       case DT_REG:
         return ZXIO_NODE_PROTOCOL_FILE;
+      case DT_FIFO:
+        // Not supported.
+      case DT_LNK:
+        // Not supported.
       case DT_SOCK:
-        // TODO(jamesr): Switch to Directory2/Enumerate and remove this code.
-        //
-        // This points to the POSIX dirent data structure used by the io1
-        // directory enumeration protocol not being sufficient to fully describe
-        // the protocol(s) understood on a node. The io2 enumeration API lists
-        // the protocols understood by each node.
-        return ZXIO_NODE_PROTOCOL_STREAM_SOCKET;
+        // Not supported.
       default:
         return ZXIO_NODE_PROTOCOL_NONE;
     }
@@ -219,22 +212,20 @@
       return ZXIO_NODE_PROTOCOL_DEVICE;
     case S_IFREG:
       return ZXIO_NODE_PROTOCOL_FILE;
-    case S_IFIFO:
-      return ZXIO_NODE_PROTOCOL_PIPE;
+    case fuchsia_io::wire::kModeTypeService:
       // fuchsia::io has mode type service which breaks stat.
       // TODO(fxbug.dev/52930): return ZXIO_NODE_PROTOCOL_CONNECTOR instead.
-    case fuchsia_io::wire::kModeTypeService:
       return ZXIO_NODE_PROTOCOL_FILE;
+    case S_IFIFO:
+      // Named pipes are not supported on Fuchsia.
     case S_IFLNK:
       // Symbolic links are not supported on Fuchsia.
+    case S_IFSOCK:
+      // Named sockets are not supported on Fuchsia.
+    default:
       // A reasonable fallback is to keep the protocols unchanged,
       // i.e. same as getting a protocol we do not understand.
       return ZXIO_NODE_PROTOCOL_NONE;
-    case S_IFSOCK:
-      // TODO(jamesr): Could also be datagram or raw. How important is this?
-      return ZXIO_NODE_PROTOCOL_STREAM_SOCKET;
-    default:
-      return ZXIO_NODE_PROTOCOL_NONE;
   }
 }
 
@@ -251,21 +242,6 @@
   if (protocols & ZXIO_NODE_PROTOCOL_MEMORY) {
     return S_IFREG;
   }
-  if (protocols & ZXIO_NODE_PROTOCOL_PIPE) {
-    return S_IFIFO;
-  }
-  if (protocols & ZXIO_NODE_PROTOCOL_STREAM_SOCKET) {
-    return S_IFSOCK;
-  }
-  if (protocols & ZXIO_NODE_PROTOCOL_SYNCHRONOUS_DATAGRAM_SOCKET) {
-    return S_IFSOCK;
-  }
-  if (protocols & ZXIO_NODE_PROTOCOL_DATAGRAM_SOCKET) {
-    return S_IFSOCK;
-  }
-  if (protocols & ZXIO_NODE_PROTOCOL_RAW_SOCKET) {
-    return S_IFSOCK;
-  }
   if (protocols & ZXIO_NODE_PROTOCOL_DEVICE) {
     return S_IFBLK;
   }
diff --git a/sdk/lib/zxio/remote_v2/common_utils.cc b/sdk/lib/zxio/remote_v2/common_utils.cc
index 75be1a4..3f8c321 100644
--- a/sdk/lib/zxio/remote_v2/common_utils.cc
+++ b/sdk/lib/zxio/remote_v2/common_utils.cc
@@ -23,21 +23,6 @@
   if (protocols & NodeProtocols::kMemory) {
     zxio_protocols |= ZXIO_NODE_PROTOCOL_MEMORY;
   }
-  if (protocols & NodeProtocols::kPipe) {
-    zxio_protocols |= ZXIO_NODE_PROTOCOL_PIPE;
-  }
-  if (protocols & NodeProtocols::kSynchronousDatagramSocket) {
-    zxio_protocols |= ZXIO_NODE_PROTOCOL_SYNCHRONOUS_DATAGRAM_SOCKET;
-  }
-  if (protocols & NodeProtocols::kDatagramSocket) {
-    zxio_protocols |= ZXIO_NODE_PROTOCOL_DATAGRAM_SOCKET;
-  }
-  if (protocols & NodeProtocols::kStreamSocket) {
-    zxio_protocols |= ZXIO_NODE_PROTOCOL_STREAM_SOCKET;
-  }
-  if (protocols & NodeProtocols::kRawSocket) {
-    zxio_protocols |= ZXIO_NODE_PROTOCOL_RAW_SOCKET;
-  }
 
   if (protocols & NodeProtocols::kDevice) {
     zxio_protocols |= ZXIO_NODE_PROTOCOL_DEVICE;
@@ -62,21 +47,6 @@
   if (zxio_protocols & ZXIO_NODE_PROTOCOL_MEMORY) {
     protocols |= NodeProtocols::kMemory;
   }
-  if (zxio_protocols & ZXIO_NODE_PROTOCOL_PIPE) {
-    protocols |= NodeProtocols::kPipe;
-  }
-  if (zxio_protocols & ZXIO_NODE_PROTOCOL_SYNCHRONOUS_DATAGRAM_SOCKET) {
-    protocols |= NodeProtocols::kSynchronousDatagramSocket;
-  }
-  if (zxio_protocols & ZXIO_NODE_PROTOCOL_DATAGRAM_SOCKET) {
-    protocols |= NodeProtocols::kDatagramSocket;
-  }
-  if (zxio_protocols & ZXIO_NODE_PROTOCOL_STREAM_SOCKET) {
-    protocols |= NodeProtocols::kStreamSocket;
-  }
-  if (zxio_protocols & ZXIO_NODE_PROTOCOL_RAW_SOCKET) {
-    protocols |= NodeProtocols::kRawSocket;
-  }
 
   if (zxio_protocols & ZXIO_NODE_PROTOCOL_DEVICE) {
     protocols |= NodeProtocols::kDevice;
diff --git a/sdk/lib/zxio/tests/create-test.cc b/sdk/lib/zxio/tests/create-test.cc
index 74f710b..5c36863 100644
--- a/sdk/lib/zxio/tests/create-test.cc
+++ b/sdk/lib/zxio/tests/create-test.cc
@@ -373,36 +373,6 @@
   ASSERT_OK(zxio_close(zxio()));
 }
 
-TEST_F(CreateTest, Pipe) {
-  zx::socket socket0, socket1;
-  ASSERT_OK(zx::socket::create(0u, &socket0, &socket1));
-  node_server().set_describe_function(
-      [socket0 = std::move(socket0)](DescribeRequestView request,
-                                     DescribeCompleter::Sync& completer) mutable {
-        completer.Reply(fuchsia_io::wire::NodeInfo::WithPipe({
-            .socket = std::move(socket0),
-        }));
-      });
-
-  StartServerThread();
-
-  ASSERT_OK(zxio_create(TakeClientChannel().release(), storage()));
-
-  // Send some data through the kernel socket object and read it through zxio to
-  // sanity check that the pipe is functional.
-  int32_t data = 0x1a2a3a4a;
-  size_t actual = 0u;
-  ASSERT_OK(socket1.write(0u, &data, sizeof(data), &actual));
-  EXPECT_EQ(actual, sizeof(data));
-
-  int32_t buffer = 0;
-  ASSERT_OK(zxio_read(zxio(), &buffer, sizeof(buffer), 0u, &actual));
-  EXPECT_EQ(actual, sizeof(buffer));
-  EXPECT_EQ(buffer, data);
-
-  ASSERT_OK(zxio_close(zxio()));
-}
-
 TEST(CreateWithTypeTest, Pipe) {
   zx::socket socket0, socket1;
   ASSERT_OK(zx::socket::create(0u, &socket0, &socket1));
@@ -455,32 +425,6 @@
   ASSERT_OK(zxio_close(zxio));
 }
 
-TEST_F(CreateWithOnOpenTest, Pipe) {
-  zx::socket socket0, socket1;
-  ASSERT_OK(zx::socket::create(0u, &socket0, &socket1));
-  SendOnOpenEvent(fuchsia_io::wire::NodeInfo::WithPipe({
-      .socket = std::move(socket0),
-  }));
-
-  ASSERT_OK(zxio_create_with_on_open(TakeClientChannel().release(), storage()));
-
-  StartServerThread();
-
-  // Send some data through the kernel socket object and read it through zxio to
-  // sanity check that the pipe is functional.
-  int32_t data = 0x1a2a3a4a;
-  size_t actual = 0u;
-  ASSERT_OK(socket1.write(0u, &data, sizeof(data), &actual));
-  EXPECT_EQ(actual, sizeof(data));
-
-  int32_t buffer = 0;
-  ASSERT_OK(zxio_read(zxio(), &buffer, sizeof(buffer), 0u, &actual));
-  EXPECT_EQ(actual, sizeof(buffer));
-  EXPECT_EQ(buffer, data);
-
-  ASSERT_OK(zxio_close(zxio()));
-}
-
 TEST_F(CreateTest, Service) {
   node_server().set_describe_function(
       [](DescribeRequestView request, DescribeCompleter::Sync& completer) mutable {
diff --git a/sdk/lib/zxio/tests/inception-test.cc b/sdk/lib/zxio/tests/inception-test.cc
index 60244ca..84db001 100644
--- a/sdk/lib/zxio/tests/inception-test.cc
+++ b/sdk/lib/zxio/tests/inception-test.cc
@@ -258,50 +258,6 @@
   file_control_loop.Shutdown();
 }
 
-TEST(CreateWithInfo, Pipe) {
-  zx::status node_ends = fidl::CreateEndpoints<fuchsia_io::Node>();
-  ASSERT_OK(node_ends.status_value());
-  auto [node_client, node_server] = std::move(node_ends.value());
-
-  zx::socket socket0, socket1;
-  ASSERT_OK(zx::socket::create(0u, &socket0, &socket1));
-  fuchsia_io::wire::PipeObject pipe = {.socket = std::move(socket0)};
-  auto node_info = fuchsia_io::wire::NodeInfo::WithPipe(std::move(pipe));
-
-  auto allocator = [](zxio_object_type_t type, zxio_storage_t** out_storage, void** out_context) {
-    if (type != ZXIO_OBJECT_TYPE_PIPE) {
-      return ZX_ERR_NOT_SUPPORTED;
-    }
-    *out_storage = new zxio_storage_t;
-    *out_context = *out_storage;
-    return ZX_OK;
-  };
-
-  void* context = nullptr;
-  ASSERT_OK(zxio_create_with_allocator(std::move(node_client), node_info, allocator, &context));
-  ASSERT_NE(context, nullptr);
-
-  // The socket in node_info should be consumed by zxio.
-  EXPECT_FALSE(node_info.pipe().socket.is_valid());
-
-  std::unique_ptr<zxio_storage_t> storage(static_cast<zxio_storage_t*>(context));
-  zxio_t* zxio = &(storage->io);
-
-  // Send some data through the kernel socket object and read it through zxio to
-  // sanity check that the pipe is functional.
-  int32_t data = 0x1a2a3a4a;
-  size_t actual = 0u;
-  ASSERT_OK(socket1.write(0u, &data, sizeof(data), &actual));
-  EXPECT_EQ(actual, sizeof(data));
-
-  int32_t buffer = 0;
-  ASSERT_OK(zxio_read(zxio, &buffer, sizeof(buffer), 0u, &actual));
-  EXPECT_EQ(actual, sizeof(buffer));
-  EXPECT_EQ(buffer, data);
-
-  ASSERT_OK(zxio_close(zxio));
-}
-
 class TestServiceNodeServer : public fidl::testing::WireTestBase<fuchsia_io::Node> {
  public:
   void NotImplemented_(const std::string& name, fidl::CompleterBase& completer) final {
diff --git a/sdk/lib/zxio/tests/posix-mode-test.cc b/sdk/lib/zxio/tests/posix-mode-test.cc
index 6e4f306..42a649a 100644
--- a/sdk/lib/zxio/tests/posix-mode-test.cc
+++ b/sdk/lib/zxio/tests/posix-mode-test.cc
@@ -17,8 +17,6 @@
   EXPECT_EQ(S_IFDIR, zxio_node_protocols_to_posix_type(ZXIO_NODE_PROTOCOL_DIRECTORY));
   EXPECT_EQ(S_IFREG, zxio_node_protocols_to_posix_type(ZXIO_NODE_PROTOCOL_FILE));
   EXPECT_EQ(S_IFREG, zxio_node_protocols_to_posix_type(ZXIO_NODE_PROTOCOL_MEMORY));
-  EXPECT_EQ(S_IFSOCK, zxio_node_protocols_to_posix_type(ZXIO_NODE_PROTOCOL_STREAM_SOCKET));
-  EXPECT_EQ(S_IFIFO, zxio_node_protocols_to_posix_type(ZXIO_NODE_PROTOCOL_PIPE));
   EXPECT_EQ(S_IFBLK, zxio_node_protocols_to_posix_type(ZXIO_NODE_PROTOCOL_DEVICE));
   EXPECT_EQ(S_IFCHR, zxio_node_protocols_to_posix_type(ZXIO_NODE_PROTOCOL_TTY));
 }
diff --git a/sdk/manifests/core.manifest b/sdk/manifests/core.manifest
index c2f2354..dea2070 100644
--- a/sdk/manifests/core.manifest
+++ b/sdk/manifests/core.manifest
@@ -191,6 +191,7 @@
 sdk://tools/arm64/blobfs_do_not_depend
 sdk://tools/arm64/bootserver
 sdk://tools/arm64/cmc
+sdk://tools/arm64/configc
 sdk://tools/arm64/device-finder
 sdk://tools/arm64/far
 sdk://tools/arm64/fconfig
@@ -229,6 +230,7 @@
 sdk://tools/x64/blobfs_do_not_depend
 sdk://tools/x64/bootserver
 sdk://tools/x64/cmc
+sdk://tools/x64/configc
 sdk://tools/x64/device-finder
 sdk://tools/x64/far
 sdk://tools/x64/fconfig
diff --git a/sdk/manifests/ddk.manifest b/sdk/manifests/ddk.manifest
index d3a4645..396ee79d8 100644
--- a/sdk/manifests/ddk.manifest
+++ b/sdk/manifests/ddk.manifest
@@ -51,6 +51,7 @@
 sdk://pkg/stdcompat
 sdk://pkg/svc
 sdk://pkg/sync
+sdk://pkg/sys_component_cpp
 sdk://pkg/sys_component_llcpp
 sdk://pkg/syslog
 sdk://pkg/syslog_structured_backend
@@ -64,4 +65,4 @@
 sdk://tools/arm64/fidlgen_cpp_experimental_driver_only_toolchain
 sdk://tools/x64/bindc
 sdk://tools/x64/fidlgen_banjo
-sdk://tools/x64/fidlgen_cpp_experimental_driver_only_toolchain
+sdk://tools/x64/fidlgen_cpp_experimental_driver_only_toolchain
\ No newline at end of file
diff --git a/src/cobalt/bin/system-metrics/log_stats_component_allowlist.txt b/src/cobalt/bin/system-metrics/log_stats_component_allowlist.txt
index c12d8ff..9fbd6c73 100644
--- a/src/cobalt/bin/system-metrics/log_stats_component_allowlist.txt
+++ b/src/cobalt/bin/system-metrics/log_stats_component_allowlist.txt
@@ -7,7 +7,6 @@
 6 fuchsia-pkg://fuchsia.com/mdns#meta/mdns.cmx
 7 fuchsia-pkg://fuchsia.com/a11y-manager#meta/a11y-manager.cmx
 8 fuchsia-pkg://fuchsia.com/cast_runner#meta/cast_runner.cmx
-9 fuchsia-pkg://fuchsia.com/dhcpd#meta/dhcpd.cm
 10 fuchsia-pkg://fuchsia.com/scenic#meta/scenic.cmx
 11 fuchsia-pkg://fuchsia.com/sessionmgr#meta/sessionmgr.cmx
 12 fuchsia-pkg://fuchsia.com/audio_core#meta/audio_core.cmx
diff --git a/src/connectivity/bluetooth/core/bt-host/gap/adapter.cc b/src/connectivity/bluetooth/core/bt-host/gap/adapter.cc
index 8ec3046..23379c4 100644
--- a/src/connectivity/bluetooth/core/bt-host/gap/adapter.cc
+++ b/src/connectivity/bluetooth/core/bt-host/gap/adapter.cc
@@ -272,15 +272,19 @@
  private:
   // Second step of the initialization sequence. Called by Initialize() when the
   // first batch of HCI commands have been sent.
-  void InitializeStep2(InitializeCallback callback);
+  void InitializeStep2();
 
   // Third step of the initialization sequence. Called by InitializeStep2() when
   // the second batch of HCI commands have been sent.
-  void InitializeStep3(InitializeCallback callback);
+  void InitializeStep3();
 
   // Fourth step of the initialization sequence. Called by InitializeStep3()
   // when the third batch of HCI commands have been sent.
-  void InitializeStep4(InitializeCallback callback);
+  void InitializeStep4();
+
+  // Returns true if initialization was completed, or false if initialization is not
+  // in progress.
+  bool CompleteInitialization(bool success);
 
   // Assigns properties to |adapter_node_| using values discovered during other initialization
   // steps.
@@ -385,6 +389,9 @@
   std::atomic<State> init_state_;
   std::unique_ptr<hci::SequentialCommandRunner> init_seq_runner_;
 
+  // The callback passed to Initialize(). Null after initialization completes.
+  InitializeCallback init_cb_;
+
   // Contains the global adapter state.
   AdapterState state_;
 
@@ -504,12 +511,11 @@
   }
 
   ZX_DEBUG_ASSERT(!IsInitializing());
-
-  init_state_ = State::kInitializing;
-
   ZX_DEBUG_ASSERT(init_seq_runner_->IsReady());
   ZX_DEBUG_ASSERT(!init_seq_runner_->HasQueuedCommands());
 
+  init_state_ = State::kInitializing;
+  init_cb_ = std::move(callback);
   transport_closed_cb_ = std::move(transport_closed_cb);
 
   state_.vendor_features_ = hci_->GetVendorFeatures();
@@ -586,17 +592,15 @@
         });
   }
 
-  init_seq_runner_->RunCommands(
-      [callback = std::move(callback), this](hci::Result<> status) mutable {
-        if (bt_is_error(status, ERROR, "gap", "Failed to obtain initial controller information: %s",
-                        bt_str(status))) {
-          CleanUp();
-          callback(false);
-          return;
-        }
+  init_seq_runner_->RunCommands([this](hci::Result<> status) mutable {
+    if (bt_is_error(status, ERROR, "gap", "Failed to obtain initial controller information: %s",
+                    bt_str(status))) {
+      CompleteInitialization(/*success=*/false);
+      return;
+    }
 
-        InitializeStep2(std::move(callback));
-      });
+    InitializeStep2();
+  });
 
   return true;
 }
@@ -695,14 +699,13 @@
                                                            "open_l2cap_channel_requests");
 }
 
-void AdapterImpl::InitializeStep2(InitializeCallback callback) {
+void AdapterImpl::InitializeStep2() {
   ZX_DEBUG_ASSERT(IsInitializing());
 
   // Low Energy MUST be supported. We don't support BR/EDR-only controllers.
   if (!state_.IsLowEnergySupported()) {
     bt_log(ERROR, "gap", "Bluetooth LE not supported by controller");
-    CleanUp();
-    callback(false);
+    CompleteInitialization(/*success=*/false);
     return;
   }
 
@@ -816,19 +819,17 @@
         });
   }
 
-  init_seq_runner_->RunCommands(
-      [callback = std::move(callback), this](hci::Result<> status) mutable {
-        if (bt_is_error(status, ERROR, "gap",
-                        "failed to obtain initial controller information (step 2)")) {
-          CleanUp();
-          callback(false);
-          return;
-        }
-        InitializeStep3(std::move(callback));
-      });
+  init_seq_runner_->RunCommands([this](hci::Result<> status) mutable {
+    if (bt_is_error(status, ERROR, "gap",
+                    "failed to obtain initial controller information (step 2)")) {
+      CompleteInitialization(/*success=*/false);
+      return;
+    }
+    InitializeStep3();
+  });
 }
 
-void AdapterImpl::InitializeStep3(InitializeCallback callback) {
+void AdapterImpl::InitializeStep3() {
   ZX_ASSERT(IsInitializing());
   ZX_ASSERT(init_seq_runner_->IsReady());
   ZX_ASSERT(!init_seq_runner_->HasQueuedCommands());
@@ -836,8 +837,7 @@
   if (!state_.bredr_data_buffer_info().IsAvailable() &&
       !state_.low_energy_state().data_buffer_info().IsAvailable()) {
     bt_log(ERROR, "gap", "Both BR/EDR and LE buffers are unavailable");
-    CleanUp();
-    callback(false);
+    CompleteInitialization(/*success=*/false);
     return;
   }
 
@@ -846,8 +846,7 @@
   if (!hci_->InitializeACLDataChannel(state_.bredr_data_buffer_info(),
                                       state_.low_energy_state().data_buffer_info())) {
     bt_log(ERROR, "gap", "Failed to initialize ACLDataChannel (step 3)");
-    CleanUp();
-    callback(false);
+    CompleteInitialization(/*success=*/false);
     return;
   }
 
@@ -959,19 +958,17 @@
         });
   }
 
-  init_seq_runner_->RunCommands(
-      [callback = std::move(callback), this](hci::Result<> status) mutable {
-        if (bt_is_error(status, ERROR, "gap",
-                        "failed to obtain initial controller information (step 3)")) {
-          CleanUp();
-          callback(false);
-          return;
-        }
-        InitializeStep4(std::move(callback));
-      });
+  init_seq_runner_->RunCommands([this](hci::Result<> status) mutable {
+    if (bt_is_error(status, ERROR, "gap",
+                    "failed to obtain initial controller information (step 3)")) {
+      CompleteInitialization(/*success=*/false);
+      return;
+    }
+    InitializeStep4();
+  });
 }
 
-void AdapterImpl::InitializeStep4(InitializeCallback callback) {
+void AdapterImpl::InitializeStep4() {
   // Initialize the scan manager and low energy adapters based on current feature support
   ZX_DEBUG_ASSERT(IsInitializing());
 
@@ -1047,19 +1044,31 @@
 
   // Assign a default name and device class before notifying completion.
   auto self = weak_ptr_factory_.GetWeakPtr();
-  SetLocalName(kDefaultLocalName, [self, callback = std::move(callback)](auto status) mutable {
+  SetLocalName(kDefaultLocalName, [self](auto status) mutable {
     // Set the default device class - a computer with audio.
     // TODO(fxbug.dev/1234): set this from a platform configuration file
     DeviceClass dev_class(DeviceClass::MajorClass::kComputer);
     dev_class.SetServiceClasses({DeviceClass::ServiceClass::kAudio});
-    self->SetDeviceClass(dev_class, [self, callback = std::move(callback)](const auto&) {
-      // This completes the initialization sequence.
-      self->init_state_ = State::kInitialized;
-      callback(true);
-    });
+    self->SetDeviceClass(dev_class,
+                         [self](const auto&) { self->CompleteInitialization(/*success=*/true); });
   });
 }
 
+bool AdapterImpl::CompleteInitialization(bool success) {
+  if (!init_cb_) {
+    return false;
+  }
+
+  if (success) {
+    init_state_ = State::kInitialized;
+  } else {
+    CleanUp();
+  }
+
+  init_cb_(success);
+  return true;
+}
+
 void AdapterImpl::UpdateInspectProperties() {
   inspect_properties_.adapter_id = adapter_node_.CreateString("adapter_id", identifier_.ToString());
   inspect_properties_.hci_version =
@@ -1120,8 +1129,12 @@
 
 void AdapterImpl::OnTransportClosed() {
   bt_log(INFO, "gap", "HCI transport was closed");
-  if (transport_closed_cb_)
+  if (CompleteInitialization(/*success=*/false)) {
+    return;
+  }
+  if (transport_closed_cb_) {
     transport_closed_cb_();
+  }
 }
 
 void AdapterImpl::OnLeAutoConnectRequest(Peer* peer) {
diff --git a/src/connectivity/bluetooth/core/bt-host/gap/adapter.h b/src/connectivity/bluetooth/core/bt-host/gap/adapter.h
index 7bfef72..1ca8d4f 100644
--- a/src/connectivity/bluetooth/core/bt-host/gap/adapter.h
+++ b/src/connectivity/bluetooth/core/bt-host/gap/adapter.h
@@ -87,7 +87,7 @@
   // device disappeared or the transport channels were closed for an unknown
   // reason). The implementation is responsible for cleaning up this adapter by
   // calling ShutDown().
-  using InitializeCallback = fit::function<void(bool success)>;
+  using InitializeCallback = fit::callback<void(bool success)>;
   virtual bool Initialize(InitializeCallback callback, fit::closure transport_closed_callback) = 0;
 
   // Shuts down this Adapter. Invokes |callback| when shut down has completed.
diff --git a/src/connectivity/bluetooth/core/bt-host/gap/adapter_unittest.cc b/src/connectivity/bluetooth/core/bt-host/gap/adapter_unittest.cc
index b2708e5..874245d 100644
--- a/src/connectivity/bluetooth/core/bt-host/gap/adapter_unittest.cc
+++ b/src/connectivity/bluetooth/core/bt-host/gap/adapter_unittest.cc
@@ -255,6 +255,35 @@
   EXPECT_FALSE(transport_closed_called());
 }
 
+TEST_F(AdapterTest, InitializeFailureTransportErrorDuringWriteLocalName) {
+  std::optional<bool> success;
+  int init_cb_count = 0;
+  auto init_cb = [&](bool cb_success) {
+    success = cb_success;
+    init_cb_count++;
+  };
+
+  // Make all settings valid but make an HCI command fail.
+  FakeController::Settings settings;
+  settings.ApplyDualModeDefaults();
+  test_device()->set_settings(settings);
+  fit::closure resume_write_local_name_cb = nullptr;
+  test_device()->pause_responses_for_opcode(hci_spec::kWriteLocalName, [&](fit::closure resume) {
+    resume_write_local_name_cb = std::move(resume);
+  });
+
+  InitializeAdapter(std::move(init_cb));
+  ASSERT_TRUE(resume_write_local_name_cb);
+  EXPECT_EQ(0, init_cb_count);
+
+  // Signaling an error should cause Transport to close, which should cause initialization to fail.
+  test_device()->SignalError(ZX_ERR_IO);
+  ASSERT_TRUE(success.has_value());
+  EXPECT_FALSE(*success);
+  EXPECT_EQ(1, init_cb_count);
+  EXPECT_FALSE(transport_closed_called());
+}
+
 TEST_F(AdapterTest, TransportClosedCallback) {
   bool success;
   int init_cb_count = 0;
@@ -279,6 +308,7 @@
   RunLoopUntilIdle();
 
   EXPECT_TRUE(transport_closed_called());
+  EXPECT_EQ(1, init_cb_count);
 }
 
 // TODO(fxbug.dev/1512): Add a unit test for Adapter::ShutDown() and update
diff --git a/src/connectivity/bluetooth/core/bt-host/gap/bredr_connection_manager.cc b/src/connectivity/bluetooth/core/bt-host/gap/bredr_connection_manager.cc
index 39b21228..f05e8b7 100644
--- a/src/connectivity/bluetooth/core/bt-host/gap/bredr_connection_manager.cc
+++ b/src/connectivity/bluetooth/core/bt-host/gap/bredr_connection_manager.cc
@@ -191,13 +191,19 @@
 }
 
 BrEdrConnectionManager::~BrEdrConnectionManager() {
+  // Disconnect any connections that we're holding.
+  connections_.clear();
+
+  if (!hci_ || !hci_->command_channel()) {
+    return;
+  }
+
   if (pending_request_ && pending_request_->Cancel())
     SendCreateConnectionCancelCommand(pending_request_->peer_address());
 
-  // Disconnect any connections that we're holding.
-  connections_.clear();
   // Become unconnectable
   SetPageScanEnabled(/*enabled=*/false, hci_, dispatcher_, [](const auto) {});
+
   // Remove all event handlers
   for (auto handler_id : event_handler_ids_) {
     hci_->command_channel()->RemoveEventHandler(handler_id);
diff --git a/src/connectivity/bluetooth/core/bt-host/gap/fake_adapter.cc b/src/connectivity/bluetooth/core/bt-host/gap/fake_adapter.cc
index 31c2254..d72f54d 100644
--- a/src/connectivity/bluetooth/core/bt-host/gap/fake_adapter.cc
+++ b/src/connectivity/bluetooth/core/bt-host/gap/fake_adapter.cc
@@ -16,7 +16,7 @@
 
 bool FakeAdapter::Initialize(InitializeCallback callback, fit::closure transport_closed_callback) {
   init_state_ = InitState::kInitializing;
-  async::PostTask(async_get_default_dispatcher(), [this, cb = std::move(callback)] {
+  async::PostTask(async_get_default_dispatcher(), [this, cb = std::move(callback)]() mutable {
     init_state_ = InitState::kInitialized;
     cb(/*success=*/true);
   });
diff --git a/src/connectivity/bluetooth/core/bt-host/host_unittest.cc b/src/connectivity/bluetooth/core/bt-host/host_unittest.cc
index 9432a02..88e5d2f 100644
--- a/src/connectivity/bluetooth/core/bt-host/host_unittest.cc
+++ b/src/connectivity/bluetooth/core/bt-host/host_unittest.cc
@@ -117,16 +117,21 @@
       [&]() { error_cb_called = true; });
 
   // Any command sent to the command channel will time out, so run the loop until it does, and the
-  // transport should signal failure, which should call our error callback.
+  // transport should signal failure, which should call our init callback.
   constexpr zx::duration kCommandTimeout = zx::sec(12);
   RunLoopFor(kCommandTimeout);
-  EXPECT_TRUE(error_cb_called);
-  EXPECT_FALSE(init_cb_result.has_value());
+  ASSERT_TRUE(init_cb_result.has_value());
+  EXPECT_FALSE(*init_cb_result);
+  EXPECT_FALSE(error_cb_called);
+  init_cb_result.reset();
 
   host()->ShutDown();
-  ASSERT_TRUE(init_cb_result.has_value());
-  EXPECT_FALSE(init_cb_result.value());
+  EXPECT_FALSE(init_cb_result.has_value());
+  EXPECT_FALSE(error_cb_called);
+
   DestroyHost();
+  EXPECT_FALSE(init_cb_result.has_value());
+  EXPECT_FALSE(error_cb_called);
 }
 
 }  // namespace
diff --git a/src/connectivity/bluetooth/core/bt-host/testing/controller_test_double_base.h b/src/connectivity/bluetooth/core/bt-host/testing/controller_test_double_base.h
index db65837..9fabc81 100644
--- a/src/connectivity/bluetooth/core/bt-host/testing/controller_test_double_base.h
+++ b/src/connectivity/bluetooth/core/bt-host/testing/controller_test_double_base.h
@@ -76,13 +76,13 @@
   void HandleACLPacket(std::unique_ptr<hci::ACLDataPacket> packet);
   void HandleScoPacket(std::unique_ptr<hci::ScoDataPacket> packet);
 
- protected:
   void SignalError(zx_status_t status) {
     if (error_cb_) {
       error_cb_(status);
     }
   }
 
+ protected:
   const zx::channel& snoop_channel() const { return snoop_channel_; }
 
   // Called when there is an outgoing command packet.
diff --git a/src/connectivity/lowpan/lib/lowpan_driver_common/src/net/tun.rs b/src/connectivity/lowpan/lib/lowpan_driver_common/src/net/tun.rs
index d67105b..d30cc073 100644
--- a/src/connectivity/lowpan/lib/lowpan_driver_common/src/net/tun.rs
+++ b/src/connectivity/lowpan/lib/lowpan_driver_common/src/net/tun.rs
@@ -6,6 +6,7 @@
 use super::debug::*;
 use super::iface::*;
 use crate::prelude_internal::*;
+use std::collections::{HashMap, HashSet};
 
 use crate::spinel::Subnet;
 use anyhow::Error;
@@ -37,6 +38,7 @@
     control: fnetifext::admin::Control,
     control_sync: Mutex<fnetifadmin::ControlSynchronousProxy>,
     stack_sync: Mutex<fnetstack::StackSynchronousProxy>,
+    routes: Mutex<HashMap<fnet::Subnet, HashSet<std::net::Ipv6Addr>>>,
     mcast_socket: UdpSocket,
     id: u64,
 }
@@ -150,6 +152,7 @@
             control_sync,
             stack_sync,
             mcast_socket,
+            routes: Mutex::new(HashMap::new()),
             id,
         })
     }
@@ -252,50 +255,74 @@
             server_end,
         )?;
 
-        let mut forwarding_entry = fnetstack::ForwardingEntry {
-            subnet: fnetext::apply_subnet_mask(fnet::Subnet {
-                addr: fnetext::IpAddress(addr.addr.into()).into(),
-                prefix_len: addr.prefix_len,
-            }),
-            device_id: self.id,
-            next_hop: None,
-            metric: 0,
-        };
-        self.stack_sync
-            .lock()
-            .add_forwarding_entry(&mut forwarding_entry, zx::Time::INFINITE)?
-            .expect("add_forwarding_entry");
+        let subnet = fnetext::apply_subnet_mask(device_addr);
+
+        let mut routes = self.routes.lock();
 
         fx_log_info!("TunNetworkInterface: Successfully added address {:?}", addr);
+
+        if let Some(addresses) = routes.get_mut(&subnet) {
+            addresses.insert(addr.addr);
+        } else {
+            let mut forwarding_entry = fnetstack::ForwardingEntry {
+                subnet,
+                device_id: self.id,
+                next_hop: None,
+                metric: 0,
+            };
+            self.stack_sync
+                .lock()
+                .add_forwarding_entry(&mut forwarding_entry, zx::Time::INFINITE)?
+                .expect("add_forwarding_entry");
+            routes.insert(subnet, HashSet::from([addr.addr]));
+            fx_log_info!("TunNetworkInterface: Successfully added forwarding entry for {:?}", addr);
+        }
+
         Ok(())
     }
 
     fn remove_address(&self, addr: &Subnet) -> Result<(), Error> {
         fx_log_info!("TunNetworkInterface: Removing Address: {:?}", addr);
+
         let mut device_addr = fnet::Subnet {
             addr: fnetext::IpAddress(addr.addr.into()).into(),
             prefix_len: addr.prefix_len,
         };
-        let mut forwarding_entry = fnetstack::ForwardingEntry {
-            subnet: fnetext::apply_subnet_mask(fnet::Subnet {
-                addr: fnetext::IpAddress(addr.addr.into()).into(),
-                prefix_len: addr.prefix_len,
-            }),
-            device_id: self.id,
-            next_hop: None,
-            metric: 0,
-        };
-
-        self.stack_sync
-            .lock()
-            .del_forwarding_entry(&mut forwarding_entry, zx::Time::INFINITE)
-            .squash_result()?;
 
         self.control_sync
             .lock()
             .remove_address(&mut device_addr, zx::Time::INFINITE)?
             .expect("control_sync.remove_address");
+
+        let subnet = fnetext::apply_subnet_mask(device_addr);
+
         fx_log_info!("TunNetworkInterface: Successfully removed address {:?}", addr);
+
+        let mut routes = self.routes.lock();
+
+        if let Some(addresses) = routes.get_mut(&subnet) {
+            addresses.remove(&addr.addr);
+            if addresses.is_empty() {
+                routes.remove(&subnet);
+
+                let mut forwarding_entry = fnetstack::ForwardingEntry {
+                    subnet,
+                    device_id: self.id,
+                    next_hop: None,
+                    metric: 0,
+                };
+
+                self.stack_sync
+                    .lock()
+                    .del_forwarding_entry(&mut forwarding_entry, zx::Time::INFINITE)
+                    .squash_result()?;
+                fx_log_info!(
+                    "TunNetworkInterface: Successfully removed forwarding entry for {:?}",
+                    addr
+                );
+            }
+        }
+
         Ok(())
     }
 
diff --git a/src/connectivity/lowpan/lib/openthread_rust/BUILD.gn b/src/connectivity/lowpan/lib/openthread_rust/BUILD.gn
index 87b044f..75bdbf5 100644
--- a/src/connectivity/lowpan/lib/openthread_rust/BUILD.gn
+++ b/src/connectivity/lowpan/lib/openthread_rust/BUILD.gn
@@ -46,6 +46,7 @@
     "src/ot/link.rs",
     "src/ot/message.rs",
     "src/ot/mod.rs",
+    "src/ot/net_data.rs",
     "src/ot/otbox.rs",
     "src/ot/platform.rs",
     "src/ot/radio.rs",
diff --git a/src/connectivity/lowpan/lib/openthread_rust/src/ot/mod.rs b/src/connectivity/lowpan/lib/openthread_rust/src/ot/mod.rs
index 666d172..8bc3c8dc 100644
--- a/src/connectivity/lowpan/lib/openthread_rust/src/ot/mod.rs
+++ b/src/connectivity/lowpan/lib/openthread_rust/src/ot/mod.rs
@@ -133,6 +133,9 @@
 mod trel;
 pub use trel::*;
 
+mod net_data;
+pub use net_data::*;
+
 /// Trait implemented by all OpenThread instances.
 pub trait InstanceInterface:
     Ip6
@@ -152,6 +155,7 @@
     + Udp
     + Trel
     + BorderAgent
+    + NetData
 {
 }
 
diff --git a/src/connectivity/lowpan/lib/openthread_rust/src/ot/net_data.rs b/src/connectivity/lowpan/lib/openthread_rust/src/ot/net_data.rs
new file mode 100644
index 0000000..4fc9c46
--- /dev/null
+++ b/src/connectivity/lowpan/lib/openthread_rust/src/ot/net_data.rs
@@ -0,0 +1,69 @@
+// Copyright 2022 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use crate::prelude_internal::*;
+
+/// The maximum length of Thread network data, in bytes.
+pub const MAX_NET_DATA_LEN: usize = 255;
+
+/// Methods from the [OpenThread "NetData" Module][1].
+///
+/// [1]: https://openthread.io/reference/group/api-thread-general
+pub trait NetData {
+    /// Functional equivalent of [`otsys::otNetDataGet`](crate::otsys::otNetDataGet).
+    fn net_data_get<'a>(&self, stable: bool, data: &'a mut [u8]) -> Result<&'a [u8]>;
+
+    /// Same as [`net_data_get`], but returns the net data as a vector.
+    fn net_data_as_vec(&self, stable: bool) -> Result<Vec<u8>> {
+        let mut ret = vec![0; MAX_NET_DATA_LEN];
+
+        let len = self.net_data_get(stable, ret.as_mut_slice())?.len();
+
+        ret.truncate(len);
+
+        Ok(ret)
+    }
+
+    /// Functional equivalent of [`otsys::otNetDataGetVersion`](crate::otsys::otNetDataGetVersion).
+    fn net_data_get_version(&self) -> u8;
+
+    /// Functional equivalent of
+    /// [`otsys::otNetDataGetStableVersion`](crate::otsys::otNetDataGetStableVersion).
+    fn net_data_get_stable_version(&self) -> u8;
+}
+
+impl<T: NetData + Boxable> NetData for ot::Box<T> {
+    fn net_data_get<'a>(&self, stable: bool, data: &'a mut [u8]) -> Result<&'a [u8]> {
+        self.as_ref().net_data_get(stable, data)
+    }
+
+    fn net_data_get_version(&self) -> u8 {
+        self.as_ref().net_data_get_version()
+    }
+
+    fn net_data_get_stable_version(&self) -> u8 {
+        self.as_ref().net_data_get_version()
+    }
+}
+
+impl NetData for Instance {
+    fn net_data_get<'a>(&self, stable: bool, data: &'a mut [u8]) -> Result<&'a [u8]> {
+        let mut len: u8 = data.len().min(MAX_NET_DATA_LEN).try_into().unwrap();
+
+        Error::from(unsafe {
+            otNetDataGet(self.as_ot_ptr(), stable, data.as_mut_ptr(), (&mut len) as *mut u8)
+        })
+        .into_result()?;
+
+        Ok(&data[..(len as usize)])
+    }
+
+    fn net_data_get_version(&self) -> u8 {
+        unsafe { otNetDataGetVersion(self.as_ot_ptr()) }
+    }
+
+    fn net_data_get_stable_version(&self) -> u8 {
+        unsafe { otNetDataGetStableVersion(self.as_ot_ptr()) }
+    }
+}
diff --git a/src/connectivity/lowpan/lib/openthread_rust/src/ot/types/mod.rs b/src/connectivity/lowpan/lib/openthread_rust/src/ot/types/mod.rs
index c9dd57c..686acf8 100644
--- a/src/connectivity/lowpan/lib/openthread_rust/src/ot/types/mod.rs
+++ b/src/connectivity/lowpan/lib/openthread_rust/src/ot/types/mod.rs
@@ -81,3 +81,19 @@
 
 /// Unspecified power
 pub const DECIBELS_UNSPECIFIED: Decibels = -128;
+
+/// The largest child ID supported by OpenThread, 511.
+pub const MAX_CHILD_ID: u16 = 0x1FF;
+
+/// The bit offset to the router ID in an RLOC16.
+pub const ROUTER_ID_OFFSET: usize = 9;
+
+/// Extracts the child ID from an RLOC16.
+pub fn rloc16_to_child_id(rloc16: u16) -> u16 {
+    rloc16 & MAX_CHILD_ID
+}
+
+/// Extracts the router ID from an RLOC16.
+pub fn rloc16_to_router_id(rloc16: u16) -> u8 {
+    (rloc16 >> ROUTER_ID_OFFSET) as u8
+}
diff --git a/src/connectivity/network/dhcp/BUILD.gn b/src/connectivity/network/dhcp/BUILD.gn
index db62c4a..36bdd8d 100644
--- a/src/connectivity/network/dhcp/BUILD.gn
+++ b/src/connectivity/network/dhcp/BUILD.gn
@@ -118,14 +118,6 @@
   deps = [ ":bin" ]
 }
 
-fuchsia_package("dhcpd") {
-  deps = [ ":component" ]
-}
-
-group("dhcp") {
-  deps = [ ":dhcpd" ]
-}
-
 group("tests") {
   testonly = true
   deps = [ ":dhcp-tests" ]
diff --git a/src/connectivity/network/netstack3/core/src/context.rs b/src/connectivity/network/netstack3/core/src/context.rs
index 9daa82f..d9c8f7c 100644
--- a/src/connectivity/network/netstack3/core/src/context.rs
+++ b/src/connectivity/network/netstack3/core/src/context.rs
@@ -310,20 +310,7 @@
 /// optimized out entirely by the compiler.
 pub trait CounterContext {
     /// Increment the counter with the given key.
-    fn increment_counter(&self, key: &'static str);
-}
-
-// Temporary blanket impl until we switch over entirely to the traits defined in
-// this module.
-impl<NonSyncCtx: NonSyncContext> CounterContext for SyncCtx<NonSyncCtx> {
-    // TODO(rheacock): This is tricky because it's used in test only macro
-    // code so the compiler thinks `key` is unused. Remove this when this is
-    // no longer a problem.
-    #[allow(unused)]
-    fn increment_counter(&self, key: &'static str) {
-        #[cfg(test)]
-        self.state.test_counters.borrow_mut().increment(key);
-    }
+    fn increment_counter(&mut self, key: &'static str);
 }
 
 /// A context for emitting events.
@@ -992,20 +979,25 @@
     /// A dummy [`CounterContext`].
     #[derive(Default)]
     pub struct DummyCounterCtx {
-        counters: core::cell::RefCell<HashMap<&'static str, usize>>,
+        counters: HashMap<&'static str, usize>,
+    }
+
+    impl DummyCounterCtx {
+        pub(crate) fn get_counter_val(&self, key: &str) -> usize {
+            *self.counters.get(key).unwrap_or(&0)
+        }
     }
 
     impl CounterContext for DummyCounterCtx {
-        fn increment_counter(&self, key: &'static str) {
-            let mut counters = self.counters.borrow_mut();
-            let val = counters.entry(key).or_insert(0);
+        fn increment_counter(&mut self, key: &'static str) {
+            let val = self.counters.entry(key).or_insert(0);
             *val += 1;
         }
     }
 
-    impl<T: AsRef<DummyCounterCtx>> CounterContext for T {
-        fn increment_counter(&self, key: &'static str) {
-            self.as_ref().increment_counter(key);
+    impl<T: AsMut<DummyCounterCtx>> CounterContext for T {
+        fn increment_counter(&mut self, key: &'static str) {
+            self.as_mut().increment_counter(key);
         }
     }
 
@@ -1016,6 +1008,7 @@
         timers: DummyTimerCtx<TimerId>,
         events: DummyEventCtx<Event>,
         frames: DummyFrameCtx<DeviceId>,
+        counters: DummyCounterCtx,
         state: State,
     }
 
@@ -1026,6 +1019,7 @@
                 timers: DummyTimerCtx::default(),
                 events: DummyEventCtx::default(),
                 frames: DummyFrameCtx::default(),
+                counters: DummyCounterCtx::default(),
                 state: Default::default(),
             }
         }
@@ -1073,6 +1067,10 @@
         pub(crate) fn state_mut(&mut self) -> &mut State {
             &mut self.state
         }
+
+        pub(crate) fn counter_ctx(&self) -> &DummyCounterCtx {
+            &self.counters
+        }
     }
 
     impl<TimerId, Event: Debug, State> RngContext for DummyNonSyncCtx<TimerId, Event, State> {
@@ -1093,6 +1091,12 @@
         }
     }
 
+    impl<Id, Event: Debug, State> AsMut<DummyCounterCtx> for DummyNonSyncCtx<Id, Event, State> {
+        fn as_mut(&mut self) -> &mut DummyCounterCtx {
+            &mut self.counters
+        }
+    }
+
     impl<Id, Event: Debug, State> AsRef<DummyTimerCtx<Id>> for DummyNonSyncCtx<Id, Event, State> {
         fn as_ref(&self) -> &DummyTimerCtx<Id> {
             &self.timers
@@ -1183,25 +1187,16 @@
         }
     }
 
-    impl<S, Id, Meta, Event: Debug, DeviceId, NonSyncCtxState> AsRef<DummyCounterCtx>
-        for DummyCtx<S, Id, Meta, Event, DeviceId, NonSyncCtxState>
-    {
-        fn as_ref(&self) -> &DummyCounterCtx {
-            &self.sync_ctx.counters
-        }
-    }
-
     impl<S, Id, Meta, Event: Debug, DeviceId, NonSyncCtxState: Default>
         DummyCtx<S, Id, Meta, Event, DeviceId, NonSyncCtxState>
     {
         /// Constructs a `DummyCtx` with the given state and default
-        /// `DummyTimerCtx`, `DummyFrameCtx`, and `DummyCounterCtx`.
+        /// `DummyTimerCtx`, and `DummyFrameCtx`.
         pub(crate) fn with_state(state: S) -> Self {
             DummyCtx {
                 sync_ctx: DummySyncCtx {
                     state,
                     frames: DummyFrameCtx::default(),
-                    counters: DummyCounterCtx::default(),
                     _devices_marker: PhantomData,
                 },
                 non_sync_ctx: DummyNonSyncCtx::default(),
@@ -1218,7 +1213,6 @@
     pub(crate) struct DummySyncCtx<S, Meta, DeviceId> {
         state: S,
         frames: DummyFrameCtx<Meta>,
-        counters: DummyCounterCtx,
         _devices_marker: PhantomData<DeviceId>,
     }
 
@@ -1230,14 +1224,9 @@
 
     impl<S, Meta, DeviceId> DummySyncCtx<S, Meta, DeviceId> {
         /// Constructs a `DummySyncCtx` with the given state and default
-        /// `DummyTimerCtx`, `DummyFrameCtx`, and `DummyCounterCtx`.
+        /// `DummyTimerCtx`, and `DummyFrameCtx`.
         pub(crate) fn with_state(state: S) -> Self {
-            DummySyncCtx {
-                state,
-                frames: DummyFrameCtx::default(),
-                counters: DummyCounterCtx::default(),
-                _devices_marker: PhantomData,
-            }
+            DummySyncCtx { state, frames: DummyFrameCtx::default(), _devices_marker: PhantomData }
         }
 
         /// Get an immutable reference to the inner state.
@@ -1267,11 +1256,6 @@
         pub(crate) fn take_frames(&mut self) -> Vec<(Meta, Vec<u8>)> {
             self.frames.take_frames()
         }
-
-        /// Get the value of the named counter.
-        pub(crate) fn get_counter(&self, ctr: &str) -> usize {
-            self.counters.counters.borrow().get(ctr).cloned().unwrap_or(0)
-        }
     }
 
     impl<S, Meta, DeviceId> AsMut<DummyFrameCtx<Meta>> for DummySyncCtx<S, Meta, DeviceId> {
@@ -1280,12 +1264,6 @@
         }
     }
 
-    impl<S, Meta, DeviceId> AsRef<DummyCounterCtx> for DummySyncCtx<S, Meta, DeviceId> {
-        fn as_ref(&self) -> &DummyCounterCtx {
-            &self.counters
-        }
-    }
-
     impl<B: BufferMut, S, Id, Meta, Event: Debug, DeviceId, NonSyncCtxState>
         FrameContext<DummyNonSyncCtx<Id, Event, NonSyncCtxState>, B, Meta>
         for DummySyncCtx<S, Meta, DeviceId>
diff --git a/src/connectivity/network/netstack3/core/src/device/arp.rs b/src/connectivity/network/netstack3/core/src/device/arp.rs
index d820081..7896b0b 100644
--- a/src/connectivity/network/netstack3/core/src/device/arp.rs
+++ b/src/connectivity/network/netstack3/core/src/device/arp.rs
@@ -153,11 +153,15 @@
 
 /// The non-synchronized execution context for the ARP protocol.
 pub(crate) trait ArpNonSyncCtx<D: ArpDevice, P: PType, DeviceId>:
-    TimerContext<ArpTimerId<D, P, DeviceId>>
+    TimerContext<ArpTimerId<D, P, DeviceId>> + CounterContext
 {
 }
-impl<DeviceId, D: ArpDevice, P: PType, C: TimerContext<ArpTimerId<D, P, DeviceId>>>
-    ArpNonSyncCtx<D, P, DeviceId> for C
+impl<
+        DeviceId,
+        D: ArpDevice,
+        P: PType,
+        C: TimerContext<ArpTimerId<D, P, DeviceId>> + CounterContext,
+    > ArpNonSyncCtx<D, P, DeviceId> for C
 {
 }
 
@@ -166,7 +170,6 @@
     ArpDeviceIdContext<D>
     + StateContext<C, ArpState<D, P>, <Self as ArpDeviceIdContext<D>>::DeviceId>
     + FrameContext<C, EmptyBuf, ArpFrameMetadata<D, <Self as ArpDeviceIdContext<D>>::DeviceId>>
-    + CounterContext
 {
     /// Get a protocol address of this interface.
     ///
@@ -403,7 +406,7 @@
             packet.sender_protocol_address(),
         ));
 
-        sync_ctx.increment_counter("arp::rx_gratuitous_resolve");
+        ctx.increment_counter("arp::rx_gratuitous_resolve");
         // Notify device layer:
         sync_ctx.address_resolved(
             ctx,
@@ -479,7 +482,7 @@
             packet.sender_protocol_address(),
         ));
 
-        sync_ctx.increment_counter("arp::rx_resolve");
+        ctx.increment_counter("arp::rx_resolve");
         // Notify device layer:
         sync_ctx.address_resolved(
             ctx,
@@ -490,7 +493,7 @@
     }
     if addressed_to_me && packet.operation() == ArpOp::Request {
         let self_hw_addr = sync_ctx.get_hardware_addr(ctx, device_id);
-        sync_ctx.increment_counter("arp::rx_request");
+        ctx.increment_counter("arp::rx_request");
         // TODO(joshlf): Do something if send_frame returns an error?
         let _ = sync_ctx.send_frame(
             ctx,
diff --git a/src/connectivity/network/netstack3/core/src/device/ethernet.rs b/src/connectivity/network/netstack3/core/src/device/ethernet.rs
index b6e77ca..439ea86 100644
--- a/src/connectivity/network/netstack3/core/src/device/ethernet.rs
+++ b/src/connectivity/network/netstack3/core/src/device/ethernet.rs
@@ -378,7 +378,7 @@
     local_addr: SpecifiedAddr<A>,
     body: S,
 ) -> Result<(), S> {
-    sync_ctx.increment_counter("ethernet::send_ip_frame");
+    ctx.increment_counter("ethernet::send_ip_frame");
 
     trace!("ethernet::send_ip_frame: local_addr = {:?}; device = {:?}", local_addr, device_id);
 
@@ -1187,7 +1187,7 @@
             IpVersion::V4 => "receive_ipv4_packet",
             IpVersion::V6 => "receive_ipv6_packet",
         };
-        assert_eq!(get_counter_val(&mut sync_ctx, counter), expected_received);
+        assert_eq!(get_counter_val(&non_sync_ctx, counter), expected_received);
     }
 
     // TODO(https://fxbug.dev/102105): Unify these when #[ip_test] works with
@@ -1264,7 +1264,7 @@
             }
         }
 
-        assert_eq!(get_counter_val(&mut sync_ctx, "ethernet::send_ip_frame"), expected_sent);
+        assert_eq!(get_counter_val(&non_sync_ctx, "ethernet::send_ip_frame"), expected_sent);
     }
 
     // TODO(https://fxbug.dev/102105): Unify these when #[ip_test] works with
@@ -1480,14 +1480,14 @@
             .expect("error setting promiscuous mode");
         crate::device::receive_frame(&mut sync_ctx, &mut non_sync_ctx, device, buf.clone())
             .expect("error receiving frame");
-        assert_eq!(get_counter_val(&mut sync_ctx, dispatch_receive_ip_packet_name::<I>()), 1);
+        assert_eq!(get_counter_val(&non_sync_ctx, dispatch_receive_ip_packet_name::<I>()), 1);
 
         // Accept packet destined for this device if promiscuous mode is on.
         crate::device::set_promiscuous_mode(&mut sync_ctx, &mut non_sync_ctx, device, true)
             .expect("error setting promiscuous mode");
         crate::device::receive_frame(&mut sync_ctx, &mut non_sync_ctx, device, buf.clone())
             .expect("error receiving frame");
-        assert_eq!(get_counter_val(&mut sync_ctx, dispatch_receive_ip_packet_name::<I>()), 2);
+        assert_eq!(get_counter_val(&non_sync_ctx, dispatch_receive_ip_packet_name::<I>()), 2);
 
         let buf = Buf::new(Vec::new(), ..)
             .encapsulate(I::PacketBuilder::new(
@@ -1512,14 +1512,14 @@
             .expect("error setting promiscuous mode");
         crate::device::receive_frame(&mut sync_ctx, &mut non_sync_ctx, device, buf.clone())
             .expect("error receiving frame");
-        assert_eq!(get_counter_val(&mut sync_ctx, dispatch_receive_ip_packet_name::<I>()), 2);
+        assert_eq!(get_counter_val(&non_sync_ctx, dispatch_receive_ip_packet_name::<I>()), 2);
 
         // Accept packet not destined for this device if promiscuous mode is on.
         crate::device::set_promiscuous_mode(&mut sync_ctx, &mut non_sync_ctx, device, true)
             .expect("error setting promiscuous mode");
         crate::device::receive_frame(&mut sync_ctx, &mut non_sync_ctx, device, buf.clone())
             .expect("error receiving frame");
-        assert_eq!(get_counter_val(&mut sync_ctx, dispatch_receive_ip_packet_name::<I>()), 3);
+        assert_eq!(get_counter_val(&non_sync_ctx, dispatch_receive_ip_packet_name::<I>()), 3);
     }
 
     #[ip_test]
@@ -1627,7 +1627,7 @@
             buf,
         );
         assert_eq!(
-            get_counter_val(sync_ctx, dispatch_receive_ip_packet_name::<A::Version>()),
+            get_counter_val(non_sync_ctx, dispatch_receive_ip_packet_name::<A::Version>()),
             expected
         );
     }
@@ -1900,7 +1900,7 @@
         let addr_sub1 = AddrSubnet::new(ip1.get(), 64).unwrap();
         let addr_sub2 = AddrSubnet::new(ip2.get(), 64).unwrap();
 
-        assert_eq!(get_counter_val(&mut sync_ctx, "dispatch_receive_ip_packet"), 0);
+        assert_eq!(get_counter_val(&non_sync_ctx, "dispatch_receive_ip_packet"), 0);
 
         // Add ip1 to the device.
         //
diff --git a/src/connectivity/network/netstack3/core/src/device/mod.rs b/src/connectivity/network/netstack3/core/src/device/mod.rs
index 37c738e..8860cb3 100644
--- a/src/connectivity/network/netstack3/core/src/device/mod.rs
+++ b/src/connectivity/network/netstack3/core/src/device/mod.rs
@@ -79,8 +79,11 @@
 }
 
 /// The non-synchronized execution context for an IP device.
-pub(crate) trait IpLinkDeviceNonSyncContext<TimerId>: TimerContext<TimerId> {}
-impl<TimerId, C: TimerContext<TimerId>> IpLinkDeviceNonSyncContext<TimerId> for C {}
+pub(crate) trait IpLinkDeviceNonSyncContext<TimerId>:
+    TimerContext<TimerId> + CounterContext
+{
+}
+impl<TimerId, C: TimerContext<TimerId> + CounterContext> IpLinkDeviceNonSyncContext<TimerId> for C {}
 
 /// The context provided by the device layer to a particular IP device
 /// implementation.
@@ -89,7 +92,6 @@
 /// the inherited traits.
 pub(crate) trait IpLinkDeviceContext<D: LinkDevice, C: IpLinkDeviceNonSyncContext<TimerId>, TimerId>:
     DeviceIdContext<D>
-    + CounterContext
     + StateContext<C, IpLinkDeviceState<C::Instant, D::State>, Self::DeviceId>
     + FrameContext<C, EmptyBuf, Self::DeviceId>
     + FrameContext<C, Buf<Vec<u8>>, Self::DeviceId>
@@ -102,7 +104,6 @@
         TimerId,
         SC: NonTestCtxMarker
             + DeviceIdContext<D>
-            + CounterContext
             + StateContext<C, IpLinkDeviceState<C::Instant, D::State>, Self::DeviceId>
             + FrameContext<C, EmptyBuf, Self::DeviceId>
             + FrameContext<C, Buf<Vec<u8>>, Self::DeviceId>,
diff --git a/src/connectivity/network/netstack3/core/src/device/ndp.rs b/src/connectivity/network/netstack3/core/src/device/ndp.rs
index 07d94d2..3b025de 100644
--- a/src/connectivity/network/netstack3/core/src/device/ndp.rs
+++ b/src/connectivity/network/netstack3/core/src/device/ndp.rs
@@ -179,20 +179,17 @@
 
 /// The non-synchronized execution context for NDP.
 pub(crate) trait NdpNonSyncContext<D: LinkDevice, DeviceId>:
-    TimerContext<NdpTimerId<D, DeviceId>>
+    TimerContext<NdpTimerId<D, DeviceId>> + CounterContext
 {
 }
-impl<DeviceId, D: LinkDevice, C: TimerContext<NdpTimerId<D, DeviceId>>>
+impl<DeviceId, D: LinkDevice, C: TimerContext<NdpTimerId<D, DeviceId>> + CounterContext>
     NdpNonSyncContext<D, DeviceId> for C
 {
 }
 
 /// The execution context for an NDP device.
 pub(crate) trait NdpContext<D: LinkDevice, C: NdpNonSyncContext<D, Self::DeviceId>>:
-    Sized
-    + DeviceIdContext<D>
-    + CounterContext
-    + StateContext<C, NdpState<D>, <Self as DeviceIdContext<D>>::DeviceId>
+    Sized + DeviceIdContext<D> + StateContext<C, NdpState<D>, <Self as DeviceIdContext<D>>::DeviceId>
 {
     /// Returns the NDP retransmission timer configured on the device.
     // TODO(https://fxbug.dev/72378): Remove this method once NUD operates in
@@ -478,7 +475,7 @@
                     // unreachable state forever, remove the neighbor from the
                     // database:
                     ndp_state.neighbors.delete_neighbor_state(&neighbor_addr);
-                    sync_ctx.increment_counter("ndp::neighbor_solicitation_timer");
+                    ctx.increment_counter("ndp::neighbor_solicitation_timer");
 
                     sync_ctx.address_resolution_failed(ctx, id.device_id, &neighbor_addr);
                 }
@@ -934,7 +931,7 @@
             // TODO(ghanan): Make sure IP's hop limit is set to 255 as per RFC
             // 4861 section 6.1.2.
 
-            sync_ctx.increment_counter("ndp::rx_router_advertisement");
+            ctx.increment_counter("ndp::rx_router_advertisement");
 
             if sync_ctx.is_router_device(device_id) {
                 trace!("receive_ndp_packet: received NDP RA as a router, discarding NDP RA");
@@ -1059,7 +1056,7 @@
             //
             // TODO(https://fxbub.dev/99830): Move all of NDP handling
             // to IP.
-            sync_ctx.increment_counter("ndp::rx_neighbor_solicitation");
+            ctx.increment_counter("ndp::rx_neighbor_solicitation");
 
             // If we have a source link layer address option, we take it and
             // save to our cache.
@@ -1120,7 +1117,7 @@
                 }
             };
 
-            sync_ctx.increment_counter("ndp::rx_neighbor_advertisement");
+            ctx.increment_counter("ndp::rx_neighbor_advertisement");
 
             let ndp_state = sync_ctx.get_state_mut_with(device_id);
 
@@ -1507,7 +1504,7 @@
         }
         // Check that we hit the timeout after MAX_MULTICAST_SOLICIT.
         assert_eq!(
-            get_counter_val(&sync_ctx, "ndp::neighbor_solicitation_timer"),
+            get_counter_val(&non_sync_ctx, "ndp::neighbor_solicitation_timer"),
             1,
             "timeout counter at zero"
         );
@@ -1595,7 +1592,7 @@
         );
 
         assert_eq!(
-            get_counter_val(net.sync_ctx("remote"), "ndp::rx_neighbor_solicitation"),
+            get_counter_val(net.non_sync_ctx("remote"), "ndp::rx_neighbor_solicitation"),
             1,
             "remote received solicitation"
         );
@@ -1605,7 +1602,7 @@
         let _: StepResult = net.step(receive_frame_or_panic, handle_timer);
 
         assert_eq!(
-            get_counter_val(net.sync_ctx("local"), "ndp::rx_neighbor_advertisement"),
+            get_counter_val(net.non_sync_ctx("local"), "ndp::rx_neighbor_advertisement"),
             1,
             "local received advertisement"
         );
@@ -1649,7 +1646,7 @@
         });
         let _: StepResult = net.step(receive_frame_or_panic, handle_timer);
         assert_eq!(
-            get_counter_val(net.sync_ctx("remote"), "<IcmpIpTransportContext as BufferIpTransportContext<Ipv6>>::receive_ip_packet::echo_request"),
+            get_counter_val(net.non_sync_ctx("remote"), "<IcmpIpTransportContext as BufferIpTransportContext<Ipv6>>::receive_ip_packet::echo_request"),
             1
         );
 
@@ -2388,7 +2385,7 @@
             config.local_ip,
             icmpv6_packet.unwrap_ndp(),
         );
-        assert_eq!(get_counter_val(&mut sync_ctx, "ndp::rx_router_solicitation"), 0);
+        assert_eq!(get_counter_val(&non_sync_ctx, "ndp::rx_router_solicitation"), 0);
     }
 
     #[test]
@@ -2423,7 +2420,7 @@
             config.local_ip,
             icmpv6_packet.unwrap_ndp(),
         );
-        assert_eq!(get_counter_val(&mut sync_ctx, "ndp::rx_router_advertisement"), 0);
+        assert_eq!(get_counter_val(&non_sync_ctx, "ndp::rx_router_advertisement"), 0);
 
         // Test receiving NDP RA where source IP is a link local address (should
         // receive).
@@ -2441,7 +2438,7 @@
             config.local_ip,
             icmpv6_packet.unwrap_ndp(),
         );
-        assert_eq!(get_counter_val(&mut sync_ctx, "ndp::rx_router_advertisement"), 1);
+        assert_eq!(get_counter_val(&non_sync_ctx, "ndp::rx_router_advertisement"), 1);
     }
 
     #[test]
@@ -2549,7 +2546,7 @@
             config.local_ip,
             icmpv6_packet.unwrap_ndp(),
         );
-        assert_eq!(get_counter_val(&mut sync_ctx, "ndp::rx_router_advertisement"), 1);
+        assert_eq!(get_counter_val(&non_sync_ctx, "ndp::rx_router_advertisement"), 1);
         let ndp_state = StateContext::<_, NdpState<EthernetLinkDevice>, _>::get_state_mut_with(
             &mut sync_ctx,
             device_id.try_into().expect("expected ethernet ID"),
@@ -2579,7 +2576,7 @@
             config.local_ip,
             icmpv6_packet.unwrap_ndp(),
         );
-        assert_eq!(get_counter_val(&mut sync_ctx, "ndp::rx_router_advertisement"), 2);
+        assert_eq!(get_counter_val(&non_sync_ctx, "ndp::rx_router_advertisement"), 2);
         let ndp_state = StateContext::<_, NdpState<EthernetLinkDevice>, _>::get_state_mut_with(
             &mut sync_ctx,
             device_id.try_into().expect("expected ethernet ID"),
@@ -2631,7 +2628,7 @@
             config.local_ip,
             icmpv6_packet.unwrap_ndp(),
         );
-        assert_eq!(get_counter_val(&mut sync_ctx, "ndp::rx_router_advertisement"), 1);
+        assert_eq!(get_counter_val(&non_sync_ctx, "ndp::rx_router_advertisement"), 1);
         assert_eq!(crate::ip::IpDeviceContext::<Ipv6, _>::get_mtu(&sync_ctx, device), hw_mtu);
 
         // Receive a new RA with an invalid MTU option (value is lower than IPv6
@@ -2649,7 +2646,7 @@
             config.local_ip,
             icmpv6_packet.unwrap_ndp(),
         );
-        assert_eq!(get_counter_val(&mut sync_ctx, "ndp::rx_router_advertisement"), 2);
+        assert_eq!(get_counter_val(&non_sync_ctx, "ndp::rx_router_advertisement"), 2);
         assert_eq!(crate::ip::IpDeviceContext::<Ipv6, _>::get_mtu(&sync_ctx, device), hw_mtu);
 
         // Receive a new RA with a valid MTU option (value is exactly IPv6 min
@@ -2667,7 +2664,7 @@
             config.local_ip,
             icmpv6_packet.unwrap_ndp(),
         );
-        assert_eq!(get_counter_val(&mut sync_ctx, "ndp::rx_router_advertisement"), 3);
+        assert_eq!(get_counter_val(&non_sync_ctx, "ndp::rx_router_advertisement"), 3);
         assert_eq!(
             crate::ip::IpDeviceContext::<Ipv6, _>::get_mtu(&sync_ctx, device),
             Ipv6::MINIMUM_LINK_MTU.into()
@@ -3900,7 +3897,7 @@
         );
 
         // Verify that `conflicted_addr` was generated and rejected.
-        assert_eq!(get_counter_val(&mut sync_ctx, "generated_slaac_addr_exists"), 1);
+        assert_eq!(get_counter_val(&non_sync_ctx, "generated_slaac_addr_exists"), 1);
 
         // Should have gotten a new temporary IP.
         let temporary_slaac_addresses =
diff --git a/src/connectivity/network/netstack3/core/src/ip/device/integration.rs b/src/connectivity/network/netstack3/core/src/ip/device/integration.rs
index 6489aba..9320d2c 100644
--- a/src/connectivity/network/netstack3/core/src/ip/device/integration.rs
+++ b/src/connectivity/network/netstack3/core/src/ip/device/integration.rs
@@ -21,7 +21,7 @@
 };
 
 use crate::{
-    context::{CounterContext, FrameContext},
+    context::FrameContext,
     error::ExistsError,
     ip::{
         self,
@@ -68,7 +68,7 @@
 
 impl<
         C: IpDeviceNonSyncContext<Ipv6, SC::DeviceId>,
-        SC: device::Ipv6DeviceContext<C> + GmpHandler<Ipv6, C> + DadHandler<C> + CounterContext,
+        SC: device::Ipv6DeviceContext<C> + GmpHandler<Ipv6, C> + DadHandler<C>,
     > SlaacStateContext<C> for SC
 {
     fn get_config(&self, device_id: Self::DeviceId) -> SlaacConfiguration {
diff --git a/src/connectivity/network/netstack3/core/src/ip/device/mod.rs b/src/connectivity/network/netstack3/core/src/ip/device/mod.rs
index 3d07889..ce72122 100644
--- a/src/connectivity/network/netstack3/core/src/ip/device/mod.rs
+++ b/src/connectivity/network/netstack3/core/src/ip/device/mod.rs
@@ -24,7 +24,9 @@
 use packet_formats::utils::NonZeroDuration;
 
 use crate::{
-    context::{EventContext, InstantContext, RngContext, TimerContext, TimerHandler},
+    context::{
+        CounterContext, EventContext, InstantContext, RngContext, TimerContext, TimerHandler,
+    },
     error::{ExistsError, NotFoundError},
     ip::{
         device::{
@@ -253,13 +255,17 @@
 pub(crate) trait IpDeviceNonSyncContext<
     I: IpDeviceIpExt<<Self as InstantContext>::Instant, DeviceId>,
     DeviceId,
->: RngContext + TimerContext<I::Timer> + EventContext<IpDeviceEvent<DeviceId, I>>
+>:
+    RngContext + TimerContext<I::Timer> + EventContext<IpDeviceEvent<DeviceId, I>> + CounterContext
 {
 }
 impl<
         DeviceId,
         I: IpDeviceIpExt<<C as InstantContext>::Instant, DeviceId>,
-        C: RngContext + TimerContext<I::Timer> + EventContext<IpDeviceEvent<DeviceId, I>>,
+        C: RngContext
+            + TimerContext<I::Timer>
+            + EventContext<IpDeviceEvent<DeviceId, I>>
+            + CounterContext,
     > IpDeviceNonSyncContext<I, DeviceId> for C
 {
 }
diff --git a/src/connectivity/network/netstack3/core/src/ip/device/slaac.rs b/src/connectivity/network/netstack3/core/src/ip/device/slaac.rs
index 27866f8..75096c72 100644
--- a/src/connectivity/network/netstack3/core/src/ip/device/slaac.rs
+++ b/src/connectivity/network/netstack3/core/src/ip/device/slaac.rs
@@ -194,24 +194,18 @@
 
 /// The non-synchronized execution context for SLAAC.
 pub(super) trait SlaacNonSyncContext<DeviceId>:
-    RngContext + TimerContext<SlaacTimerId<DeviceId>>
+    RngContext + TimerContext<SlaacTimerId<DeviceId>> + CounterContext
 {
 }
-impl<DeviceId, C: RngContext + TimerContext<SlaacTimerId<DeviceId>>> SlaacNonSyncContext<DeviceId>
-    for C
+impl<DeviceId, C: RngContext + TimerContext<SlaacTimerId<DeviceId>> + CounterContext>
+    SlaacNonSyncContext<DeviceId> for C
 {
 }
 
 /// The execution context for SLAAC.
-trait SlaacContext<C: SlaacNonSyncContext<Self::DeviceId>>:
-    SlaacStateContext<C> + CounterContext
-{
-}
+trait SlaacContext<C: SlaacNonSyncContext<Self::DeviceId>>: SlaacStateContext<C> {}
 
-impl<C: SlaacNonSyncContext<SC::DeviceId>, SC: SlaacStateContext<C> + CounterContext>
-    SlaacContext<C> for SC
-{
-}
+impl<C: SlaacNonSyncContext<SC::DeviceId>, SC: SlaacStateContext<C>> SlaacContext<C> for SC {}
 
 /// An implementation of SLAAC.
 pub(crate) trait SlaacHandler<C: InstantContext>: IpDeviceIdContext<Ipv6> {
@@ -1343,7 +1337,7 @@
                 // Try the next address.
                 //
                 // TODO(https://fxbug.dev/100003): Limit number of attempts.
-                sync_ctx.increment_counter("generated_slaac_addr_exists");
+                ctx.increment_counter("generated_slaac_addr_exists");
             }
             Ok(()) => break address,
         }
diff --git a/src/connectivity/network/netstack3/core/src/ip/icmp.rs b/src/connectivity/network/netstack3/core/src/ip/icmp.rs
index efdd77a3..56ab185 100644
--- a/src/connectivity/network/netstack3/core/src/ip/icmp.rs
+++ b/src/connectivity/network/netstack3/core/src/ip/icmp.rs
@@ -598,8 +598,11 @@
 }
 
 /// The non-synchronized execution context shared by both ICMPv4 and ICMPv6.
-pub(crate) trait IcmpNonSyncCtx<I: IcmpIpExt>: InstantContext + IcmpContext<I> {}
-impl<I: IcmpIpExt, C: InstantContext + IcmpContext<I>> IcmpNonSyncCtx<I> for C {}
+pub(crate) trait IcmpNonSyncCtx<I: IcmpIpExt>:
+    InstantContext + IcmpContext<I> + CounterContext
+{
+}
+impl<I: IcmpIpExt, C: InstantContext + IcmpContext<I> + CounterContext> IcmpNonSyncCtx<I> for C {}
 
 /// The execution context shared by ICMP(v4) and ICMPv6 for the internal
 /// operations of the IP stack.
@@ -609,7 +612,6 @@
 pub(crate) trait InnerIcmpContext<I: IcmpIpExt + IpExt, C: IcmpNonSyncCtx<I>>:
     IpSocketHandler<I, C>
     + IpDeviceIdContext<I>
-    + CounterContext
     + StateContext<
         C,
         IcmpState<I::Addr, C::Instant, IpSock<I, <Self as IpDeviceIdContext<I>>::DeviceId>>,
@@ -851,7 +853,7 @@
         let instant_ctx = crate::context::new_cached_instant_context($ctx);
         let state: &mut IcmpState<_, _, _> = $sync_ctx.get_state_mut();
         if state.error_send_bucket.try_take(&instant_ctx) {
-            $sync_ctx.increment_counter($counter_name);
+            $ctx.increment_counter($counter_name);
             $e
         } else {
             trace!("ip::icmp::try_send_error!: dropping rate-limited ICMP error message");
@@ -877,7 +879,7 @@
         mut original_body: &[u8],
         err: I::ErrorCode,
     ) {
-        sync_ctx.increment_counter("IcmpIpTransportContext::receive_icmp_error");
+        ctx.increment_counter("IcmpIpTransportContext::receive_icmp_error");
         trace!("IcmpIpTransportContext::receive_icmp_error({:?})", err);
 
         let echo_request = if let Ok(echo_request) =
@@ -905,7 +907,7 @@
             icmp_id: id,
         }) {
             let seq = echo_request.message().seq();
-            sync_ctx.increment_counter("IcmpContext::receive_icmp_error");
+            ctx.increment_counter("IcmpContext::receive_icmp_error");
             IcmpContext::receive_icmp_error(ctx, IcmpConnId::new(conn), seq, err);
         } else {
             trace!("IcmpIpTransportContext::receive_icmp_error: Got ICMP error message for nonexistent ICMP echo socket; either the socket responsible has since been removed, or the error message was sent in error or corrupted");
@@ -940,7 +942,7 @@
 
         match packet {
             Icmpv4Packet::EchoRequest(echo_request) => {
-                sync_ctx.increment_counter("<IcmpIpTransportContext as BufferIpTransportContext<Ipv4>>::receive_ip_packet::echo_request");
+                ctx.increment_counter("<IcmpIpTransportContext as BufferIpTransportContext<Ipv4>>::receive_ip_packet::echo_request");
 
                 if let Some(src_ip) = SpecifiedAddr::new(src_ip) {
                     let req = *echo_request.message();
@@ -961,14 +963,14 @@
                 }
             }
             Icmpv4Packet::EchoReply(echo_reply) => {
-                sync_ctx.increment_counter("<IcmpIpTransportContext as BufferIpTransportContext<Ipv4>>::receive_ip_packet::echo_reply");
+                ctx.increment_counter("<IcmpIpTransportContext as BufferIpTransportContext<Ipv4>>::receive_ip_packet::echo_reply");
                 trace!("<IcmpIpTransportContext as BufferIpTransportContext<Ipv4>>::receive_ip_packet: Received an EchoReply message");
                 let id = echo_reply.message().id();
                 let seq = echo_reply.message().seq();
                 receive_icmp_echo_reply(sync_ctx,ctx, src_ip, dst_ip, id, seq, buffer);
             }
             Icmpv4Packet::TimestampRequest(timestamp_request) => {
-                sync_ctx.increment_counter("<IcmpIpTransportContext as BufferIpTransportContext<Ipv4>>::receive_ip_packet::timestamp_request");
+                ctx.increment_counter("<IcmpIpTransportContext as BufferIpTransportContext<Ipv4>>::receive_ip_packet::timestamp_request");
                 if let Some(src_ip) = SpecifiedAddr::new(src_ip) {
                     if StateContext::<_, Icmpv4State<_, _>>::get_state(sync_ctx).send_timestamp_reply {
                         trace!("<IcmpIpTransportContext as BufferIpTransportContext<Ipv4>>::receive_ip_packet: Responding to Timestamp Request message");
@@ -1025,7 +1027,7 @@
                 debug!("<IcmpIpTransportContext as BufferIpTransportContext<Ipv4>>::receive_ip_packet: Received unsolicited Timestamp Reply message");
             }
             Icmpv4Packet::DestUnreachable(dest_unreachable) => {
-                sync_ctx.increment_counter("<IcmpIpTransportContext as BufferIpTransportContext<Ipv4>>::receive_ip_packet::dest_unreachable");
+                ctx.increment_counter("<IcmpIpTransportContext as BufferIpTransportContext<Ipv4>>::receive_ip_packet::dest_unreachable");
                 trace!("<IcmpIpTransportContext as BufferIpTransportContext<Ipv4>>::receive_ip_packet: Received a Destination Unreachable message");
 
                 if dest_unreachable.code() == Icmpv4DestUnreachableCode::FragmentationRequired {
@@ -1097,7 +1099,7 @@
                 );
             }
             Icmpv4Packet::TimeExceeded(time_exceeded) => {
-                sync_ctx.increment_counter("<IcmpIpTransportContext as BufferIpTransportContext<Ipv4>>::receive_ip_packet::time_exceeded");
+                ctx.increment_counter("<IcmpIpTransportContext as BufferIpTransportContext<Ipv4>>::receive_ip_packet::time_exceeded");
                 trace!("<IcmpIpTransportContext as BufferIpTransportContext<Ipv4>>::receive_ip_packet: Received a Time Exceeded message");
 
                 receive_icmpv4_error(
@@ -1110,7 +1112,7 @@
             }
             Icmpv4Packet::Redirect(_) => log_unimplemented!((), "<IcmpIpTransportContext as BufferIpTransportContext<Ipv4>>::receive_ip_packet::redirect"),
             Icmpv4Packet::ParameterProblem(parameter_problem) => {
-                sync_ctx.increment_counter("<IcmpIpTransportContext as BufferIpTransportContext<Ipv4>>::receive_ip_packet::parameter_problem");
+                ctx.increment_counter("<IcmpIpTransportContext as BufferIpTransportContext<Ipv4>>::receive_ip_packet::parameter_problem");
                 trace!("<IcmpIpTransportContext as BufferIpTransportContext<Ipv4>>::receive_ip_packet: Received a Parameter Problem message");
 
                 receive_icmpv4_error(
@@ -1359,7 +1361,7 @@
                 Ipv6SourceAddr::Unspecified => return,
             };
 
-            sync_ctx.increment_counter("ndp::rx_router_advertisement");
+            ctx.increment_counter("ndp::rx_router_advertisement");
 
             // As per RFC 4861 section 6.3.4,
             //   The RetransTimer variable SHOULD be copied from the Retrans
@@ -1498,7 +1500,7 @@
 
         match packet {
             Icmpv6Packet::EchoRequest(echo_request) => {
-                sync_ctx.increment_counter("<IcmpIpTransportContext as BufferIpTransportContext<Ipv6>>::receive_ip_packet::echo_request");
+                ctx.increment_counter("<IcmpIpTransportContext as BufferIpTransportContext<Ipv6>>::receive_ip_packet::echo_request");
 
                 if let Ipv6SourceAddr::Unicast(src_ip) = src_ip {
                     let req = *echo_request.message();
@@ -1526,7 +1528,7 @@
                 }
             }
             Icmpv6Packet::EchoReply(echo_reply) => {
-                sync_ctx.increment_counter("<IcmpIpTransportContext as BufferIpTransportContext<Ipv6>>::receive_ip_packet::echo_reply");
+                ctx.increment_counter("<IcmpIpTransportContext as BufferIpTransportContext<Ipv6>>::receive_ip_packet::echo_reply");
                 trace!("<IcmpIpTransportContext as BufferIpTransportContext<Ipv6>>::receive_ip_packet: Received an EchoReply message");
                 // We don't allow creating echo sockets connected to the
                 // unspecified address, so it's OK to bail early here if the
@@ -1541,7 +1543,7 @@
                 receive_ndp_packet(sync_ctx, ctx, device, src_ip, dst_ip, packet)
             }
             Icmpv6Packet::PacketTooBig(packet_too_big) => {
-                sync_ctx.increment_counter("<IcmpIpTransportContext as BufferIpTransportContext<Ipv6>>::receive_ip_packet::packet_too_big");
+                ctx.increment_counter("<IcmpIpTransportContext as BufferIpTransportContext<Ipv6>>::receive_ip_packet::packet_too_big");
                 trace!("<IcmpIpTransportContext as BufferIpTransportContext<Ipv6>>::receive_ip_packet: Received a Packet Too Big message");
                 if let Ipv6SourceAddr::Unicast(src_ip) = src_ip {
                     // We are updating the path MTU from the destination address
@@ -1613,8 +1615,8 @@
 fn send_icmp_reply<
     I: crate::ip::IpExt,
     B: BufferMut,
-    C,
-    SC: BufferIpSocketHandler<I, C, B> + IpDeviceIdContext<I> + CounterContext,
+    C: BufferIcmpNonSyncCtx<I, B>,
+    SC: BufferIpSocketHandler<I, C, B> + IpDeviceIdContext<I>,
     S: Serializer<Buffer = B>,
     F: FnOnce(SpecifiedAddr<I::Addr>) -> S,
 >(
@@ -1626,7 +1628,7 @@
     get_body_from_src_ip: F,
 ) -> Result<(), S> {
     trace!("send_icmp_reply({:?}, {}, {})", device, original_src_ip, original_dst_ip);
-    sync_ctx.increment_counter("send_icmp_reply");
+    ctx.increment_counter("send_icmp_reply");
     sync_ctx
         .send_oneshot_ip_packet(
             ctx,
@@ -1765,7 +1767,7 @@
     original_packet: B,
     header_len: usize,
 ) {
-    sync_ctx.increment_counter("send_icmpv4_protocol_unreachable");
+    ctx.increment_counter("send_icmpv4_protocol_unreachable");
 
     send_icmpv4_dest_unreachable(
         sync_ctx,
@@ -1809,7 +1811,7 @@
     original_packet: B,
     header_len: usize,
 ) {
-    sync_ctx.increment_counter("send_icmpv6_protocol_unreachable");
+    ctx.increment_counter("send_icmpv6_protocol_unreachable");
 
     send_icmpv6_parameter_problem(
         sync_ctx,
@@ -1863,7 +1865,7 @@
     original_packet: B,
     header_len: usize,
 ) {
-    sync_ctx.increment_counter("send_icmpv4_port_unreachable");
+    ctx.increment_counter("send_icmpv4_port_unreachable");
 
     send_icmpv4_dest_unreachable(
         sync_ctx,
@@ -1905,7 +1907,7 @@
     dst_ip: SpecifiedAddr<Ipv6Addr>,
     original_packet: B,
 ) {
-    sync_ctx.increment_counter("send_icmpv6_port_unreachable");
+    ctx.increment_counter("send_icmpv6_port_unreachable");
 
     send_icmpv6_dest_unreachable(
         sync_ctx,
@@ -1946,7 +1948,7 @@
     header_len: usize,
     fragment_type: Ipv4FragmentType,
 ) {
-    sync_ctx.increment_counter("send_icmpv4_net_unreachable");
+    ctx.increment_counter("send_icmpv4_net_unreachable");
 
     // Check whether we MUST NOT send an ICMP error message
     // because the original packet was itself an ICMP error message.
@@ -1993,7 +1995,7 @@
     original_packet: B,
     header_len: usize,
 ) {
-    sync_ctx.increment_counter("send_icmpv6_net_unreachable");
+    ctx.increment_counter("send_icmpv6_net_unreachable");
 
     // Check whether we MUST NOT send an ICMP error message
     // because the original packet was itself an ICMP error message.
@@ -2040,7 +2042,7 @@
     header_len: usize,
     fragment_type: Ipv4FragmentType,
 ) {
-    sync_ctx.increment_counter("send_icmpv4_ttl_expired");
+    ctx.increment_counter("send_icmpv4_ttl_expired");
 
     // Check whether we MUST NOT send an ICMP error message because the original
     // packet was itself an ICMP error message.
@@ -2088,7 +2090,7 @@
     original_packet: B,
     header_len: usize,
 ) {
-    sync_ctx.increment_counter("send_icmpv6_ttl_expired");
+    ctx.increment_counter("send_icmpv6_ttl_expired");
 
     // Check whether we MUST NOT send an ICMP error message because the
     // original packet was itself an ICMP error message.
@@ -2135,7 +2137,7 @@
     original_packet: B,
     header_len: usize,
 ) {
-    sync_ctx.increment_counter("send_icmpv6_packet_too_big");
+    ctx.increment_counter("send_icmpv6_packet_too_big");
     // Check whether we MUST NOT send an ICMP error message because the
     // original packet was itself an ICMP error message.
     if is_icmp_error_message::<Ipv6>(proto, &original_packet.as_ref()[header_len..]) {
@@ -2191,7 +2193,7 @@
     header_len: usize,
     fragment_type: Ipv4FragmentType,
 ) {
-    sync_ctx.increment_counter("send_icmpv4_parameter_problem");
+    ctx.increment_counter("send_icmpv4_parameter_problem");
 
     send_icmpv4_error_message(
         sync_ctx,
@@ -2240,7 +2242,7 @@
     // a multicast address.
     assert!(!allow_dst_multicast || code == Icmpv6ParameterProblemCode::UnrecognizedIpv6Option);
 
-    sync_ctx.increment_counter("send_icmpv6_parameter_problem");
+    ctx.increment_counter("send_icmpv6_parameter_problem");
 
     send_icmpv6_error_message(
         sync_ctx,
@@ -2272,7 +2274,7 @@
     header_len: usize,
     fragment_type: Ipv4FragmentType,
 ) {
-    sync_ctx.increment_counter("send_icmpv4_dest_unreachable");
+    ctx.increment_counter("send_icmpv4_dest_unreachable");
     send_icmpv4_error_message(
         sync_ctx,
         ctx,
@@ -3030,7 +3032,7 @@
         }
 
         for counter in assert_counters {
-            assert!(get_counter_val(&sync_ctx, counter) > 0, "counter at zero: {}", counter);
+            assert!(get_counter_val(&non_sync_ctx, counter) > 0, "counter at zero: {}", counter);
         }
 
         if let Some((expect_message, expect_code)) = expect_message_code {
@@ -3650,14 +3652,14 @@
 
         assert_eq!(
             get_counter_val(
-                net.sync_ctx(LOCAL_CTX_NAME),
+                net.non_sync_ctx(LOCAL_CTX_NAME),
                 &format!("{}::echo_reply", recv_icmp_packet_name)
             ),
             1
         );
         assert_eq!(
             get_counter_val(
-                &net.context(ctx_name_receiving_req).sync_ctx,
+                net.non_sync_ctx(ctx_name_receiving_req),
                 &format!("{}::echo_request", recv_icmp_packet_name)
             ),
             1
@@ -3897,7 +3899,7 @@
                     original_body: &[u8],
                     err: <$ip as IcmpIpExt>::ErrorCode,
                 ) {
-                    self.increment_counter("InnerIcmpContext::receive_icmp_error");
+                    ctx.increment_counter("InnerIcmpContext::receive_icmp_error");
                     self.get_mut().inner.receive_icmp_error.push(err);
                     if original_proto == <$ip as packet_formats::icmp::IcmpIpExt>::ICMP_IP_PROTO {
                         <IcmpIpTransportContext as IpTransportContext<$ip, _, _>>::receive_icmp_error(
@@ -4122,7 +4124,12 @@
             .unwrap();
 
             for (ctr, count) in assert_counters {
-                assert_eq!(sync_ctx.get_counter(ctr), *count, "wrong count for counter {}", ctr);
+                assert_eq!(
+                    non_sync_ctx.counter_ctx().get_counter_val(ctr),
+                    *count,
+                    "wrong count for counter {}",
+                    ctr
+                );
             }
             f(&ctx);
         }
@@ -4438,7 +4445,12 @@
             .unwrap();
 
             for (ctr, count) in assert_counters {
-                assert_eq!(sync_ctx.get_counter(ctr), *count, "wrong count for counter {}", ctr);
+                assert_eq!(
+                    non_sync_ctx.counter_ctx().get_counter_val(ctr),
+                    *count,
+                    "wrong count for counter {}",
+                    ctr
+                );
             }
             f(&ctx);
         }
@@ -4846,25 +4858,34 @@
 
             for i in 0..ERRORS_PER_SECOND {
                 send(&mut ctx);
-                assert_eq!(ctx.sync_ctx.get_counter(counter_str), i as usize + 1);
+                assert_eq!(
+                    ctx.non_sync_ctx.counter_ctx().get_counter_val(counter_str),
+                    i as usize + 1
+                );
             }
 
-            assert_eq!(ctx.sync_ctx.get_counter(counter_str), ERRORS_PER_SECOND as usize);
+            assert_eq!(
+                ctx.non_sync_ctx.counter_ctx().get_counter_val(counter_str),
+                ERRORS_PER_SECOND as usize
+            );
             send(&mut ctx);
-            assert_eq!(ctx.sync_ctx.get_counter(counter_str), ERRORS_PER_SECOND as usize);
+            assert_eq!(
+                ctx.non_sync_ctx.counter_ctx().get_counter_val(counter_str),
+                ERRORS_PER_SECOND as usize
+            );
 
             // Test that, if we set a rate of 0, we are not able to send any
             // error messages regardless of how much time has elapsed.
 
             let mut ctx = with_errors_per_second(0);
             send(&mut ctx);
-            assert_eq!(ctx.sync_ctx.get_counter(counter_str), 0);
+            assert_eq!(ctx.non_sync_ctx.counter_ctx().get_counter_val(counter_str), 0);
             ctx.non_sync_ctx.sleep_skip_timers(Duration::from_secs(1));
             send(&mut ctx);
-            assert_eq!(ctx.sync_ctx.get_counter(counter_str), 0);
+            assert_eq!(ctx.non_sync_ctx.counter_ctx().get_counter_val(counter_str), 0);
             ctx.non_sync_ctx.sleep_skip_timers(Duration::from_secs(1));
             send(&mut ctx);
-            assert_eq!(ctx.sync_ctx.get_counter(counter_str), 0);
+            assert_eq!(ctx.non_sync_ctx.counter_ctx().get_counter_val(counter_str), 0);
         }
 
         fn with_errors_per_second_v4(errors_per_second: u64) -> Dummyv4Ctx {
diff --git a/src/connectivity/network/netstack3/core/src/ip/mod.rs b/src/connectivity/network/netstack3/core/src/ip/mod.rs
index 78dee9b..5bed708 100644
--- a/src/connectivity/network/netstack3/core/src/ip/mod.rs
+++ b/src/connectivity/network/netstack3/core/src/ip/mod.rs
@@ -406,7 +406,7 @@
 pub(crate) trait IpStateContext<
     I: IpLayerStateIpExt<Instant, Self::DeviceId>,
     Instant: crate::Instant,
->: IpDeviceIdContext<I> + CounterContext
+>: IpDeviceIdContext<I>
 {
     /// Gets immutable access to the IP layer state.
     fn get_ip_layer_state(&self) -> &I::State;
@@ -470,11 +470,14 @@
 
 /// The non-synchronized execution context for the IP layer.
 pub(crate) trait IpLayerNonSyncContext<I: Ip, DeviceId>:
-    InstantContext + EventContext<IpLayerEvent<DeviceId, I>>
+    InstantContext + EventContext<IpLayerEvent<DeviceId, I>> + CounterContext
 {
 }
-impl<I: Ip, DeviceId, C: InstantContext + EventContext<IpLayerEvent<DeviceId, I>>>
-    IpLayerNonSyncContext<I, DeviceId> for C
+impl<
+        I: Ip,
+        DeviceId,
+        C: InstantContext + EventContext<IpLayerEvent<DeviceId, I>> + CounterContext,
+    > IpLayerNonSyncContext<I, DeviceId> for C
 {
 }
 
@@ -664,7 +667,7 @@
 
 /// The transport context provided to the IP layer requiring a buffer type.
 pub(crate) trait BufferTransportContext<I: IpLayerIpExt, C, B: BufferMut>:
-    IpDeviceIdContext<I> + CounterContext
+    IpDeviceIdContext<I>
 {
     /// Dispatches a received incoming IP packet to the appropriate protocol.
     fn dispatch_receive_ip_packet(
@@ -720,10 +723,8 @@
 }
 
 impl<
-        C,
-        SC: BufferIpTransportLayerContext<Ipv4, C, B>
-            + IgmpPacketHandler<C, SC::DeviceId, B>
-            + CounterContext,
+        C: IpLayerNonSyncContext<Ipv4, SC::DeviceId>,
+        SC: BufferIpTransportLayerContext<Ipv4, C, B> + IgmpPacketHandler<C, SC::DeviceId, B>,
         B: BufferMut,
     > BufferTransportContext<Ipv4, C, B> for SC
 where
@@ -780,8 +781,11 @@
     }
 }
 
-impl<C, SC: BufferIpTransportLayerContext<Ipv6, C, B> + CounterContext, B: BufferMut>
-    BufferTransportContext<Ipv6, C, B> for SC
+impl<
+        C: IpLayerNonSyncContext<Ipv6, SC::DeviceId>,
+        SC: BufferIpTransportLayerContext<Ipv6, C, B>,
+        B: BufferMut,
+    > BufferTransportContext<Ipv6, C, B> for SC
 where
     IcmpIpTransportContext: BufferIpTransportContext<Ipv6, C, SC, B>,
 {
@@ -1108,7 +1112,7 @@
     body: B,
     parse_metadata: Option<ParseMetadata>,
 ) {
-    sync_ctx.increment_counter("dispatch_receive_ipv4_packet");
+    ctx.increment_counter("dispatch_receive_ipv4_packet");
 
     let (mut body, err) =
         match sync_ctx.dispatch_receive_ip_packet(ctx, device, src_ip, dst_ip, proto, body) {
@@ -1188,7 +1192,7 @@
     // parse_metadata argument which corresponds to a single extension
     // header rather than all of the IPv6 headers.
 
-    sync_ctx.increment_counter("dispatch_receive_ipv6_packet");
+    ctx.increment_counter("dispatch_receive_ipv6_packet");
 
     let (mut body, err) =
         match sync_ctx.dispatch_receive_ip_packet(ctx, device, src_ip, dst_ip, proto, body) {
@@ -1412,7 +1416,7 @@
         return;
     }
 
-    increment_counter!(sync_ctx, "receive_ipv4_packet");
+    ctx.increment_counter("receive_ipv4_packet");
     trace!("receive_ip_packet({})", device);
 
     let mut packet: Ipv4Packet<_> = match try_parse_ip_packet!(buffer) {
@@ -1626,7 +1630,7 @@
         return;
     }
 
-    increment_counter!(sync_ctx, "receive_ipv6_packet");
+    ctx.increment_counter("receive_ipv6_packet");
     trace!("receive_ipv6_packet({})", device);
 
     let mut packet: Ipv6Packet<_> = match try_parse_ip_packet!(buffer) {
@@ -1689,7 +1693,7 @@
                 "receive_ipv6_packet: received packet from non-unicast source {}; dropping",
                 packet.src_ip()
             );
-            increment_counter!(sync_ctx, "receive_ipv6_packet: non-unicast source");
+            ctx.increment_counter("receive_ipv6_packet: non-unicast source");
             return;
         }
     };
@@ -1783,7 +1787,7 @@
             }
         }
         ReceivePacketAction::Forward { dst } => {
-            increment_counter!(sync_ctx, "receive_ipv6_packet::forward");
+            ctx.increment_counter("receive_ipv6_packet::forward");
             let ttl = packet.ttl();
             if ttl > 1 {
                 trace!("receive_ipv6_packet: forwarding");
@@ -1881,7 +1885,7 @@
             }
         }
         ReceivePacketAction::Drop { reason } => {
-            increment_counter!(sync_ctx, "receive_ipv6_packet::drop");
+            ctx.increment_counter("receive_ipv6_packet::drop");
             debug!(
                 "receive_ipv6_packet: dropping packet from {} to {} received on {}: {}",
                 packet.src_ip(),
@@ -1967,7 +1971,7 @@
             | Ipv4PresentAddressStatus::SubnetBroadcast
             | Ipv4PresentAddressStatus::Multicast
             | Ipv4PresentAddressStatus::Unicast => {
-                sync_ctx.increment_counter("receive_ipv4_packet_action::deliver");
+                ctx.increment_counter("receive_ipv4_packet_action::deliver");
                 ReceivePacketAction::Deliver
             }
         },
@@ -2005,11 +2009,11 @@
     };
     match address_status {
         AddressStatus::Present(Ipv6PresentAddressStatus::Multicast) => {
-            sync_ctx.increment_counter("receive_ipv6_packet_action::deliver_multicast");
+            ctx.increment_counter("receive_ipv6_packet_action::deliver_multicast");
             ReceivePacketAction::Deliver
         }
         AddressStatus::Present(Ipv6PresentAddressStatus::UnicastAssigned) => {
-            sync_ctx.increment_counter("receive_ipv6_packet_action::deliver_unicast");
+            ctx.increment_counter("receive_ipv6_packet_action::deliver_unicast");
             ReceivePacketAction::Deliver
         }
         AddressStatus::Present(Ipv6PresentAddressStatus::UnicastTentative) => {
@@ -2043,7 +2047,7 @@
             // address. NS and NA packets should be addressed to a multicast
             // address that we would have joined during DAD so that we can
             // receive those packets.
-            sync_ctx.increment_counter("receive_ipv6_packet_action::drop_for_tentative");
+            ctx.increment_counter("receive_ipv6_packet_action::drop_for_tentative");
             ReceivePacketAction::Drop { reason: DropReason::Tentative }
         }
         AddressStatus::Unassigned => {
@@ -2078,16 +2082,16 @@
         // case is a Destination Unreachable message, we interpret the RFC text
         // to mean that, consistent with IPv4's behavior, we should silently
         // discard the packet in this case.
-        sync_ctx.increment_counter("receive_ip_packet_action_common::routing_disabled_per_device");
+        ctx.increment_counter("receive_ip_packet_action_common::routing_disabled_per_device");
         ReceivePacketAction::Drop { reason: DropReason::ForwardingDisabledInboundIface }
     } else {
         match lookup_route(sync_ctx, ctx, None, dst_ip) {
             Some(dst) => {
-                sync_ctx.increment_counter("receive_ip_packet_action_common::forward");
+                ctx.increment_counter("receive_ip_packet_action_common::forward");
                 ReceivePacketAction::Forward { dst }
             }
             None => {
-                sync_ctx.increment_counter("receive_ip_packet_action_common::no_route_to_host");
+                ctx.increment_counter("receive_ip_packet_action_common::no_route_to_host");
                 ReceivePacketAction::SendNoRouteToDest
             }
         }
@@ -2364,8 +2368,7 @@
         C: IpLayerNonSyncContext<Ipv4, SC::DeviceId> + IcmpNonSyncCtx<Ipv4>,
         SC: IpTransportLayerContext<Ipv4, C>
             + StateContext<C, IcmpState<Ipv4Addr, C::Instant, IpSock<Ipv4, SC::DeviceId>>>
-            + IpSocketHandler<Ipv4, C>
-            + CounterContext,
+            + IpSocketHandler<Ipv4, C>,
     > InnerIcmpContext<Ipv4, C> for SC
 {
     fn receive_icmp_error(
@@ -2378,7 +2381,7 @@
         original_body: &[u8],
         err: Icmpv4ErrorCode,
     ) {
-        self.increment_counter("InnerIcmpContext<Ipv4>::receive_icmp_error");
+        ctx.increment_counter("InnerIcmpContext<Ipv4>::receive_icmp_error");
         trace!("InnerIcmpContext<Ipv4>::receive_icmp_error({:?})", err);
 
         macro_rules! mtch {
@@ -2407,8 +2410,7 @@
         C: IpLayerNonSyncContext<Ipv6, SC::DeviceId> + IcmpNonSyncCtx<Ipv6>,
         SC: IpTransportLayerContext<Ipv6, C>
             + StateContext<C, IcmpState<Ipv6Addr, C::Instant, IpSock<Ipv6, SC::DeviceId>>>
-            + IpSocketHandler<Ipv6, C>
-            + CounterContext,
+            + IpSocketHandler<Ipv6, C>,
     > InnerIcmpContext<Ipv6, C> for SC
 {
     fn receive_icmp_error(
@@ -2421,7 +2423,7 @@
         original_body: &[u8],
         err: Icmpv6ErrorCode,
     ) {
-        self.increment_counter("InnerIcmpContext<Ipv6>::receive_icmp_error");
+        ctx.increment_counter("InnerIcmpContext<Ipv6>::receive_icmp_error");
         trace!("InnerIcmpContext<Ipv6>::receive_icmp_error({:?})", err);
 
         macro_rules! mtch {
@@ -2718,10 +2720,10 @@
             buf,
         );
 
-        assert_eq!(get_counter_val(&mut sync_ctx, "send_icmpv4_parameter_problem"), 0);
-        assert_eq!(get_counter_val(&mut sync_ctx, "send_icmpv6_parameter_problem"), 0);
-        assert_eq!(get_counter_val(&mut sync_ctx, "dispatch_receive_ipv4_packet"), 0);
-        assert_eq!(get_counter_val(&mut sync_ctx, "dispatch_receive_ipv6_packet"), 0);
+        assert_eq!(get_counter_val(&non_sync_ctx, "send_icmpv4_parameter_problem"), 0);
+        assert_eq!(get_counter_val(&non_sync_ctx, "send_icmpv6_parameter_problem"), 0);
+        assert_eq!(get_counter_val(&non_sync_ctx, "dispatch_receive_ipv4_packet"), 0);
+        assert_eq!(get_counter_val(&non_sync_ctx, "dispatch_receive_ipv6_packet"), 0);
     }
 
     #[test]
@@ -2796,8 +2798,8 @@
             false,
         );
         receive_ipv6_packet(&mut sync_ctx, &mut non_sync_ctx, device, frame_dst, buf);
-        assert_eq!(get_counter_val(&mut sync_ctx, "send_icmpv6_parameter_problem"), expected_icmps);
-        assert_eq!(get_counter_val(&mut sync_ctx, "dispatch_receive_ipv6_packet"), 1);
+        assert_eq!(get_counter_val(&non_sync_ctx, "send_icmpv6_parameter_problem"), expected_icmps);
+        assert_eq!(get_counter_val(&non_sync_ctx, "dispatch_receive_ipv6_packet"), 1);
         assert_eq!(non_sync_ctx.frames_sent().len(), expected_icmps);
 
         // Test with unrecognized option type set with
@@ -2809,7 +2811,7 @@
             false,
         );
         receive_ipv6_packet(&mut sync_ctx, &mut non_sync_ctx, device, frame_dst, buf);
-        assert_eq!(get_counter_val(&mut sync_ctx, "send_icmpv6_parameter_problem"), expected_icmps);
+        assert_eq!(get_counter_val(&non_sync_ctx, "send_icmpv6_parameter_problem"), expected_icmps);
         assert_eq!(non_sync_ctx.frames_sent().len(), expected_icmps);
 
         // Test with unrecognized option type set with
@@ -2823,7 +2825,7 @@
         );
         receive_ipv6_packet(&mut sync_ctx, &mut non_sync_ctx, device, frame_dst, buf);
         expected_icmps += 1;
-        assert_eq!(get_counter_val(&mut sync_ctx, "send_icmpv6_parameter_problem"), expected_icmps);
+        assert_eq!(get_counter_val(&non_sync_ctx, "send_icmpv6_parameter_problem"), expected_icmps);
         assert_eq!(non_sync_ctx.frames_sent().len(), expected_icmps);
         verify_icmp_for_unrecognized_ext_hdr_option(
             &mut non_sync_ctx,
@@ -2843,7 +2845,7 @@
         );
         receive_ipv6_packet(&mut sync_ctx, &mut non_sync_ctx, device, frame_dst, buf);
         expected_icmps += 1;
-        assert_eq!(get_counter_val(&mut sync_ctx, "send_icmpv6_parameter_problem"), expected_icmps);
+        assert_eq!(get_counter_val(&non_sync_ctx, "send_icmpv6_parameter_problem"), expected_icmps);
         assert_eq!(non_sync_ctx.frames_sent().len(), expected_icmps);
         verify_icmp_for_unrecognized_ext_hdr_option(
             &mut non_sync_ctx,
@@ -2863,7 +2865,7 @@
         );
         receive_ipv6_packet(&mut sync_ctx, &mut non_sync_ctx, device, frame_dst, buf);
         expected_icmps += 1;
-        assert_eq!(get_counter_val(&mut sync_ctx, "send_icmpv6_parameter_problem"), expected_icmps);
+        assert_eq!(get_counter_val(&non_sync_ctx, "send_icmpv6_parameter_problem"), expected_icmps);
         assert_eq!(non_sync_ctx.frames_sent().len(), expected_icmps);
         verify_icmp_for_unrecognized_ext_hdr_option(
             &mut non_sync_ctx,
@@ -2883,14 +2885,14 @@
         );
         // Do not expect an ICMP response for this packet
         receive_ipv6_packet(&mut sync_ctx, &mut non_sync_ctx, device, frame_dst, buf);
-        assert_eq!(get_counter_val(&mut sync_ctx, "send_icmpv6_parameter_problem"), expected_icmps);
+        assert_eq!(get_counter_val(&non_sync_ctx, "send_icmpv6_parameter_problem"), expected_icmps);
         assert_eq!(non_sync_ctx.frames_sent().len(), expected_icmps);
 
         // None of our tests should have sent an icmpv4 packet, or dispatched an
         // IP packet after the first.
 
-        assert_eq!(get_counter_val(&mut sync_ctx, "send_icmpv4_parameter_problem"), 0);
-        assert_eq!(get_counter_val(&mut sync_ctx, "dispatch_receive_ipv6_packet"), 1);
+        assert_eq!(get_counter_val(&non_sync_ctx, "send_icmpv4_parameter_problem"), 0);
+        assert_eq!(get_counter_val(&non_sync_ctx, "dispatch_receive_ipv6_packet"), 1);
     }
 
     #[ip_test]
@@ -2900,14 +2902,14 @@
         let device = DeviceId::new_ethernet(0);
         let fragment_id = 5;
 
-        assert_eq!(get_counter_val(&mut sync_ctx, dispatch_receive_ip_packet_name::<I>()), 0);
+        assert_eq!(get_counter_val(&non_sync_ctx, dispatch_receive_ip_packet_name::<I>()), 0);
 
         // Test that a non fragmented packet gets dispatched right away.
 
         process_ip_fragment::<I, _>(&mut sync_ctx, &mut non_sync_ctx, device, fragment_id, 0, 1);
 
         // Make sure the packet got dispatched.
-        assert_eq!(get_counter_val(&mut sync_ctx, dispatch_receive_ip_packet_name::<I>()), 1);
+        assert_eq!(get_counter_val(&non_sync_ctx, dispatch_receive_ip_packet_name::<I>()), 1);
     }
 
     #[ip_test]
@@ -2927,14 +2929,14 @@
         process_ip_fragment::<I, _>(&mut sync_ctx, &mut non_sync_ctx, device, fragment_id, 1, 3);
 
         // Make sure no packets got dispatched yet.
-        assert_eq!(get_counter_val(&mut sync_ctx, dispatch_receive_ip_packet_name::<I>()), 0);
+        assert_eq!(get_counter_val(&non_sync_ctx, dispatch_receive_ip_packet_name::<I>()), 0);
 
         // Process fragment #2
         process_ip_fragment::<I, _>(&mut sync_ctx, &mut non_sync_ctx, device, fragment_id, 2, 3);
 
         // Make sure the packet finally got dispatched now that the final
         // fragment has been 'received'.
-        assert_eq!(get_counter_val(&mut sync_ctx, dispatch_receive_ip_packet_name::<I>()), 1);
+        assert_eq!(get_counter_val(&non_sync_ctx, dispatch_receive_ip_packet_name::<I>()), 1);
     }
 
     #[ip_test]
@@ -2959,33 +2961,33 @@
         process_ip_fragment::<I, _>(&mut sync_ctx, &mut non_sync_ctx, device, fragment_id_1, 0, 3);
 
         // Make sure no packets got dispatched yet.
-        assert_eq!(get_counter_val(&mut sync_ctx, dispatch_receive_ip_packet_name::<I>()), 0);
+        assert_eq!(get_counter_val(&non_sync_ctx, dispatch_receive_ip_packet_name::<I>()), 0);
 
         // Process a packet that does not require reassembly (packet #2, fragment #0).
         process_ip_fragment::<I, _>(&mut sync_ctx, &mut non_sync_ctx, device, fragment_id_2, 0, 1);
 
         // Make packet #1 got dispatched since it didn't need reassembly.
-        assert_eq!(get_counter_val(&mut sync_ctx, dispatch_receive_ip_packet_name::<I>()), 1);
+        assert_eq!(get_counter_val(&non_sync_ctx, dispatch_receive_ip_packet_name::<I>()), 1);
 
         // Process packet #0, fragment #2
         process_ip_fragment::<I, _>(&mut sync_ctx, &mut non_sync_ctx, device, fragment_id_0, 2, 3);
 
         // Make sure no other packets got dispatched yet.
-        assert_eq!(get_counter_val(&mut sync_ctx, dispatch_receive_ip_packet_name::<I>()), 1);
+        assert_eq!(get_counter_val(&non_sync_ctx, dispatch_receive_ip_packet_name::<I>()), 1);
 
         // Process packet #0, fragment #0
         process_ip_fragment::<I, _>(&mut sync_ctx, &mut non_sync_ctx, device, fragment_id_0, 0, 3);
 
         // Make sure that packet #0 finally got dispatched now that the final
         // fragment has been 'received'.
-        assert_eq!(get_counter_val(&mut sync_ctx, dispatch_receive_ip_packet_name::<I>()), 2);
+        assert_eq!(get_counter_val(&non_sync_ctx, dispatch_receive_ip_packet_name::<I>()), 2);
 
         // Process packet #1, fragment #1
         process_ip_fragment::<I, _>(&mut sync_ctx, &mut non_sync_ctx, device, fragment_id_1, 1, 3);
 
         // Make sure the packet finally got dispatched now that the final
         // fragment has been 'received'.
-        assert_eq!(get_counter_val(&mut sync_ctx, dispatch_receive_ip_packet_name::<I>()), 3);
+        assert_eq!(get_counter_val(&non_sync_ctx, dispatch_receive_ip_packet_name::<I>()), 3);
     }
 
     #[ip_test]
@@ -3039,7 +3041,7 @@
         // technically received all the fragments, this fragment (#2) arrived
         // too late and the reassembly timer was triggered, causing the prior
         // fragment data to be discarded.
-        assert_eq!(get_counter_val(&mut sync_ctx, dispatch_receive_ip_packet_name::<I>()), 0);
+        assert_eq!(get_counter_val(&non_sync_ctx, dispatch_receive_ip_packet_name::<I>()), 0);
     }
 
     #[ip_test]
@@ -3081,10 +3083,13 @@
 
         // Make sure no packets got dispatched yet.
         assert_eq!(
-            get_counter_val(net.sync_ctx("alice"), dispatch_receive_ip_packet_name::<I>()),
+            get_counter_val(net.non_sync_ctx("alice"), dispatch_receive_ip_packet_name::<I>()),
             0
         );
-        assert_eq!(get_counter_val(net.sync_ctx("bob"), dispatch_receive_ip_packet_name::<I>()), 0);
+        assert_eq!(
+            get_counter_val(net.non_sync_ctx("bob"), dispatch_receive_ip_packet_name::<I>()),
+            0
+        );
 
         // Process fragment #2
         net.with_context("alice", |Ctx { sync_ctx, non_sync_ctx }| {
@@ -3095,10 +3100,13 @@
         // Make sure the packet finally got dispatched now that the final
         // fragment has been received by bob.
         assert_eq!(
-            get_counter_val(net.sync_ctx("alice"), dispatch_receive_ip_packet_name::<I>()),
+            get_counter_val(net.non_sync_ctx("alice"), dispatch_receive_ip_packet_name::<I>()),
             0
         );
-        assert_eq!(get_counter_val(net.sync_ctx("bob"), dispatch_receive_ip_packet_name::<I>()), 1);
+        assert_eq!(
+            get_counter_val(net.non_sync_ctx("bob"), dispatch_receive_ip_packet_name::<I>()),
+            1
+        );
 
         // Make sure there are no more events.
         assert!(net.step(receive_frame_or_panic, handle_timer).is_idle());
@@ -3156,8 +3164,8 @@
         );
 
         // Should not have dispatched the packet.
-        assert_eq!(get_counter_val(&mut sync_ctx, "dispatch_receive_ipv6_packet"), 0);
-        assert_eq!(get_counter_val(&mut sync_ctx, "send_icmpv6_packet_too_big"), 1);
+        assert_eq!(get_counter_val(&non_sync_ctx, "dispatch_receive_ipv6_packet"), 0);
+        assert_eq!(get_counter_val(&non_sync_ctx, "send_icmpv6_packet_too_big"), 1);
 
         // Should have sent out one frame though.
         assert_eq!(non_sync_ctx.frames_sent().len(), 1);
@@ -3256,7 +3264,7 @@
         );
 
         // Should have dispatched the packet.
-        assert_eq!(get_counter_val(&mut sync_ctx, dispatch_receive_ip_packet_name::<I>()), 1);
+        assert_eq!(get_counter_val(&non_sync_ctx, dispatch_receive_ip_packet_name::<I>()), 1);
 
         assert_eq!(
             I::get_state_inner(&sync_ctx.state)
@@ -3289,7 +3297,7 @@
         );
 
         // Should have dispatched the packet.
-        assert_eq!(get_counter_val(&mut sync_ctx, dispatch_receive_ip_packet_name::<I>()), 2);
+        assert_eq!(get_counter_val(&non_sync_ctx, dispatch_receive_ip_packet_name::<I>()), 2);
 
         // The PMTU should not have updated to `new_mtu2`
         assert_eq!(
@@ -3323,7 +3331,7 @@
         );
 
         // Should have dispatched the packet.
-        assert_eq!(get_counter_val(&mut sync_ctx, dispatch_receive_ip_packet_name::<I>()), 3);
+        assert_eq!(get_counter_val(&non_sync_ctx, dispatch_receive_ip_packet_name::<I>()), 3);
 
         // The PMTU should have updated to 1900.
         assert_eq!(
@@ -3369,7 +3377,7 @@
         );
 
         // Should have dispatched the packet.
-        assert_eq!(get_counter_val(&mut sync_ctx, dispatch_receive_ip_packet_name::<I>()), 1);
+        assert_eq!(get_counter_val(&non_sync_ctx, dispatch_receive_ip_packet_name::<I>()), 1);
 
         assert_eq!(
             I::get_state_inner(&sync_ctx.state)
@@ -3417,7 +3425,7 @@
         receive_ipv4_packet(&mut sync_ctx, &mut non_sync_ctx, device, frame_dst, packet_buf);
 
         // Should have dispatched the packet.
-        assert_eq!(get_counter_val(&mut sync_ctx, "dispatch_receive_ipv4_packet"), 1);
+        assert_eq!(get_counter_val(&non_sync_ctx, "dispatch_receive_ipv4_packet"), 1);
 
         // Should have decreased PMTU value to the next lower PMTU
         // plateau from `crate::ip::path_mtu::PMTU_PLATEAUS`.
@@ -3444,7 +3452,7 @@
         receive_ipv4_packet(&mut sync_ctx, &mut non_sync_ctx, device, frame_dst, packet_buf);
 
         // Should have dispatched the packet.
-        assert_eq!(get_counter_val(&mut sync_ctx, "dispatch_receive_ipv4_packet"), 2);
+        assert_eq!(get_counter_val(&non_sync_ctx, "dispatch_receive_ipv4_packet"), 2);
 
         // Should not have updated PMTU as there is no other valid
         // lower PMTU value.
@@ -3471,7 +3479,7 @@
         receive_ipv4_packet(&mut sync_ctx, &mut non_sync_ctx, device, frame_dst, packet_buf);
 
         // Should have dispatched the packet.
-        assert_eq!(get_counter_val(&mut sync_ctx, "dispatch_receive_ipv4_packet"), 3);
+        assert_eq!(get_counter_val(&non_sync_ctx, "dispatch_receive_ipv4_packet"), 3);
 
         // Should have decreased PMTU value to the next lower PMTU
         // plateau from `crate::ip::path_mtu::PMTU_PLATEAUS`.
@@ -3501,7 +3509,7 @@
         receive_ipv4_packet(&mut sync_ctx, &mut non_sync_ctx, device, frame_dst, packet_buf);
 
         // Should have dispatched the packet.
-        assert_eq!(get_counter_val(&mut sync_ctx, "dispatch_receive_ipv4_packet"), 4);
+        assert_eq!(get_counter_val(&non_sync_ctx, "dispatch_receive_ipv4_packet"), 4);
 
         // Should not have updated the PMTU as the current PMTU is lower.
         assert_eq!(
@@ -3546,8 +3554,8 @@
         receive_ipv6_packet(&mut sync_ctx, &mut non_sync_ctx, device, frame_dst, buf);
 
         // Should not have dispatched the packet.
-        assert_eq!(get_counter_val(&mut sync_ctx, "receive_ipv6_packet"), 1);
-        assert_eq!(get_counter_val(&mut sync_ctx, "dispatch_receive_ipv6_packet"), 0);
+        assert_eq!(get_counter_val(&non_sync_ctx, "receive_ipv6_packet"), 1);
+        assert_eq!(get_counter_val(&non_sync_ctx, "dispatch_receive_ipv6_packet"), 0);
 
         // In IPv6, the next header value (ICMP(v4)) would have been considered
         // unrecognized so an ICMP parameter problem response SHOULD be sent,
@@ -3589,8 +3597,8 @@
         receive_ipv4_packet(&mut sync_ctx, &mut non_sync_ctx, device, frame_dst, buf);
 
         // Should have dispatched the packet but resulted in an ICMP error.
-        assert_eq!(get_counter_val(&mut sync_ctx, "dispatch_receive_ipv4_packet"), 1);
-        assert_eq!(get_counter_val(&mut sync_ctx, "send_icmpv4_dest_unreachable"), 1);
+        assert_eq!(get_counter_val(&non_sync_ctx, "dispatch_receive_ipv4_packet"), 1);
+        assert_eq!(get_counter_val(&non_sync_ctx, "send_icmpv4_dest_unreachable"), 1);
         assert_eq!(non_sync_ctx.frames_sent().len(), 1);
         let buf = &non_sync_ctx.frames_sent()[0].1[..];
         let (_, _, _, _, _, _, code) =
@@ -3637,7 +3645,7 @@
         assert!(!I::get_ip_device_state(&sync_ctx, device).multicast_groups.contains(&multi_addr));
         receive_frame(&mut sync_ctx, &mut non_sync_ctx, device, buf.clone())
             .expect("error receiving frame");
-        assert_eq!(get_counter_val(&mut sync_ctx, dispatch_receive_ip_packet_name::<I>()), 0);
+        assert_eq!(get_counter_val(&non_sync_ctx, dispatch_receive_ip_packet_name::<I>()), 0);
 
         // Join the multicast group and receive the packet, we should dispatch
         // it.
@@ -3658,7 +3666,7 @@
         assert!(I::get_ip_device_state(&sync_ctx, device).multicast_groups.contains(&multi_addr));
         receive_frame(&mut sync_ctx, &mut non_sync_ctx, device, buf.clone())
             .expect("error receiving frame");
-        assert_eq!(get_counter_val(&mut sync_ctx, dispatch_receive_ip_packet_name::<I>()), 1);
+        assert_eq!(get_counter_val(&non_sync_ctx, dispatch_receive_ip_packet_name::<I>()), 1);
 
         // Leave the multicast group and receive the packet, we should not
         // dispatch it.
@@ -3679,7 +3687,7 @@
         assert!(!I::get_ip_device_state(&sync_ctx, device).multicast_groups.contains(&multi_addr));
         receive_frame(&mut sync_ctx, &mut non_sync_ctx, device, buf.clone())
             .expect("error receiving frame");
-        assert_eq!(get_counter_val(&mut sync_ctx, dispatch_receive_ip_packet_name::<I>()), 1);
+        assert_eq!(get_counter_val(&non_sync_ctx, dispatch_receive_ip_packet_name::<I>()), 1);
     }
 
     #[test]
@@ -3720,7 +3728,7 @@
 
         // Received packet should not have been dispatched.
         receive_ipv6_packet(&mut sync_ctx, &mut non_sync_ctx, device, frame_dst, buf.clone());
-        assert_eq!(get_counter_val(&mut sync_ctx, "dispatch_receive_ipv6_packet"), 0);
+        assert_eq!(get_counter_val(&non_sync_ctx, "dispatch_receive_ipv6_packet"), 0);
 
         // Wait until DAD is complete. Arbitrarily choose a year in the future
         // as a time after which we're confident DAD will be complete. We can't
@@ -3738,7 +3746,7 @@
 
         // Received packet should have been dispatched.
         receive_ipv6_packet(&mut sync_ctx, &mut non_sync_ctx, device, frame_dst, buf);
-        assert_eq!(get_counter_val(&mut sync_ctx, "dispatch_receive_ipv6_packet"), 1);
+        assert_eq!(get_counter_val(&non_sync_ctx, "dispatch_receive_ipv6_packet"), 1);
 
         // Set the new IP (this should trigger DAD).
         let ip = config.local_ip.get();
@@ -3758,7 +3766,7 @@
 
         // Received packet should not have been dispatched.
         receive_ipv6_packet(&mut sync_ctx, &mut non_sync_ctx, device, frame_dst, buf.clone());
-        assert_eq!(get_counter_val(&mut sync_ctx, "dispatch_receive_ipv6_packet"), 1);
+        assert_eq!(get_counter_val(&non_sync_ctx, "dispatch_receive_ipv6_packet"), 1);
 
         // Make sure all timers are done (DAD to complete on the interface due
         // to new IP).
@@ -3773,7 +3781,7 @@
 
         // Received packet should have been dispatched.
         receive_ipv6_packet(&mut sync_ctx, &mut non_sync_ctx, device, frame_dst, buf);
-        assert_eq!(get_counter_val(&mut sync_ctx, "dispatch_receive_ipv6_packet"), 2);
+        assert_eq!(get_counter_val(&non_sync_ctx, "dispatch_receive_ipv6_packet"), 2);
     }
 
     #[test]
@@ -3810,7 +3818,7 @@
             FrameDestination::Unicast,
             buf.clone(),
         );
-        assert_eq!(get_counter_val(&mut sync_ctx, "receive_ipv6_packet: non-unicast source"), 1);
+        assert_eq!(get_counter_val(&non_sync_ctx, "receive_ipv6_packet: non-unicast source"), 1);
     }
 
     #[test]
diff --git a/src/connectivity/network/netstack3/core/src/ip/socket.rs b/src/connectivity/network/netstack3/core/src/ip/socket.rs
index c3fc160..f76dfc9 100644
--- a/src/connectivity/network/netstack3/core/src/ip/socket.rs
+++ b/src/connectivity/network/netstack3/core/src/ip/socket.rs
@@ -337,15 +337,15 @@
 }
 
 /// The non-synchronized execution context for IP sockets.
-pub(super) trait IpSocketNonSyncContext: InstantContext {}
-impl<C: InstantContext> IpSocketNonSyncContext for C {}
+pub(super) trait IpSocketNonSyncContext: InstantContext + CounterContext {}
+impl<C: InstantContext + CounterContext> IpSocketNonSyncContext for C {}
 
 /// The context required in order to implement [`IpSocketHandler`].
 ///
 /// Blanket impls of `IpSocketHandler` are provided in terms of
 /// `IpSocketContext`.
 pub(super) trait IpSocketContext<I, C: IpSocketNonSyncContext>:
-    IpDeviceIdContext<I> + CounterContext
+    IpDeviceIdContext<I>
 where
     I: IpDeviceStateIpExt<C::Instant>,
 {
@@ -496,7 +496,7 @@
         mtu: Option<u32>,
     ) -> Result<(), (S, IpSockSendError)> {
         // TODO(joshlf): Call `trace!` with relevant fields from the socket.
-        self.increment_counter("send_ipv4_packet");
+        ctx.increment_counter("send_ipv4_packet");
 
         send_ip_packet(self, ctx, ip_sock, body, mtu)
     }
@@ -516,7 +516,7 @@
         mtu: Option<u32>,
     ) -> Result<(), (S, IpSockSendError)> {
         // TODO(joshlf): Call `trace!` with relevant fields from the socket.
-        self.increment_counter("send_ipv6_packet");
+        ctx.increment_counter("send_ipv6_packet");
 
         send_ip_packet(self, ctx, ip_sock, body, mtu)
     }
@@ -1543,10 +1543,10 @@
         assert_eq!(non_sync_ctx.frames_sent().len(), 0);
 
         #[ipv4]
-        assert_eq!(get_counter_val(&mut sync_ctx, "dispatch_receive_ipv4_packet"), 1);
+        assert_eq!(get_counter_val(&non_sync_ctx, "dispatch_receive_ipv4_packet"), 1);
 
         #[ipv6]
-        assert_eq!(get_counter_val(&mut sync_ctx, "dispatch_receive_ipv6_packet"), 1);
+        assert_eq!(get_counter_val(&non_sync_ctx, "dispatch_receive_ipv6_packet"), 1);
     }
 
     #[ip_test]
diff --git a/src/connectivity/network/netstack3/core/src/lib.rs b/src/connectivity/network/netstack3/core/src/lib.rs
index f799389..4b3aab7 100644
--- a/src/connectivity/network/netstack3/core/src/lib.rs
+++ b/src/connectivity/network/netstack3/core/src/lib.rs
@@ -94,7 +94,7 @@
 use packet::{Buf, BufferMut, EmptyBuf};
 
 use crate::{
-    context::{EventContext, RngContext, TimerContext},
+    context::{CounterContext, EventContext, RngContext, TimerContext},
     device::{DeviceLayerState, DeviceLayerTimerId},
     ip::{
         device::{Ipv4DeviceTimerId, Ipv6DeviceTimerId},
@@ -202,8 +202,6 @@
             ipv4: self.ipv4.build(),
             ipv6: self.ipv6.build(),
             device: Default::default(),
-            #[cfg(test)]
-            test_counters: Default::default(),
         }
     }
 }
@@ -214,8 +212,6 @@
     ipv4: Ipv4State<I, DeviceId>,
     ipv6: Ipv6State<I, DeviceId>,
     device: DeviceLayerState<I>,
-    #[cfg(test)]
-    test_counters: core::cell::RefCell<testutil::TestCounters>,
 }
 
 impl<I: Instant> Default for StackState<I> {
@@ -253,7 +249,8 @@
 
 /// The non-synchronized context for the stack.
 pub trait NonSyncContext:
-    BufferNonSyncContextInner<Buf<Vec<u8>>>
+    CounterContext
+    + BufferNonSyncContextInner<Buf<Vec<u8>>>
     + BufferNonSyncContextInner<EmptyBuf>
     + RngContext
     + TimerContext<TimerId>
@@ -270,7 +267,8 @@
 {
 }
 impl<
-        C: BufferNonSyncContextInner<Buf<Vec<u8>>>
+        C: CounterContext
+            + BufferNonSyncContextInner<Buf<Vec<u8>>>
             + BufferNonSyncContextInner<EmptyBuf>
             + RngContext
             + TimerContext<TimerId>
@@ -416,7 +414,7 @@
         }
         #[cfg(test)]
         TimerId(TimerIdInner::Nop(_)) => {
-            increment_counter!(sync_ctx, "timer::nop");
+            ctx.increment_counter("timer::nop");
         }
     }
 }
diff --git a/src/connectivity/network/netstack3/core/src/macros.rs b/src/connectivity/network/netstack3/core/src/macros.rs
index cf60824..77fddcb 100644
--- a/src/connectivity/network/netstack3/core/src/macros.rs
+++ b/src/connectivity/network/netstack3/core/src/macros.rs
@@ -23,13 +23,6 @@
     }};
 }
 
-macro_rules! increment_counter {
-    ($ctx:ident, $key:expr) => {
-        #[cfg(test)]
-        crate::context::CounterContext::increment_counter($ctx, $key);
-    };
-}
-
 /// Implement [`TimerContext`] for one ID type in terms of an existing
 /// implementation for a different ID type.
 ///
diff --git a/src/connectivity/network/netstack3/core/src/testutil.rs b/src/connectivity/network/netstack3/core/src/testutil.rs
index f24f52c..fc15125 100644
--- a/src/connectivity/network/netstack3/core/src/testutil.rs
+++ b/src/connectivity/network/netstack3/core/src/testutil.rs
@@ -4,13 +4,7 @@
 
 //! Testing-related utilities.
 
-use alloc::{
-    borrow::ToOwned,
-    collections::HashMap,
-    string::{String, ToString},
-    vec,
-    vec::Vec,
-};
+use alloc::{borrow::ToOwned, collections::HashMap, vec, vec::Vec};
 use core::{fmt::Debug, time::Duration};
 
 use net_types::{
@@ -223,21 +217,6 @@
     }
 }
 
-#[derive(Default, Debug)]
-pub(crate) struct TestCounters {
-    data: HashMap<String, usize>,
-}
-
-impl TestCounters {
-    pub(crate) fn increment(&mut self, key: &str) {
-        *(self.data.entry(key.to_string()).or_insert(0)) += 1;
-    }
-
-    pub(crate) fn get(&self, key: &str) -> &usize {
-        self.data.get(key).unwrap_or(&0)
-    }
-}
-
 /// log::Log implementation that uses stdout.
 ///
 /// Useful when debugging tests.
@@ -273,8 +252,8 @@
 }
 
 /// Get the counter value for a `key`.
-pub(crate) fn get_counter_val(ctx: &DummySyncCtx, key: &str) -> usize {
-    *ctx.state.test_counters.borrow().get(key)
+pub(crate) fn get_counter_val(ctx: &DummyNonSyncCtx, key: &str) -> usize {
+    ctx.counter_ctx().get_counter_val(key)
 }
 
 /// An extension trait for `Ip` providing test-related functionality.
@@ -872,28 +851,28 @@
         });
 
         // No timers fired before.
-        assert_eq!(get_counter_val(net.sync_ctx(1), "timer::nop"), 0);
-        assert_eq!(get_counter_val(net.sync_ctx(2), "timer::nop"), 0);
+        assert_eq!(get_counter_val(net.non_sync_ctx(1), "timer::nop"), 0);
+        assert_eq!(get_counter_val(net.non_sync_ctx(2), "timer::nop"), 0);
         assert_eq!(net.step(receive_frame_or_panic, handle_timer).timers_fired, 1);
         // Only timer in context 1 should have fired.
-        assert_eq!(get_counter_val(net.sync_ctx(1), "timer::nop"), 1);
-        assert_eq!(get_counter_val(net.sync_ctx(2), "timer::nop"), 0);
+        assert_eq!(get_counter_val(net.non_sync_ctx(1), "timer::nop"), 1);
+        assert_eq!(get_counter_val(net.non_sync_ctx(2), "timer::nop"), 0);
         assert_eq!(net.step(receive_frame_or_panic, handle_timer).timers_fired, 1);
         // Only timer in context 2 should have fired.
-        assert_eq!(get_counter_val(net.sync_ctx(1), "timer::nop"), 1);
-        assert_eq!(get_counter_val(net.sync_ctx(2), "timer::nop"), 1);
+        assert_eq!(get_counter_val(net.non_sync_ctx(1), "timer::nop"), 1);
+        assert_eq!(get_counter_val(net.non_sync_ctx(2), "timer::nop"), 1);
         assert_eq!(net.step(receive_frame_or_panic, handle_timer).timers_fired, 1);
         // Only timer in context 2 should have fired.
-        assert_eq!(get_counter_val(net.sync_ctx(1), "timer::nop"), 1);
-        assert_eq!(get_counter_val(net.sync_ctx(2), "timer::nop"), 2);
+        assert_eq!(get_counter_val(net.non_sync_ctx(1), "timer::nop"), 1);
+        assert_eq!(get_counter_val(net.non_sync_ctx(2), "timer::nop"), 2);
         assert_eq!(net.step(receive_frame_or_panic, handle_timer).timers_fired, 1);
         // Only timer in context 1 should have fired.
-        assert_eq!(get_counter_val(net.sync_ctx(1), "timer::nop"), 2);
-        assert_eq!(get_counter_val(net.sync_ctx(2), "timer::nop"), 2);
+        assert_eq!(get_counter_val(net.non_sync_ctx(1), "timer::nop"), 2);
+        assert_eq!(get_counter_val(net.non_sync_ctx(2), "timer::nop"), 2);
         assert_eq!(net.step(receive_frame_or_panic, handle_timer).timers_fired, 2);
         // Both timers have fired at the same time.
-        assert_eq!(get_counter_val(net.sync_ctx(1), "timer::nop"), 3);
-        assert_eq!(get_counter_val(net.sync_ctx(2), "timer::nop"), 3);
+        assert_eq!(get_counter_val(net.non_sync_ctx(1), "timer::nop"), 3);
+        assert_eq!(get_counter_val(net.non_sync_ctx(2), "timer::nop"), 3);
 
         assert!(net.step(receive_frame_or_panic, handle_timer).is_idle());
         // Check that current time on contexts tick together.
@@ -929,8 +908,8 @@
         });
 
         while !net.step(receive_frame_or_panic, handle_timer).is_idle()
-            && (get_counter_val(net.sync_ctx(1), "timer::nop") < 1
-                || get_counter_val(net.sync_ctx(2), "timer::nop") < 1)
+            && (get_counter_val(net.non_sync_ctx(1), "timer::nop") < 1
+                || get_counter_val(net.non_sync_ctx(2), "timer::nop") < 1)
         {}
         // Assert that we stopped before all times were fired, meaning we can
         // step again.
@@ -1017,13 +996,13 @@
             bob_echo_request: usize,
             alice_echo_response: usize,
         ) {
-            let alice = net.sync_ctx("alice");
+            let alice = net.non_sync_ctx("alice");
             assert_eq!(get_counter_val(alice, "timer::nop"), alice_nop);
             assert_eq!(get_counter_val(alice, "<IcmpIpTransportContext as BufferIpTransportContext<Ipv4>>::receive_ip_packet::echo_reply"),
                 alice_echo_response
             );
 
-            let bob = net.sync_ctx("bob");
+            let bob = net.non_sync_ctx("bob");
             assert_eq!(get_counter_val(bob, "timer::nop"), bob_nop);
             assert_eq!(get_counter_val(bob, "<IcmpIpTransportContext as BufferIpTransportContext<Ipv4>>::receive_ip_packet::echo_request"),
                 bob_echo_request
diff --git a/src/connectivity/network/netstack3/core/src/transport/integration.rs b/src/connectivity/network/netstack3/core/src/transport/integration.rs
index 61cd650..bdcb58a 100644
--- a/src/connectivity/network/netstack3/core/src/transport/integration.rs
+++ b/src/connectivity/network/netstack3/core/src/transport/integration.rs
@@ -5,9 +5,7 @@
 use net_types::MulticastAddr;
 
 use crate::{
-    context::CounterContext,
-    context::NonTestCtxMarker,
-    context::StateContext,
+    context::{NonTestCtxMarker, StateContext},
     ip::{
         device::{IpDeviceContext, IpDeviceIpExt, IpDeviceNonSyncContext},
         gmp::GmpHandler,
@@ -19,8 +17,7 @@
 impl<
         I: IpExt + IpDeviceIpExt<C::Instant, SC::DeviceId>,
         C: UdpStateNonSyncContext<I> + IpDeviceNonSyncContext<I, SC::DeviceId>,
-        SC: CounterContext
-            + TransportIpContext<I, C>
+        SC: TransportIpContext<I, C>
             + StateContext<C, UdpState<I, SC::DeviceId>>
             + IpDeviceContext<I, C>
             + GmpHandler<I, C>
diff --git a/src/connectivity/network/netstack3/core/src/transport/udp.rs b/src/connectivity/network/netstack3/core/src/transport/udp.rs
index 500fbb8..36bde81 100644
--- a/src/connectivity/network/netstack3/core/src/transport/udp.rs
+++ b/src/connectivity/network/netstack3/core/src/transport/udp.rs
@@ -884,14 +884,18 @@
 }
 
 /// The non-synchronized context for UDP.
-pub trait UdpStateNonSyncContext<I: IpExt>: InstantContext + RngContext + UdpContext<I> {}
-impl<I: IpExt, C: InstantContext + RngContext + UdpContext<I>> UdpStateNonSyncContext<I> for C {}
+pub trait UdpStateNonSyncContext<I: IpExt>:
+    InstantContext + RngContext + UdpContext<I> + CounterContext
+{
+}
+impl<I: IpExt, C: InstantContext + RngContext + UdpContext<I> + CounterContext>
+    UdpStateNonSyncContext<I> for C
+{
+}
 
 /// An execution context for the UDP protocol which also provides access to state.
 pub trait UdpStateContext<I: IpExt, C: UdpStateNonSyncContext<I>>:
-    CounterContext
-    + TransportIpContext<I, C>
-    + StateContext<C, UdpState<I, <Self as IpDeviceIdContext<I>>::DeviceId>>
+    TransportIpContext<I, C> + StateContext<C, UdpState<I, <Self as IpDeviceIdContext<I>>::DeviceId>>
 {
     /// Requests that the specified device join the given multicast group.
     ///
@@ -1035,7 +1039,7 @@
         mut udp_packet: &[u8],
         err: I::ErrorCode,
     ) {
-        sync_ctx.increment_counter("UdpIpTransportContext::receive_icmp_error");
+        ctx.increment_counter("UdpIpTransportContext::receive_icmp_error");
         trace!("UdpIpTransportContext::receive_icmp_error({:?})", err);
 
         let udp_packet =
diff --git a/src/connectivity/network/netstack3/src/bindings/integration_tests.rs b/src/connectivity/network/netstack3/src/bindings/integration_tests.rs
index 0985de5..5cb40cc 100644
--- a/src/connectivity/network/netstack3/src/bindings/integration_tests.rs
+++ b/src/connectivity/network/netstack3/src/bindings/integration_tests.rs
@@ -21,7 +21,7 @@
     SpecifiedAddr,
 };
 use netstack3_core::{
-    context::{EventContext, InstantContext, RngContext, TimerContext},
+    context::{CounterContext, EventContext, InstantContext, RngContext, TimerContext},
     get_all_ip_addr_subnets, get_ipv4_configuration, get_ipv6_configuration,
     icmp::{BufferIcmpContext, IcmpConnId, IcmpContext, IcmpIpExt},
     update_ipv4_configuration, update_ipv6_configuration, AddableEntryEither, BufferUdpContext,
@@ -133,6 +133,12 @@
     }
 }
 
+impl CounterContext for TestNonSyncCtx {
+    fn increment_counter(&mut self, key: &'static str) {
+        self.ctx.increment_counter(key)
+    }
+}
+
 impl RngContext for TestNonSyncCtx {
     type Rng = OsRng;
 
diff --git a/src/connectivity/network/netstack3/src/bindings/interfaces_admin.rs b/src/connectivity/network/netstack3/src/bindings/interfaces_admin.rs
index 9725d88..072ba24 100644
--- a/src/connectivity/network/netstack3/src/bindings/interfaces_admin.rs
+++ b/src/connectivity/network/netstack3/src/bindings/interfaces_admin.rs
@@ -55,10 +55,9 @@
     let worker = netdevice_worker::NetdeviceWorker::new(ns.ctx.clone(), device).await?;
     let handler = worker.new_handler();
     let worker_fut = worker.run().map_err(DeviceControlError::Worker);
-    let (stop_trigger, stop_fut) = futures::channel::oneshot::channel::<()>();
-    let stop_fut = stop_fut.map(|r| r.expect("closed all cancellation senders")).shared();
+    let stop_event = async_utils::event::Event::new();
     let control_stream = device_control
-        .take_until(stop_fut.clone())
+        .take_until(stop_event.wait_or_dropped())
         .map_err(DeviceControlError::Fidl)
         .try_filter_map(|req| match req {
             fnet_interfaces_admin::DeviceControlRequest::CreateInterface {
@@ -66,7 +65,14 @@
                 control,
                 options,
                 control_handle: _,
-            } => create_interface(port, control, options, &ns, &handler, &stop_fut),
+            } => create_interface(
+                port,
+                control,
+                options,
+                &ns,
+                &handler,
+                stop_event.wait_or_dropped(),
+            ),
             fnet_interfaces_admin::DeviceControlRequest::Detach { control_handle: _ } => {
                 todo!("https://fxbug.dev/100867 support detach");
             }
@@ -99,7 +105,7 @@
     };
 
     // Send a stop signal to all tasks.
-    stop_trigger.send(()).expect("receiver should not be gone");
+    assert!(stop_event.signal(), "event was already signaled");
     match &res {
         // Control stream has finished, don't need to drain it.
         Ok(()) | Err(DeviceControlError::Fidl(_)) => (),
@@ -131,11 +137,7 @@
     options: fnet_interfaces_admin::Options,
     ns: &Netstack,
     handler: &netdevice_worker::DeviceHandler,
-    stop_fut: &(impl futures::Future<Output = ()>
-          + futures::future::FusedFuture
-          + Clone
-          + Send
-          + 'static),
+    stop_fut: async_utils::event::EventWaitResult,
 ) -> Result<Option<fuchsia_async::Task<()>>, DeviceControlError> {
     log::debug!("creating interface from {:?} with {:?}", port, options);
     let fnet_interfaces_admin::Options { name, metric: _, .. } = options;
@@ -143,7 +145,7 @@
         Ok((binding_id, status_stream)) => Ok(Some(fasync::Task::spawn(run_interface_control(
             ns.ctx.clone(),
             binding_id,
-            stop_fut.clone(),
+            stop_fut,
             status_stream,
             control,
         )))),
@@ -202,12 +204,11 @@
 }
 
 async fn run_interface_control<
-    F: Send + 'static + futures::Future<Output = ()> + futures::future::FusedFuture,
     S: futures::Stream<Item = netdevice_client::Result<netdevice_client::client::PortStatus>>,
 >(
     ctx: NetstackContext,
     id: BindingId,
-    cancel: F,
+    cancel: async_utils::event::EventWaitResult,
     status_stream: S,
     server_end: fidl::endpoints::ServerEnd<fnet_interfaces_admin::ControlMarker>,
 ) {
@@ -302,7 +303,7 @@
     futures::pin_mut!(link_state_fut);
     let outcome = futures::select! {
         o = stream_fut => Outcome::StreamEnded(o),
-        () = cancel => Outcome::Cancelled,
+        o = cancel => {o.expect("event was orphaned"); Outcome::Cancelled},
         o = link_state_fut => Outcome::StateStreamEnded(o),
     };
     let remove_reason = match outcome {
diff --git a/src/connectivity/network/netstack3/src/bindings/mod.rs b/src/connectivity/network/netstack3/src/bindings/mod.rs
index 1fd11f5..bece90b 100644
--- a/src/connectivity/network/netstack3/src/bindings/mod.rs
+++ b/src/connectivity/network/netstack3/src/bindings/mod.rs
@@ -56,7 +56,7 @@
 use net_types::ip::{AddrSubnet, AddrSubnetEither, Ip, Ipv4, Ipv6};
 use netstack3_core::{
     add_ip_addr_subnet, add_route,
-    context::{EventContext, InstantContext, RngContext, TimerContext},
+    context::{CounterContext, EventContext, InstantContext, RngContext, TimerContext},
     handle_timer, icmp, update_ipv4_configuration, update_ipv6_configuration, AddableEntryEither,
     BufferUdpContext, Ctx, DeviceId, DeviceLayerEventDispatcher, IpDeviceConfiguration, IpExt,
     Ipv4DeviceConfiguration, Ipv6DeviceConfiguration, NonSyncContext, SlaacConfiguration, TimerId,
@@ -243,6 +243,10 @@
     }
 }
 
+impl CounterContext for BindingsNonSyncCtxImpl {
+    fn increment_counter(&mut self, _key: &'static str) {}
+}
+
 impl RngContext for BindingsNonSyncCtxImpl {
     type Rng = OsRng;
 
diff --git a/src/developer/ffx/plugins/agis/src/args.rs b/src/developer/ffx/plugins/agis/src/args.rs
index 3e3f660..ea6db25 100644
--- a/src/developer/ffx/plugins/agis/src/args.rs
+++ b/src/developer/ffx/plugins/agis/src/args.rs
@@ -16,7 +16,6 @@
 #[argh(subcommand)]
 pub enum Operation {
     Register(RegisterOp),
-    Unregister(UnregisterOp),
     Vtcs(VtcsOp),
 }
 
@@ -32,12 +31,6 @@
     #[argh(positional, description = "process name")]
     pub process_name: String,
 }
-#[derive(FromArgs, PartialEq, Debug)]
-#[argh(subcommand, name = "unregister", description = "unregister a process from AGIS")]
-pub struct UnregisterOp {
-    #[argh(positional, description = "id")]
-    pub id: u64,
-}
 
 #[derive(FromArgs, PartialEq, Debug)]
 #[argh(subcommand, name = "vtcs", description = "list all vulkan traceable components")]
diff --git a/src/developer/ffx/plugins/agis/src/lib.rs b/src/developer/ffx/plugins/agis/src/lib.rs
index 73406f4..f353c60 100644
--- a/src/developer/ffx/plugins/agis/src/lib.rs
+++ b/src/developer/ffx/plugins/agis/src/lib.rs
@@ -5,27 +5,26 @@
 use {
     anyhow::{anyhow, Result},
     errors::ffx_error,
-    ffx_agis_args::{AgisCommand, Operation, RegisterOp, UnregisterOp},
+    ffx_agis_args::{AgisCommand, Operation, RegisterOp},
     ffx_core::ffx_plugin,
     fidl_fuchsia_gpu_agis::ComponentRegistryProxy,
     fidl_fuchsia_gpu_agis::ObserverProxy,
     serde::Serialize,
 };
 
+const GLOBAL_ID: u32 = 1;
+
 #[derive(Serialize, Debug)]
 // Vtc == Vulkan Traceable Component
 struct Vtc {
+    global_id: u32,
     process_koid: u64,
     process_name: String,
-
-    #[serde(skip)]
-    agi_socket: fidl::Socket,
 }
 
 #[derive(PartialEq)]
 struct VtcsResult {
     json: serde_json::Value,
-    agi_sockets: Vec<fidl::Socket>,
 }
 
 impl std::fmt::Display for VtcsResult {
@@ -37,15 +36,15 @@
 impl Vtc {
     fn from_fidl(fidl_vtc: fidl_fuchsia_gpu_agis::Vtc) -> Result<Vtc, anyhow::Error> {
         Ok(Vtc {
+            global_id: fidl_vtc.global_id.ok_or_else(|| {
+                anyhow!(ffx_error!("\"agis\" service error. The \"global_id\" is missing."))
+            })?,
             process_koid: fidl_vtc.process_koid.ok_or_else(|| {
                 anyhow!(ffx_error!("\"agis\" service error. The \"process_koid\" is missing."))
             })?,
             process_name: fidl_vtc.process_name.ok_or_else(|| {
                 anyhow!(ffx_error!("\"agis\" service error. The \"process_name\" is missing."))
             })?,
-            agi_socket: fidl_vtc.agi_socket.ok_or_else(|| {
-                anyhow!(ffx_error!("\"agis\" service error. The \"agis_socket\" is missing."))
-            })?,
         })
     }
 }
@@ -73,11 +72,13 @@
     let result = component_registry.register(op.id, op.process_koid, &op.process_name).await?;
     match result {
         Ok(_) => {
-            // Create an arbitrary, valid socket to test as a return value.
-            let (s, _) = fidl::Socket::create(fidl::SocketOpts::STREAM).unwrap();
-            let vtc =
-                Vtc { process_koid: op.process_koid, process_name: op.process_name, agi_socket: s };
-            let vtcs_result = VtcsResult { json: serde_json::to_value(&vtc)?, agi_sockets: vec![] };
+            // Create an arbitrary, valid entry to test as a return value.
+            let vtc = Vtc {
+                global_id: GLOBAL_ID,
+                process_koid: op.process_koid,
+                process_name: op.process_name,
+            };
+            let vtcs_result = VtcsResult { json: serde_json::to_value(&vtc)? };
             return Ok(vtcs_result);
         }
         Err(e) => {
@@ -86,43 +87,20 @@
     }
 }
 
-async fn component_registry_unregister(
-    component_registry: ComponentRegistryProxy,
-    op: UnregisterOp,
-) -> Result<VtcsResult, anyhow::Error> {
-    let result = component_registry.unregister(op.id).await?;
-    match result {
-        Ok(_) => {
-            let vtcs_result = VtcsResult { json: serde_json::json!([{}]), agi_sockets: vec![] };
-            return Ok(vtcs_result);
-        }
-        Err(e) => {
-            return Err(anyhow!(ffx_error!(
-                "The \"unregister\" command failed with error: {:?}",
-                e
-            )))
-        }
-    }
-}
-
 async fn observer_vtcs(observer: ObserverProxy) -> Result<VtcsResult, anyhow::Error> {
     let result = observer.vtcs().await?;
     match result {
         Ok(_fidl_vtcs) => {
             let mut vtcs = vec![];
-            let mut agi_sockets = vec![];
             for fidl_vtc in _fidl_vtcs {
                 let vtc = Vtc::from_fidl(fidl_vtc).unwrap();
-                let (s, _) = fidl::Socket::create(fidl::SocketOpts::STREAM).unwrap();
                 vtcs.push(Vtc {
+                    global_id: vtc.global_id,
                     process_name: vtc.process_name,
                     process_koid: vtc.process_koid,
-                    agi_socket: s,
                 });
-                agi_sockets.push(vtc.agi_socket);
             }
-            let vtcs_result =
-                VtcsResult { json: serde_json::to_value(&vtcs)?, agi_sockets: agi_sockets };
+            let vtcs_result = VtcsResult { json: serde_json::to_value(&vtcs)? };
             return Ok(vtcs_result);
         }
         Err(e) => {
@@ -138,7 +116,6 @@
 ) -> Result<VtcsResult, anyhow::Error> {
     match cmd.operation {
         Operation::Register(op) => component_registry_register(component_registry, op).await,
-        Operation::Unregister(op) => component_registry_unregister(component_registry, op).await,
         Operation::Vtcs(_) => observer_vtcs(observer).await,
     }
 }
@@ -157,9 +134,12 @@
         let callback = move |req| {
             match req {
                 ComponentRegistryRequest::Register { responder, .. } => {
+                    responder.send(&mut Ok(())).unwrap();
+                }
+                ComponentRegistryRequest::GetVulkanSocket { responder, .. } => {
                     // Create an arbitrary, valid socket to test as a return value.
                     let (s, _) = fidl::Socket::create(fidl::SocketOpts::STREAM).unwrap();
-                    responder.send(&mut Ok(s)).unwrap();
+                    responder.send(&mut Ok(Some(s))).unwrap();
                 }
                 ComponentRegistryRequest::Unregister { responder, .. } => {
                     responder.send(&mut Ok(())).unwrap();
@@ -174,12 +154,10 @@
             match req {
                 ObserverRequest::Vtcs { responder, .. } => {
                     let mut vtcs = vec![];
-                    // Create an arbitrary, valid socket for use as the |agi_socket|.
-                    let (s, _) = fidl::Socket::create(fidl::SocketOpts::STREAM).unwrap();
                     vtcs.push(fidl_fuchsia_gpu_agis::Vtc {
+                        global_id: Some(GLOBAL_ID),
                         process_koid: Some(PROCESS_KOID),
                         process_name: Some(PROCESS_NAME.to_string()),
-                        agi_socket: Some(s),
                         unknown_data: None,
                         ..fidl_fuchsia_gpu_agis::Vtc::EMPTY
                     });
@@ -220,21 +198,13 @@
     }
 
     #[fuchsia_async::run_singlethreaded(test)]
-    pub async fn unregister() {
-        let cmd = AgisCommand { operation: Operation::Unregister(UnregisterOp { id: 0u64 }) };
-        let component_registry = fake_component_registry();
-        let observer = fake_observer();
-        let result = agis_impl(component_registry, observer, cmd).await;
-        assert_eq!(result.unwrap().json, serde_json::json!([{}]));
-    }
-
-    #[fuchsia_async::run_singlethreaded(test)]
     pub async fn vtcs() {
         let cmd = AgisCommand { operation: Operation::Vtcs(VtcsOp {}) };
         let component_registry = fake_component_registry();
         let observer = fake_observer();
         let result = agis_impl(component_registry, observer, cmd).await;
         let expected_output = serde_json::json!([{
+            "global_id": GLOBAL_ID,
             "process_koid": PROCESS_KOID,
             "process_name": PROCESS_NAME,
         }]);
diff --git a/src/developer/memory/monitor/BUILD.gn b/src/developer/memory/monitor/BUILD.gn
index 384693d..e976ed0 100644
--- a/src/developer/memory/monitor/BUILD.gn
+++ b/src/developer/memory/monitor/BUILD.gn
@@ -25,11 +25,11 @@
     "pressure_observer.h",
   ]
   public_deps = [
-    "//sdk/fidl/fuchsia.cobalt",
     "//sdk/fidl/fuchsia.feedback",
     "//sdk/fidl/fuchsia.hardware.ram.metrics",
     "//sdk/fidl/fuchsia.memory",
     "//sdk/fidl/fuchsia.memorypressure",
+    "//sdk/fidl/fuchsia.metrics",
     "//sdk/lib/sys/cpp",
     "//sdk/lib/sys/inspect/cpp",
     "//sdk/lib/syslog/cpp",
@@ -41,7 +41,7 @@
     ":memory_metrics_registry",
     "//sdk/fidl/fuchsia.kernel:fuchsia.kernel_c",
     "//src/devices/lib/amlogic",
-    "//src/lib/cobalt/cpp:cobalt_event_builder",
+    "//src/lib/cobalt/cpp:metric_event_builder",
     "//src/lib/fsl",
     "//src/lib/fxl",
     "//zircon/system/ulib/async-loop:async-loop-cpp",
diff --git a/src/developer/memory/monitor/meta/memory_monitor.cml b/src/developer/memory/monitor/meta/memory_monitor.cml
index a9ce47e..82dd1b3 100644
--- a/src/developer/memory/monitor/meta/memory_monitor.cml
+++ b/src/developer/memory/monitor/meta/memory_monitor.cml
@@ -22,10 +22,10 @@
     use: [
         {
             protocol: [
-                "fuchsia.cobalt.LoggerFactory",
                 "fuchsia.feedback.CrashReporter",
                 "fuchsia.kernel.RootJobForInspect",
                 "fuchsia.kernel.Stats",
+                "fuchsia.metrics.MetricEventLoggerFactory",
                 "fuchsia.scheduler.ProfileProvider",
                 "fuchsia.tracing.provider.Registry",
             ],
diff --git a/src/developer/memory/monitor/meta/memory_monitor.cmx b/src/developer/memory/monitor/meta/memory_monitor.cmx
index 1d08058..f671b01 100644
--- a/src/developer/memory/monitor/meta/memory_monitor.cmx
+++ b/src/developer/memory/monitor/meta/memory_monitor.cmx
@@ -14,10 +14,10 @@
             "isolated-cache-storage"
         ],
         "services": [
-            "fuchsia.cobalt.LoggerFactory",
             "fuchsia.feedback.CrashReporter",
             "fuchsia.kernel.RootJobForInspect",
             "fuchsia.kernel.Stats",
+            "fuchsia.metrics.MetricEventLoggerFactory",
             "fuchsia.scheduler.ProfileProvider",
             "fuchsia.tracing.provider.Registry"
         ]
diff --git a/src/developer/memory/monitor/meta/memory_monitor.core_shard.cml b/src/developer/memory/monitor/meta/memory_monitor.core_shard.cml
index c967557..737b46f 100644
--- a/src/developer/memory/monitor/meta/memory_monitor.core_shard.cml
+++ b/src/developer/memory/monitor/meta/memory_monitor.core_shard.cml
@@ -32,7 +32,7 @@
             dependency: "weak_for_migration",
         },
         {
-            protocol: "fuchsia.cobalt.LoggerFactory",
+            protocol: "fuchsia.metrics.MetricEventLoggerFactory",
             from: "#cobalt",
             to: "#memory_monitor",
         },
diff --git a/src/developer/memory/monitor/metrics.cc b/src/developer/memory/monitor/metrics.cc
index 93d9286..9de1cb8 100644
--- a/src/developer/memory/monitor/metrics.cc
+++ b/src/developer/memory/monitor/metrics.cc
@@ -12,16 +12,17 @@
 #include <array>
 #include <string>
 
-#include <src/lib/cobalt/cpp/cobalt_event_builder.h>
+#include <src/lib/cobalt/cpp/metric_event_builder.h>
 
+#include "lib/fpromise/result.h"
 #include "src/developer/memory/metrics/digest.h"
 #include "src/developer/memory/metrics/printer.h"
 
 namespace monitor {
 
 using namespace memory;
-using cobalt_registry::MemoryMetricDimensionBucket;
-using TimeSinceBoot = cobalt_registry::MemoryLeakMetricDimensionTimeSinceBoot;
+using cobalt_registry::MemoryMigratedMetricDimensionBucket;
+using TimeSinceBoot = cobalt_registry::MemoryLeakMigratedMetricDimensionTimeSinceBoot;
 
 namespace {
 static const std::map<zx_duration_t, TimeSinceBoot> UptimeLevelMap = {
@@ -41,24 +42,27 @@
 // information about the memory Digests to Cobalt, in the form of several Events.
 Metrics::Metrics(const std::vector<memory::BucketMatch>& bucket_matches,
                  zx::duration poll_frequency, async_dispatcher_t* dispatcher,
-                 sys::ComponentInspector* inspector, fuchsia::cobalt::Logger_Sync* logger,
-                 CaptureCb capture_cb, DigestCb digest_cb)
+                 sys::ComponentInspector* inspector,
+                 fuchsia::metrics::MetricEventLogger_Sync* logger, CaptureCb capture_cb,
+                 DigestCb digest_cb)
     : poll_frequency_(poll_frequency),
       dispatcher_(dispatcher),
       logger_(logger),
       capture_cb_(std::move(capture_cb)),
       digest_cb_(std::move(digest_cb)),
       bucket_name_to_code_({
-          {"TotalBytes", MemoryMetricDimensionBucket::TotalBytes},
-          {"Free", MemoryMetricDimensionBucket::Free},
-          {"Kernel", MemoryMetricDimensionBucket::Kernel},
-          {"Orphaned", MemoryMetricDimensionBucket::Orphaned},
-          {"Undigested", MemoryMetricDimensionBucket::Undigested},
-          {"[Addl]PagerTotal", MemoryMetricDimensionBucket::__Addl_PagerTotal},
-          {"[Addl]PagerNewest", MemoryMetricDimensionBucket::__Addl_PagerNewest},
-          {"[Addl]PagerOldest", MemoryMetricDimensionBucket::__Addl_PagerOldest},
-          {"[Addl]DiscardableLocked", MemoryMetricDimensionBucket::__Addl_DiscardableLocked},
-          {"[Addl]DiscardableUnlocked", MemoryMetricDimensionBucket::__Addl_DiscardableUnlocked},
+          {"TotalBytes", MemoryMigratedMetricDimensionBucket::TotalBytes},
+          {"Free", MemoryMigratedMetricDimensionBucket::Free},
+          {"Kernel", MemoryMigratedMetricDimensionBucket::Kernel},
+          {"Orphaned", MemoryMigratedMetricDimensionBucket::Orphaned},
+          {"Undigested", MemoryMigratedMetricDimensionBucket::Undigested},
+          {"[Addl]PagerTotal", MemoryMigratedMetricDimensionBucket::__Addl_PagerTotal},
+          {"[Addl]PagerNewest", MemoryMigratedMetricDimensionBucket::__Addl_PagerNewest},
+          {"[Addl]PagerOldest", MemoryMigratedMetricDimensionBucket::__Addl_PagerOldest},
+          {"[Addl]DiscardableLocked",
+           MemoryMigratedMetricDimensionBucket::__Addl_DiscardableLocked},
+          {"[Addl]DiscardableUnlocked",
+           MemoryMigratedMetricDimensionBucket::__Addl_DiscardableUnlocked},
       }),
       inspector_(inspector),
       platform_metric_node_(inspector_->root().CreateChild(kInspectPlatformNodeName)),
@@ -83,8 +87,8 @@
     if (!bucket.event_code().has_value()) {
       continue;
     }
-    bucket_name_to_code_.emplace(bucket.name(),
-                                 static_cast<MemoryMetricDimensionBucket>(*bucket.event_code()));
+    bucket_name_to_code_.emplace(
+        bucket.name(), static_cast<MemoryMigratedMetricDimensionBucket>(*bucket.event_code()));
   }
   for (const auto& element : bucket_name_to_code_) {
     inspect_memory_usages_.insert(std::pair<std::string, inspect::UintProperty>(
@@ -103,11 +107,11 @@
 
   WriteDigestToInspect(digest);
 
-  std::vector<fuchsia::cobalt::CobaltEvent> events;
+  std::vector<fuchsia::metrics::MetricEvent> events;
   const auto& kmem = capture.kmem();
   AddKmemEvents(kmem, &events);
   AddKmemEventsWithUptime(kmem, capture.time(), &events);
-  auto builder = cobalt::CobaltEventBuilder(cobalt_registry::kMemoryMetricId);
+  auto builder = cobalt::MetricEventBuilder(cobalt_registry::kMemoryMigratedMetricId);
   for (const auto& bucket : digest.buckets()) {
     if (bucket.size() == 0) {
       continue;
@@ -118,13 +122,13 @@
                                 << bucket.name();
       continue;
     }
-    events.push_back(
-        builder.Clone().with_event_code(code_iter->second).as_memory_usage(bucket.size()));
+    events.push_back(builder.Clone().with_event_code(code_iter->second).as_integer(bucket.size()));
   }
-  fuchsia::cobalt::Status status = fuchsia::cobalt::Status::INTERNAL_ERROR;
-  logger_->LogCobaltEvents(std::move(events), &status);
-  if (status == fuchsia::cobalt::Status::INVALID_ARGUMENTS) {
-    FX_LOGS(ERROR) << "LogCobaltEvents() returned status INVALID_ARGUMENTS";
+  fuchsia::metrics::MetricEventLogger_LogMetricEvents_Result response =
+      fpromise::error(fuchsia::metrics::Error::INTERNAL_ERROR);
+  logger_->LogMetricEvents(std::move(events), &response);
+  if (response.is_err() && response.err() == fuchsia::metrics::Error::INVALID_ARGUMENTS) {
+    FX_LOGS(ERROR) << "LogMetricEvents() returned status INVALID_ARGUMENTS";
   }
   task_.PostDelayed(dispatcher_, poll_frequency_);
 }
@@ -147,72 +151,69 @@
 }
 
 void Metrics::AddKmemEvents(const zx_info_kmem_stats_t& kmem,
-                            std::vector<fuchsia::cobalt::CobaltEvent>* events) {
+                            std::vector<fuchsia::metrics::MetricEvent>* events) {
   TRACE_DURATION("memory_monitor", "Metrics::AddKmemEvents");
-  auto builder = cobalt::CobaltEventBuilder(cobalt_registry::kMemoryGeneralBreakdownMetricId);
-  using Breakdown = cobalt_registry::MemoryGeneralBreakdownMetricDimensionGeneralBreakdown;
+  auto builder =
+      cobalt::MetricEventBuilder(cobalt_registry::kMemoryGeneralBreakdownMigratedMetricId);
+  using Breakdown = cobalt_registry::MemoryGeneralBreakdownMigratedMetricDimensionGeneralBreakdown;
   events->push_back(
-      builder.Clone().with_event_code(Breakdown::TotalBytes).as_memory_usage(kmem.total_bytes));
+      builder.Clone().with_event_code(Breakdown::TotalBytes).as_integer(kmem.total_bytes));
   events->push_back(builder.Clone()
                         .with_event_code(Breakdown::UsedBytes)
-                        .as_memory_usage(kmem.total_bytes - kmem.free_bytes));
+                        .as_integer(kmem.total_bytes - kmem.free_bytes));
   events->push_back(
-      builder.Clone().with_event_code(Breakdown::FreeBytes).as_memory_usage(kmem.free_bytes));
+      builder.Clone().with_event_code(Breakdown::FreeBytes).as_integer(kmem.free_bytes));
   events->push_back(
-      builder.Clone().with_event_code(Breakdown::VmoBytes).as_memory_usage(kmem.vmo_bytes));
+      builder.Clone().with_event_code(Breakdown::VmoBytes).as_integer(kmem.vmo_bytes));
   events->push_back(builder.Clone()
                         .with_event_code(Breakdown::KernelFreeHeapBytes)
-                        .as_memory_usage(kmem.free_heap_bytes));
-  events->push_back(builder.Clone()
-                        .with_event_code(Breakdown::MmuBytes)
-                        .as_memory_usage(kmem.mmu_overhead_bytes));
+                        .as_integer(kmem.free_heap_bytes));
   events->push_back(
-      builder.Clone().with_event_code(Breakdown::IpcBytes).as_memory_usage(kmem.ipc_bytes));
+      builder.Clone().with_event_code(Breakdown::MmuBytes).as_integer(kmem.mmu_overhead_bytes));
+  events->push_back(
+      builder.Clone().with_event_code(Breakdown::IpcBytes).as_integer(kmem.ipc_bytes));
   events->push_back(builder.Clone()
                         .with_event_code(Breakdown::KernelTotalHeapBytes)
-                        .as_memory_usage(kmem.total_heap_bytes));
+                        .as_integer(kmem.total_heap_bytes));
   events->push_back(
-      builder.Clone().with_event_code(Breakdown::WiredBytes).as_memory_usage(kmem.wired_bytes));
+      builder.Clone().with_event_code(Breakdown::WiredBytes).as_integer(kmem.wired_bytes));
   events->push_back(
-      builder.Clone().with_event_code(Breakdown::OtherBytes).as_memory_usage(kmem.other_bytes));
+      builder.Clone().with_event_code(Breakdown::OtherBytes).as_integer(kmem.other_bytes));
 }
 
 // TODO(fxbug.dev/3778): Refactor this when dedup enum is availble in generated
 // cobalt config source code.
 void Metrics::AddKmemEventsWithUptime(const zx_info_kmem_stats_t& kmem,
                                       const zx_time_t capture_time,
-                                      std::vector<fuchsia::cobalt::CobaltEvent>* events) {
+                                      std::vector<fuchsia::metrics::MetricEvent>* events) {
   TRACE_DURATION("memory_monitor", "Metrics::AddKmemEventsWithUptime");
-  auto builder = std::move(cobalt::CobaltEventBuilder(cobalt_registry::kMemoryLeakMetricId)
+  auto builder = std::move(cobalt::MetricEventBuilder(cobalt_registry::kMemoryLeakMigratedMetricId)
                                .with_event_code_at(1, GetUpTimeEventCode(capture_time)));
-  using Breakdown = cobalt_registry::MemoryLeakMetricDimensionGeneralBreakdown;
-  events->push_back(builder.Clone()
-                        .with_event_code_at(0, Breakdown::TotalBytes)
-                        .as_memory_usage(kmem.total_bytes));
+  using Breakdown = cobalt_registry::MemoryLeakMigratedMetricDimensionGeneralBreakdown;
+  events->push_back(
+      builder.Clone().with_event_code_at(0, Breakdown::TotalBytes).as_integer(kmem.total_bytes));
   events->push_back(builder.Clone()
                         .with_event_code_at(0, Breakdown::UsedBytes)
-                        .as_memory_usage(kmem.total_bytes - kmem.free_bytes));
+                        .as_integer(kmem.total_bytes - kmem.free_bytes));
   events->push_back(
-      builder.Clone().with_event_code_at(0, Breakdown::FreeBytes).as_memory_usage(kmem.free_bytes));
+      builder.Clone().with_event_code_at(0, Breakdown::FreeBytes).as_integer(kmem.free_bytes));
   events->push_back(
-      builder.Clone().with_event_code_at(0, Breakdown::VmoBytes).as_memory_usage(kmem.vmo_bytes));
+      builder.Clone().with_event_code_at(0, Breakdown::VmoBytes).as_integer(kmem.vmo_bytes));
   events->push_back(builder.Clone()
                         .with_event_code_at(0, Breakdown::KernelFreeHeapBytes)
-                        .as_memory_usage(kmem.free_heap_bytes));
+                        .as_integer(kmem.free_heap_bytes));
   events->push_back(builder.Clone()
                         .with_event_code_at(0, Breakdown::MmuBytes)
-                        .as_memory_usage(kmem.mmu_overhead_bytes));
+                        .as_integer(kmem.mmu_overhead_bytes));
   events->push_back(
-      builder.Clone().with_event_code_at(0, Breakdown::IpcBytes).as_memory_usage(kmem.ipc_bytes));
+      builder.Clone().with_event_code_at(0, Breakdown::IpcBytes).as_integer(kmem.ipc_bytes));
   events->push_back(builder.Clone()
                         .with_event_code_at(0, Breakdown::KernelTotalHeapBytes)
-                        .as_memory_usage(kmem.total_heap_bytes));
-  events->push_back(builder.Clone()
-                        .with_event_code_at(0, Breakdown::WiredBytes)
-                        .as_memory_usage(kmem.wired_bytes));
-  events->push_back(builder.Clone()
-                        .with_event_code_at(0, Breakdown::OtherBytes)
-                        .as_memory_usage(kmem.other_bytes));
+                        .as_integer(kmem.total_heap_bytes));
+  events->push_back(
+      builder.Clone().with_event_code_at(0, Breakdown::WiredBytes).as_integer(kmem.wired_bytes));
+  events->push_back(
+      builder.Clone().with_event_code_at(0, Breakdown::OtherBytes).as_integer(kmem.other_bytes));
 }
 
 TimeSinceBoot Metrics::GetUpTimeEventCode(const zx_time_t capture_time) {
diff --git a/src/developer/memory/monitor/metrics.h b/src/developer/memory/monitor/metrics.h
index ae0d0a9..5b655e2 100644
--- a/src/developer/memory/monitor/metrics.h
+++ b/src/developer/memory/monitor/metrics.h
@@ -5,7 +5,7 @@
 #ifndef SRC_DEVELOPER_MEMORY_MONITOR_METRICS_H_
 #define SRC_DEVELOPER_MEMORY_MONITOR_METRICS_H_
 
-#include <fuchsia/cobalt/cpp/fidl.h>
+#include <fuchsia/metrics/cpp/fidl.h>
 #include <lib/async/cpp/task.h>
 #include <lib/async/dispatcher.h>
 #include <lib/sys/inspect/cpp/component.h>
@@ -28,7 +28,8 @@
   using DigestCb = fit::function<void(const memory::Capture&, memory::Digest*)>;
   Metrics(const std::vector<memory::BucketMatch>& bucket_matches, zx::duration poll_frequency,
           async_dispatcher_t* dispatcher, sys::ComponentInspector* inspector,
-          fuchsia::cobalt::Logger_Sync* logger, CaptureCb capture_cb, DigestCb digest_cb);
+          fuchsia::metrics::MetricEventLogger_Sync* logger, CaptureCb capture_cb,
+          DigestCb digest_cb);
 
   // Allow monitor to update the memory bandwidth readings
   // once a second to metrics
@@ -50,19 +51,19 @@
   void CollectMetrics();
   void WriteDigestToInspect(const memory::Digest& digest);
   void AddKmemEvents(const zx_info_kmem_stats_t& kmem,
-                     std::vector<fuchsia::cobalt::CobaltEvent>* events);
+                     std::vector<fuchsia::metrics::MetricEvent>* events);
   void AddKmemEventsWithUptime(const zx_info_kmem_stats_t& kmem, zx_time_t capture_time,
-                               std::vector<fuchsia::cobalt::CobaltEvent>* events);
-  cobalt_registry::MemoryLeakMetricDimensionTimeSinceBoot GetUpTimeEventCode(
+                               std::vector<fuchsia::metrics::MetricEvent>* events);
+  cobalt_registry::MemoryLeakMigratedMetricDimensionTimeSinceBoot GetUpTimeEventCode(
       const zx_time_t current);
 
   zx::duration poll_frequency_;
   async_dispatcher_t* dispatcher_;
-  fuchsia::cobalt::Logger_Sync* logger_;
+  fuchsia::metrics::MetricEventLogger_Sync* logger_;
   CaptureCb capture_cb_;
   DigestCb digest_cb_;
   async::TaskClosureMethod<Metrics, &Metrics::CollectMetrics> task_{this};
-  std::unordered_map<std::string, cobalt_registry::MemoryMetricDimensionBucket>
+  std::unordered_map<std::string, cobalt_registry::MemoryMigratedMetricDimensionBucket>
       bucket_name_to_code_;
 
   // The component inspector to publish data to.
diff --git a/src/developer/memory/monitor/monitor.cc b/src/developer/memory/monitor/monitor.cc
index 24fdd1c..41b20c6 100644
--- a/src/developer/memory/monitor/monitor.cc
+++ b/src/developer/memory/monitor/monitor.cc
@@ -26,11 +26,13 @@
 #include <memory>
 #include <mutex>
 #include <sstream>
+#include <utility>
 
 #include <soc/aml-common/aml-ram.h>
 #include <trace-vthread/event_vthread.h>
 
 #include "fuchsia/mem/cpp/fidl.h"
+#include "lib/fpromise/result.h"
 #include "src/developer/memory/metrics/bucket_match.h"
 #include "src/developer/memory/metrics/capture.h"
 #include "src/developer/memory/metrics/printer.h"
@@ -237,25 +239,29 @@
 }
 
 void Monitor::CreateMetrics(const std::vector<memory::BucketMatch>& bucket_matches) {
-  // Connect to the cobalt fidl service provided by the environment.
-  fuchsia::cobalt::LoggerFactorySyncPtr factory;
+  // Connect to the metrics fidl service provided by the environment.
+  fuchsia::metrics::MetricEventLoggerFactorySyncPtr factory;
   component_context_->svc()->Connect(factory.NewRequest());
   if (!factory) {
-    FX_LOGS(ERROR) << "Unable to get cobalt.LoggerFactory.";
+    FX_LOGS(ERROR) << "Unable to get metrics.MetricEventLoggerFactory.";
     return;
   }
-  // Create a Cobalt Logger. The ID name is the one we specified in the
+  // Create a Metric Event Logger. The ID name is the one we specified in the
   // Cobalt metrics registry.
-  fuchsia::cobalt::Status status = fuchsia::cobalt::Status::INTERNAL_ERROR;
-  factory->CreateLoggerFromProjectId(cobalt_registry::kProjectId, cobalt_logger_.NewRequest(),
-                                     &status);
-  if (status != fuchsia::cobalt::Status::OK) {
-    FX_LOGS(ERROR) << "Unable to get cobalt.Logger from factory.";
+  fuchsia::metrics::ProjectSpec project_spec;
+  project_spec.set_customer_id(cobalt_registry::kCustomerId);
+  project_spec.set_project_id(cobalt_registry::kProjectId);
+  fuchsia::metrics::MetricEventLoggerFactory_CreateMetricEventLogger_Result result =
+      fpromise::error(fuchsia::metrics::Error::INTERNAL_ERROR);
+  factory->CreateMetricEventLogger(std::move(project_spec), metric_event_logger_.NewRequest(),
+                                   &result);
+  if (result.is_err()) {
+    FX_LOGS(ERROR) << "Unable to get metrics.Logger from factory.";
     return;
   }
 
   metrics_ = std::make_unique<Metrics>(
-      bucket_matches, kMetricsPollFrequency, dispatcher_, &inspector_, cobalt_logger_.get(),
+      bucket_matches, kMetricsPollFrequency, dispatcher_, &inspector_, metric_event_logger_.get(),
       [this](Capture* c) { return GetCapture(c); },
       [this](const Capture& c, Digest* d) { GetDigest(c, d); });
 }
diff --git a/src/developer/memory/monitor/monitor.h b/src/developer/memory/monitor/monitor.h
index f39fe13..088058c 100644
--- a/src/developer/memory/monitor/monitor.h
+++ b/src/developer/memory/monitor/monitor.h
@@ -87,7 +87,7 @@
   zx_handle_t root_;
   async_dispatcher_t* dispatcher_;
   std::unique_ptr<sys::ComponentContext> component_context_;
-  fuchsia::cobalt::LoggerSyncPtr cobalt_logger_;
+  fuchsia::metrics::MetricEventLoggerSyncPtr metric_event_logger_;
   fidl::BindingSet<fuchsia::memory::Monitor> bindings_;
   std::vector<fuchsia::memory::WatcherPtr> watchers_;
   trace::TraceObserver trace_observer_;
diff --git a/src/developer/memory/monitor/tests/BUILD.gn b/src/developer/memory/monitor/tests/BUILD.gn
index a409eab..30a4d3e 100644
--- a/src/developer/memory/monitor/tests/BUILD.gn
+++ b/src/developer/memory/monitor/tests/BUILD.gn
@@ -30,7 +30,7 @@
     "//sdk/fidl/fuchsia.hardware.ram.metrics:fuchsia.hardware.ram.metrics",
     "//sdk/lib/inspect/testing/cpp",
     "//sdk/lib/sys/cpp/testing:unit",
-    "//src/cobalt/bin/testing:fake_logger_lib",
+    "//src/cobalt/bin/testing:stub_metric_event_logger_lib",
     "//src/developer/memory/metrics/tests:utils",
     "//src/developer/memory/monitor:lib",
     "//src/lib/fxl/test:gtest_main",
diff --git a/src/developer/memory/monitor/tests/metrics_unittest.cc b/src/developer/memory/monitor/tests/metrics_unittest.cc
index 943df16..8bef88a 100644
--- a/src/developer/memory/monitor/tests/metrics_unittest.cc
+++ b/src/developer/memory/monitor/tests/metrics_unittest.cc
@@ -10,7 +10,7 @@
 #include <zircon/time.h>
 
 #include <gtest/gtest.h>
-#include <src/cobalt/bin/testing/fake_logger.h>
+#include <src/cobalt/bin/testing/stub_metric_event_logger.h>
 
 #include "src/developer/memory/metrics/capture.h"
 #include "src/developer/memory/metrics/tests/test_utils.h"
@@ -18,42 +18,43 @@
 
 using namespace memory;
 
-using cobalt_registry::MemoryMetricDimensionBucket;
-using fuchsia::cobalt::EventPayload;
+using cobalt_registry::MemoryMigratedMetricDimensionBucket;
 
 namespace monitor {
 namespace {
 const std::vector<BucketMatch> kBucketMatches = {
-    {"ZBI Buffer", ".*", "uncompressed-bootfs", MemoryMetricDimensionBucket::ZbiBuffer},
+    {"ZBI Buffer", ".*", "uncompressed-bootfs", MemoryMigratedMetricDimensionBucket::ZbiBuffer},
     // Memory used with the GPU or display hardware.
     {"Graphics", ".*",
      "magma_create_buffer|Mali "
      ".*|Magma.*|ImagePipe2Surface.*|GFXBufferCollection.*|ScenicImageMemory|Display.*|"
      "CompactImage.*|GFX Device Memory.*",
-     MemoryMetricDimensionBucket::Graphics},
+     MemoryMigratedMetricDimensionBucket::Graphics},
     // Unused protected pool memory.
     {"ProtectedPool", "driver_host:.*", "SysmemAmlogicProtectedPool",
-     MemoryMetricDimensionBucket::ProtectedPool},
+     MemoryMigratedMetricDimensionBucket::ProtectedPool},
     // Unused contiguous pool memory.
     {"ContiguousPool", "driver_host:.*", "SysmemContiguousPool",
-     MemoryMetricDimensionBucket::ContiguousPool},
-    {"Fshost", "fshost.cm", ".*", MemoryMetricDimensionBucket::Fshost},
-    {"Minfs", ".*minfs", ".*", MemoryMetricDimensionBucket::Minfs},
-    {"BlobfsInactive", ".*blobfs", "inactive-blob-.*", MemoryMetricDimensionBucket::BlobfsInactive},
-    {"Blobfs", ".*blobfs", ".*", MemoryMetricDimensionBucket::Blobfs},
-    {"FlutterApps", "io\\.flutter\\..*", "dart.*", MemoryMetricDimensionBucket::FlutterApps},
-    {"Flutter", "io\\.flutter\\..*", ".*", MemoryMetricDimensionBucket::Flutter},
-    {"Web", "web_engine_exe:.*", ".*", MemoryMetricDimensionBucket::Web},
-    {"Kronk", "kronk.cmx|kronk_for_testing.cmx", ".*", MemoryMetricDimensionBucket::Kronk},
-    {"Scenic", "scenic.cmx", ".*", MemoryMetricDimensionBucket::Scenic},
-    {"Amlogic", "driver_host:pdev:05:00:f", ".*", MemoryMetricDimensionBucket::Amlogic},
-    {"Netstack", "netstack.cmx", ".*", MemoryMetricDimensionBucket::Netstack},
-    {"Pkgfs", "pkgfs", ".*", MemoryMetricDimensionBucket::Pkgfs},
-    {"Cast", "cast_agent.cmx", ".*", MemoryMetricDimensionBucket::Cast},
-    {"Archivist", "archivist.cm", ".*", MemoryMetricDimensionBucket::Archivist},
-    {"Cobalt", "cobalt.cm", ".*", MemoryMetricDimensionBucket::Cobalt},
-    {"Audio", "audio_core.cmx", ".*", MemoryMetricDimensionBucket::Audio},
-    {"Context", "context_provider.cmx", ".*", MemoryMetricDimensionBucket::Context},
+     MemoryMigratedMetricDimensionBucket::ContiguousPool},
+    {"Fshost", "fshost.cm", ".*", MemoryMigratedMetricDimensionBucket::Fshost},
+    {"Minfs", ".*minfs", ".*", MemoryMigratedMetricDimensionBucket::Minfs},
+    {"BlobfsInactive", ".*blobfs", "inactive-blob-.*",
+     MemoryMigratedMetricDimensionBucket::BlobfsInactive},
+    {"Blobfs", ".*blobfs", ".*", MemoryMigratedMetricDimensionBucket::Blobfs},
+    {"FlutterApps", "io\\.flutter\\..*", "dart.*",
+     MemoryMigratedMetricDimensionBucket::FlutterApps},
+    {"Flutter", "io\\.flutter\\..*", ".*", MemoryMigratedMetricDimensionBucket::Flutter},
+    {"Web", "web_engine_exe:.*", ".*", MemoryMigratedMetricDimensionBucket::Web},
+    {"Kronk", "kronk.cmx|kronk_for_testing.cmx", ".*", MemoryMigratedMetricDimensionBucket::Kronk},
+    {"Scenic", "scenic.cmx", ".*", MemoryMigratedMetricDimensionBucket::Scenic},
+    {"Amlogic", "driver_host:pdev:05:00:f", ".*", MemoryMigratedMetricDimensionBucket::Amlogic},
+    {"Netstack", "netstack.cmx", ".*", MemoryMigratedMetricDimensionBucket::Netstack},
+    {"Pkgfs", "pkgfs", ".*", MemoryMigratedMetricDimensionBucket::Pkgfs},
+    {"Cast", "cast_agent.cmx", ".*", MemoryMigratedMetricDimensionBucket::Cast},
+    {"Archivist", "archivist.cm", ".*", MemoryMigratedMetricDimensionBucket::Archivist},
+    {"Cobalt", "cobalt.cm", ".*", MemoryMigratedMetricDimensionBucket::Cobalt},
+    {"Audio", "audio_core.cmx", ".*", MemoryMigratedMetricDimensionBucket::Audio},
+    {"Context", "context_provider.cmx", ".*", MemoryMigratedMetricDimensionBucket::Context},
 };
 
 class MetricsUnitTest : public gtest::RealLoopFixture {
@@ -139,7 +140,7 @@
 
 TEST_F(MetricsUnitTest, Inspect) {
   CaptureSupplier cs(Template());
-  cobalt::FakeLogger_Sync logger;
+  cobalt::StubMetricEventLogger_Sync logger;
   sys::ComponentInspector inspector(context_provider_.context());
   Metrics m(
       kBucketMatches, zx::min(5), dispatcher(), &inspector, &logger,
@@ -170,7 +171,7 @@
 
 TEST_F(MetricsUnitTest, All) {
   CaptureSupplier cs(Template());
-  cobalt::FakeLogger_Sync logger;
+  cobalt::StubMetricEventLogger_Sync logger;
   sys::ComponentInspector inspector(context_provider_.context());
   Metrics m(
       kBucketMatches, zx::msec(10), dispatcher(), &inspector, &logger,
@@ -182,132 +183,133 @@
   // memory_leak metric: 10
   // = 44
   EXPECT_EQ(44U, logger.logged_events().size());
-  using Breakdown = cobalt_registry::MemoryGeneralBreakdownMetricDimensionGeneralBreakdown;
-  using Breakdown2 = cobalt_registry::MemoryLeakMetricDimensionGeneralBreakdown;
-  for (const auto& cobalt_event : logger.logged_events()) {
-    EXPECT_EQ(EventPayload::Tag::kMemoryBytesUsed, cobalt_event.payload.Which());
-    switch (cobalt_event.metric_id) {
-      case cobalt_registry::kMemoryMetricId:
-        ASSERT_EQ(1u, cobalt_event.event_codes.size());
-        switch (cobalt_event.event_codes[0]) {
-          case MemoryMetricDimensionBucket::ZbiBuffer:
-            EXPECT_EQ(1u, cobalt_event.payload.memory_bytes_used());
+  using Breakdown = cobalt_registry::MemoryGeneralBreakdownMigratedMetricDimensionGeneralBreakdown;
+  using Breakdown2 = cobalt_registry::MemoryLeakMigratedMetricDimensionGeneralBreakdown;
+  for (const auto& metric_event : logger.logged_events()) {
+    EXPECT_EQ(fuchsia::metrics::MetricEventPayload::Tag::kIntegerValue,
+              metric_event.payload.Which());
+    switch (metric_event.metric_id) {
+      case cobalt_registry::kMemoryMigratedMetricId:
+        ASSERT_EQ(1u, metric_event.event_codes.size());
+        switch (metric_event.event_codes[0]) {
+          case MemoryMigratedMetricDimensionBucket::ZbiBuffer:
+            EXPECT_EQ(1u, metric_event.payload.integer_value());
             break;
-          case MemoryMetricDimensionBucket::Graphics:
-            EXPECT_EQ(2u, cobalt_event.payload.memory_bytes_used());
+          case MemoryMigratedMetricDimensionBucket::Graphics:
+            EXPECT_EQ(2u, metric_event.payload.integer_value());
             break;
-          case MemoryMetricDimensionBucket::ProtectedPool:
-            EXPECT_EQ(3u, cobalt_event.payload.memory_bytes_used());
+          case MemoryMigratedMetricDimensionBucket::ProtectedPool:
+            EXPECT_EQ(3u, metric_event.payload.integer_value());
             break;
-          case MemoryMetricDimensionBucket::ContiguousPool:
-            EXPECT_EQ(4u, cobalt_event.payload.memory_bytes_used());
+          case MemoryMigratedMetricDimensionBucket::ContiguousPool:
+            EXPECT_EQ(4u, metric_event.payload.integer_value());
             break;
-          case MemoryMetricDimensionBucket::Fshost:
-            EXPECT_EQ(5u, cobalt_event.payload.memory_bytes_used());
+          case MemoryMigratedMetricDimensionBucket::Fshost:
+            EXPECT_EQ(5u, metric_event.payload.integer_value());
             break;
-          case MemoryMetricDimensionBucket::Minfs:
-            EXPECT_EQ(6u, cobalt_event.payload.memory_bytes_used());
+          case MemoryMigratedMetricDimensionBucket::Minfs:
+            EXPECT_EQ(6u, metric_event.payload.integer_value());
             break;
-          case MemoryMetricDimensionBucket::Blobfs:
-            EXPECT_EQ(7u, cobalt_event.payload.memory_bytes_used());
+          case MemoryMigratedMetricDimensionBucket::Blobfs:
+            EXPECT_EQ(7u, metric_event.payload.integer_value());
             break;
-          case MemoryMetricDimensionBucket::FlutterApps:
-            EXPECT_EQ(8u, cobalt_event.payload.memory_bytes_used());
+          case MemoryMigratedMetricDimensionBucket::FlutterApps:
+            EXPECT_EQ(8u, metric_event.payload.integer_value());
             break;
-          case MemoryMetricDimensionBucket::Flutter:
-            EXPECT_EQ(9u, cobalt_event.payload.memory_bytes_used());
+          case MemoryMigratedMetricDimensionBucket::Flutter:
+            EXPECT_EQ(9u, metric_event.payload.integer_value());
             break;
-          case MemoryMetricDimensionBucket::Web:
-            EXPECT_EQ(21u, cobalt_event.payload.memory_bytes_used());
+          case MemoryMigratedMetricDimensionBucket::Web:
+            EXPECT_EQ(21u, metric_event.payload.integer_value());
             break;
-          case MemoryMetricDimensionBucket::Kronk:
-            EXPECT_EQ(12u, cobalt_event.payload.memory_bytes_used());
+          case MemoryMigratedMetricDimensionBucket::Kronk:
+            EXPECT_EQ(12u, metric_event.payload.integer_value());
             break;
-          case MemoryMetricDimensionBucket::Scenic:
-            EXPECT_EQ(13u, cobalt_event.payload.memory_bytes_used());
+          case MemoryMigratedMetricDimensionBucket::Scenic:
+            EXPECT_EQ(13u, metric_event.payload.integer_value());
             break;
-          case MemoryMetricDimensionBucket::Amlogic:
-            EXPECT_EQ(14u, cobalt_event.payload.memory_bytes_used());
+          case MemoryMigratedMetricDimensionBucket::Amlogic:
+            EXPECT_EQ(14u, metric_event.payload.integer_value());
             break;
-          case MemoryMetricDimensionBucket::Netstack:
-            EXPECT_EQ(15u, cobalt_event.payload.memory_bytes_used());
+          case MemoryMigratedMetricDimensionBucket::Netstack:
+            EXPECT_EQ(15u, metric_event.payload.integer_value());
             break;
-          case MemoryMetricDimensionBucket::Pkgfs:
-            EXPECT_EQ(16u, cobalt_event.payload.memory_bytes_used());
+          case MemoryMigratedMetricDimensionBucket::Pkgfs:
+            EXPECT_EQ(16u, metric_event.payload.integer_value());
             break;
-          case MemoryMetricDimensionBucket::Cast:
-            EXPECT_EQ(17u, cobalt_event.payload.memory_bytes_used());
+          case MemoryMigratedMetricDimensionBucket::Cast:
+            EXPECT_EQ(17u, metric_event.payload.integer_value());
             break;
-          case MemoryMetricDimensionBucket::Archivist:
-            EXPECT_EQ(18u, cobalt_event.payload.memory_bytes_used());
+          case MemoryMigratedMetricDimensionBucket::Archivist:
+            EXPECT_EQ(18u, metric_event.payload.integer_value());
             break;
-          case MemoryMetricDimensionBucket::Cobalt:
-            EXPECT_EQ(19u, cobalt_event.payload.memory_bytes_used());
+          case MemoryMigratedMetricDimensionBucket::Cobalt:
+            EXPECT_EQ(19u, metric_event.payload.integer_value());
             break;
-          case MemoryMetricDimensionBucket::Audio:
-            EXPECT_EQ(20u, cobalt_event.payload.memory_bytes_used());
+          case MemoryMigratedMetricDimensionBucket::Audio:
+            EXPECT_EQ(20u, metric_event.payload.integer_value());
             break;
-          case MemoryMetricDimensionBucket::Context:
-            EXPECT_EQ(21u, cobalt_event.payload.memory_bytes_used());
+          case MemoryMigratedMetricDimensionBucket::Context:
+            EXPECT_EQ(21u, metric_event.payload.integer_value());
             break;
-          case MemoryMetricDimensionBucket::Undigested:
-            EXPECT_EQ(22, cobalt_event.payload.memory_bytes_used());
+          case MemoryMigratedMetricDimensionBucket::Undigested:
+            EXPECT_EQ(22, metric_event.payload.integer_value());
             break;
-          case MemoryMetricDimensionBucket::Orphaned:
+          case MemoryMigratedMetricDimensionBucket::Orphaned:
             // 900 kmem.vmo - (1 + 2 + 3 + ... + 22) vmo digested in buckets = 647
-            EXPECT_EQ(647, cobalt_event.payload.memory_bytes_used());
+            EXPECT_EQ(647, metric_event.payload.integer_value());
             break;
-          case MemoryMetricDimensionBucket::Kernel:
+          case MemoryMigratedMetricDimensionBucket::Kernel:
             // 60 wired + 200 total_heap + 60 mmu_overhead + 10 ipc + 20 other = 350
-            EXPECT_EQ(350u, cobalt_event.payload.memory_bytes_used());
+            EXPECT_EQ(350u, metric_event.payload.integer_value());
             break;
-          case MemoryMetricDimensionBucket::Free:
-            EXPECT_EQ(800u, cobalt_event.payload.memory_bytes_used());
+          case MemoryMigratedMetricDimensionBucket::Free:
+            EXPECT_EQ(800u, metric_event.payload.integer_value());
             break;
           default:
             ADD_FAILURE();
             break;
         }
         break;
-      case cobalt_registry::kMemoryGeneralBreakdownMetricId:
-        ASSERT_EQ(1u, cobalt_event.event_codes.size());
-        switch (cobalt_event.event_codes[0]) {
+      case cobalt_registry::kMemoryGeneralBreakdownMigratedMetricId:
+        ASSERT_EQ(1u, metric_event.event_codes.size());
+        switch (metric_event.event_codes[0]) {
           case Breakdown::TotalBytes:
-            EXPECT_EQ(2000u, cobalt_event.payload.memory_bytes_used());
+            EXPECT_EQ(2000u, metric_event.payload.integer_value());
             break;
           case Breakdown::UsedBytes:
-            EXPECT_EQ(1200u, cobalt_event.payload.memory_bytes_used());
+            EXPECT_EQ(1200u, metric_event.payload.integer_value());
             break;
           case Breakdown::VmoBytes:
-            EXPECT_EQ(900u, cobalt_event.payload.memory_bytes_used());
+            EXPECT_EQ(900u, metric_event.payload.integer_value());
             break;
           case Breakdown::FreeBytes:
-            EXPECT_EQ(800u, cobalt_event.payload.memory_bytes_used());
+            EXPECT_EQ(800u, metric_event.payload.integer_value());
             break;
           default:
-            EXPECT_TRUE(cobalt_event.payload.memory_bytes_used() <= 200);
+            EXPECT_TRUE(metric_event.payload.integer_value() <= 200);
             break;
         }
         break;
-      case cobalt_registry::kMemoryLeakMetricId:
-        ASSERT_EQ(2u, cobalt_event.event_codes.size());
-        ASSERT_EQ(cobalt_registry::MemoryLeakMetricDimensionTimeSinceBoot::UpSixHours,
-                  cobalt_event.event_codes[1]);
-        switch (cobalt_event.event_codes[0]) {
+      case cobalt_registry::kMemoryLeakMigratedMetricId:
+        ASSERT_EQ(2u, metric_event.event_codes.size());
+        ASSERT_EQ(cobalt_registry::MemoryLeakMigratedMetricDimensionTimeSinceBoot::UpSixHours,
+                  metric_event.event_codes[1]);
+        switch (metric_event.event_codes[0]) {
           case Breakdown2::TotalBytes:
-            EXPECT_EQ(2000u, cobalt_event.payload.memory_bytes_used());
+            EXPECT_EQ(2000u, metric_event.payload.integer_value());
             break;
           case Breakdown2::UsedBytes:
-            EXPECT_EQ(1200u, cobalt_event.payload.memory_bytes_used());
+            EXPECT_EQ(1200u, metric_event.payload.integer_value());
             break;
           case Breakdown2::VmoBytes:
-            EXPECT_EQ(900u, cobalt_event.payload.memory_bytes_used());
+            EXPECT_EQ(900u, metric_event.payload.integer_value());
             break;
           case Breakdown2::FreeBytes:
-            EXPECT_EQ(800u, cobalt_event.payload.memory_bytes_used());
+            EXPECT_EQ(800u, metric_event.payload.integer_value());
             break;
           default:
-            EXPECT_TRUE(cobalt_event.payload.memory_bytes_used() <= 200);
+            EXPECT_TRUE(metric_event.payload.integer_value() <= 200);
             break;
         }
         break;
@@ -338,7 +340,7 @@
               {.koid = 1, .name = "bin/bootsvc", .vmos = {1}},
           },
   }});
-  cobalt::FakeLogger_Sync logger;
+  cobalt::StubMetricEventLogger_Sync logger;
   sys::ComponentInspector inspector(context_provider_.context());
   Metrics m(
       kBucketMatches, zx::msec(10), dispatcher(), &inspector, &logger,
@@ -373,7 +375,7 @@
               {.koid = 2, .name = "test", .vmos = {2}},
           },
   }});
-  cobalt::FakeLogger_Sync logger;
+  cobalt::StubMetricEventLogger_Sync logger;
   sys::ComponentInspector inspector(context_provider_.context());
   Metrics m(
       kBucketMatches, zx::msec(10), dispatcher(), &inspector, &logger,
diff --git a/src/developer/memory/monitor/tests/monitor_fidl_unittest.cc b/src/developer/memory/monitor/tests/monitor_fidl_unittest.cc
index 12fc686..336307d 100644
--- a/src/developer/memory/monitor/tests/monitor_fidl_unittest.cc
+++ b/src/developer/memory/monitor/tests/monitor_fidl_unittest.cc
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <fuchsia/cobalt/cpp/fidl_test_base.h>
 #include <fuchsia/hardware/ram/metrics/cpp/fidl_test_base.h>
+#include <fuchsia/metrics/cpp/fidl_test_base.h>
 #include <lib/async/cpp/executor.h>
 #include <lib/inspect/testing/cpp/inspect.h>
 #include <lib/sys/cpp/testing/component_context_provider.h>
@@ -11,8 +11,9 @@
 #include <future>
 
 #include <gtest/gtest.h>
-#include <src/cobalt/bin/testing/fake_logger.h>
+#include <src/cobalt/bin/testing/stub_metric_event_logger.h>
 
+#include "lib/fpromise/result.h"
 #include "src/developer/memory/monitor/monitor.h"
 #include "src/lib/testing/loop_fixture/test_loop_fixture.h"
 
@@ -113,19 +114,13 @@
   fidl::Binding<fuchsia::hardware::ram::metrics::Device> binding_{this};
 };
 
-class MockLogger : public ::fuchsia::cobalt::testing::Logger_TestBase {
+class MockLogger : public ::fuchsia::metrics::testing::MetricEventLogger_TestBase {
  public:
-  void LogCobaltEvents(std::vector<fuchsia::cobalt::CobaltEvent> events,
-                       LogCobaltEventsCallback callback) override {
+  void LogMetricEvents(std::vector<fuchsia::metrics::MetricEvent> events,
+                       LogMetricEventsCallback callback) override {
     num_calls_++;
     num_events_ += events.size();
-    callback(fuchsia::cobalt::Status::OK);
-  }
-  void LogEvent(uint32_t metric_id, uint32_t event_code,
-                LogCobaltEventsCallback callback) override {
-    num_calls_++;
-    num_events_ += 1;
-    callback(fuchsia::cobalt::Status::OK);
+    callback(fpromise::ok());
   }
   void NotImplemented_(const std::string& name) override {
     ASSERT_TRUE(false) << name << " is not implemented";
@@ -138,18 +133,18 @@
   int num_events_ = 0;
 };
 
-class MockLoggerFactory : public ::fuchsia::cobalt::testing::LoggerFactory_TestBase {
+class MockLoggerFactory : public ::fuchsia::metrics::testing::MetricEventLoggerFactory_TestBase {
  public:
   MockLogger* logger() { return logger_.get(); }
   uint32_t received_project_id() { return received_project_id_; }
 
-  void CreateLoggerFromProjectId(uint32_t project_id,
-                                 ::fidl::InterfaceRequest<fuchsia::cobalt::Logger> logger,
-                                 CreateLoggerFromProjectIdCallback callback) override {
-    received_project_id_ = project_id;
+  void CreateMetricEventLogger(fuchsia::metrics::ProjectSpec project_spec,
+                               ::fidl::InterfaceRequest<fuchsia::metrics::MetricEventLogger> logger,
+                               CreateMetricEventLoggerCallback callback) override {
+    received_project_id_ = project_spec.project_id();
     logger_.reset(new MockLogger());
     logger_bindings_.AddBinding(logger_.get(), std::move(logger));
-    callback(fuchsia::cobalt::Status::OK);
+    callback(fpromise::ok());
   }
 
   void NotImplemented_(const std::string& name) override {
@@ -159,7 +154,7 @@
  private:
   uint32_t received_project_id_;
   std::unique_ptr<MockLogger> logger_;
-  fidl::BindingSet<fuchsia::cobalt::Logger> logger_bindings_;
+  fidl::BindingSet<fuchsia::metrics::MetricEventLogger> logger_bindings_;
 };
 
 class MemoryBandwidthInspectTest : public gtest::TestLoopFixture {
@@ -204,7 +199,7 @@
     // The Monitor will make asynchronous calls to the MockLogger*s that are also running in this
     // class/tests thread. So the call to the Monitor needs to be made on a different thread, such
     // that the MockLogger*s running on the main thread can respond to those calls.
-    std::future<void /*fuchsia::cobalt::Logger_Sync**/> result =
+    std::future<void /*fuchsia::metrics::MetricEventLogger_Sync**/> result =
         std::async([this]() { monitor_->CreateMetrics({}); });
     while (result.wait_for(std::chrono::milliseconds(1)) != std::future_status::ready) {
       // Run the main thread's loop, allowing the MockLogger* objects to respond to requests.
@@ -219,7 +214,7 @@
   FakeRamDevice fake_device_;
   fidl::Binding<fuchsia::hardware::ram::metrics::Device> ram_binding_;
   std::unique_ptr<MockLoggerFactory> logger_factory_;
-  fidl::BindingSet<fuchsia::cobalt::LoggerFactory> factory_bindings_;
+  fidl::BindingSet<fuchsia::metrics::MetricEventLoggerFactory> factory_bindings_;
 };
 
 TEST_F(MemoryBandwidthInspectTest, MemoryBandwidth) {
diff --git a/src/devices/bin/driver_host2/BUILD.gn b/src/devices/bin/driver_host2/BUILD.gn
index 2448c14..5f55846 100644
--- a/src/devices/bin/driver_host2/BUILD.gn
+++ b/src/devices/bin/driver_host2/BUILD.gn
@@ -29,7 +29,7 @@
     "//sdk/lib/driver2:llcpp",
     "//sdk/lib/driver_runtime:driver_runtime_cpp",
     "//sdk/lib/driver_runtime:driver_runtime_internal_cpp",
-    "//sdk/lib/sys/component/llcpp",
+    "//sdk/lib/sys/component/cpp",
     "//sdk/lib/syslog/cpp",
     "//src/devices/lib/driver:driver_runtime",
     "//src/lib/storage/vfs/cpp",
@@ -57,7 +57,7 @@
     ":bind",
     ":fuchsia.driverhost.test_llcpp",
     "//sdk/lib/driver2:llcpp",
-    "//sdk/lib/sys/component/llcpp",
+    "//sdk/lib/sys/component/cpp",
     "//src/devices/lib/driver:driver_runtime",
     "//zircon/system/ulib/service:service-llcpp",
   ]
@@ -96,7 +96,7 @@
     "//sdk/fidl/fuchsia.io",
     "//sdk/lib/driver_runtime:driver_runtime_internal",
     "//sdk/lib/inspect/testing/cpp",
-    "//sdk/lib/sys/component/llcpp",
+    "//sdk/lib/sys/component/cpp",
     "//src/devices/lib/driver:driver_runtime",
     "//src/lib/fxl/test:gtest_main",
     "//zircon/system/ulib/async-loop:async-loop-cpp",
diff --git a/src/devices/bin/driver_host2/driver_host.cc b/src/devices/bin/driver_host2/driver_host.cc
index 2894e55..e87f032 100644
--- a/src/devices/bin/driver_host2/driver_host.cc
+++ b/src/devices/bin/driver_host2/driver_host.cc
@@ -12,7 +12,7 @@
 #include <lib/fdio/directory.h>
 #include <lib/fit/defer.h>
 #include <lib/fit/function.h>
-#include <lib/sys/component/llcpp/outgoing_directory.h>
+#include <lib/sys/component/cpp/outgoing_directory.h>
 #include <lib/syslog/cpp/macros.h>
 #include <zircon/dlfcn.h>
 
diff --git a/src/devices/bin/driver_host2/driver_host.h b/src/devices/bin/driver_host2/driver_host.h
index 8df6933..989a344 100644
--- a/src/devices/bin/driver_host2/driver_host.h
+++ b/src/devices/bin/driver_host2/driver_host.h
@@ -10,7 +10,7 @@
 #include <lib/driver2/record.h>
 #include <lib/fdf/cpp/dispatcher.h>
 #include <lib/inspect/cpp/inspect.h>
-#include <lib/sys/component/llcpp/outgoing_directory.h>
+#include <lib/sys/component/cpp/outgoing_directory.h>
 #include <lib/zx/status.h>
 #include <zircon/compiler.h>
 
diff --git a/src/devices/bin/driver_host2/driver_host_test_driver.cc b/src/devices/bin/driver_host2/driver_host_test_driver.cc
index 8457190..17920c7 100644
--- a/src/devices/bin/driver_host2/driver_host_test_driver.cc
+++ b/src/devices/bin/driver_host2/driver_host_test_driver.cc
@@ -8,7 +8,7 @@
 #include <lib/fdio/directory.h>
 #include <lib/fidl/epitaph.h>
 #include <lib/service/llcpp/service.h>
-#include <lib/sys/component/llcpp/outgoing_directory.h>
+#include <lib/sys/component/cpp/outgoing_directory.h>
 
 namespace fdf = fuchsia_driver_framework;
 namespace ftest = fuchsia_driverhost_test;
diff --git a/src/devices/bin/driver_host2/main.cc b/src/devices/bin/driver_host2/main.cc
index a1e5506..2ff4072 100644
--- a/src/devices/bin/driver_host2/main.cc
+++ b/src/devices/bin/driver_host2/main.cc
@@ -8,7 +8,7 @@
 #include <lib/async-loop/default.h>
 #include <lib/fdf/internal.h>
 #include <lib/inspect/service/cpp/service.h>
-#include <lib/sys/component/llcpp/outgoing_directory.h>
+#include <lib/sys/component/cpp/outgoing_directory.h>
 #include <lib/syslog/cpp/macros.h>
 #include <lib/vfs/cpp/pseudo_dir.h>
 #include <lib/vfs/cpp/service.h>
diff --git a/src/devices/bin/driver_manager/BUILD.gn b/src/devices/bin/driver_manager/BUILD.gn
index 95c22f0..b6eb7bf 100644
--- a/src/devices/bin/driver_manager/BUILD.gn
+++ b/src/devices/bin/driver_manager/BUILD.gn
@@ -235,7 +235,7 @@
     "//sdk/fidl/fuchsia.io:fuchsia.io_llcpp",
     "//sdk/lib/fdio",
     "//sdk/lib/inspect/testing/cpp:zxtest",
-    "//sdk/lib/sys/component/llcpp",
+    "//sdk/lib/sys/component/cpp",
     "//src/devices/lib/bind:bind-lib",
     "//src/lib/storage/vfs/cpp:test_support",
     "//zircon/system/ulib/mock-boot-arguments",
diff --git a/src/devices/bin/driver_manager/devfs_test.cc b/src/devices/bin/driver_manager/devfs_test.cc
index 9bc6eff..c3dc691 100644
--- a/src/devices/bin/driver_manager/devfs_test.cc
+++ b/src/devices/bin/driver_manager/devfs_test.cc
@@ -8,7 +8,7 @@
 #include <lib/async-loop/default.h>
 #include <lib/ddk/driver.h>
 #include <lib/svc/outgoing.h>
-#include <lib/sys/component/llcpp/outgoing_directory.h>
+#include <lib/sys/component/cpp/outgoing_directory.h>
 
 #include <zxtest/zxtest.h>
 
@@ -157,7 +157,7 @@
     service_channel = std::move(server);
     loop.Quit();
   };
-  ASSERT_EQ(outgoing.AddNamedProtocol(std::move(handler), "test").status_value(), ZX_OK);
+  ASSERT_EQ(outgoing.AddProtocol(std::move(handler), "test").status_value(), ZX_OK);
 
   // Export the svc/test.
   auto endpoints = fidl::CreateEndpoints<fio::Directory>();
diff --git a/src/devices/bin/driver_manager/meta/driver-manager-base.shard.cml b/src/devices/bin/driver_manager/meta/driver-manager-base.shard.cml
index 173374dc..dc814dd 100644
--- a/src/devices/bin/driver_manager/meta/driver-manager-base.shard.cml
+++ b/src/devices/bin/driver_manager/meta/driver-manager-base.shard.cml
@@ -11,6 +11,7 @@
         },
         {
             protocol: [
+                "fuchsia.device.composite.DeprecatedCompositeCreator",
                 "fuchsia.device.fs.Exporter",
                 "fuchsia.device.manager.Administrator",
                 "fuchsia.device.manager.DebugDumper",
@@ -78,6 +79,7 @@
         },
         {
             protocol: [
+                "fuchsia.device.composite.DeprecatedCompositeCreator",
                 "fuchsia.device.fs.Exporter",
                 "fuchsia.device.manager.Administrator",
                 "fuchsia.device.manager.DebugDumper",
diff --git a/src/devices/bin/driver_manager/v2/BUILD.gn b/src/devices/bin/driver_manager/v2/BUILD.gn
index 1ce8df7..ade6dc0 100644
--- a/src/devices/bin/driver_manager/v2/BUILD.gn
+++ b/src/devices/bin/driver_manager/v2/BUILD.gn
@@ -33,10 +33,12 @@
     "//sdk/fidl/fuchsia.component.decl:fuchsia.component.decl_cpp",
     "//sdk/fidl/fuchsia.component.decl:fuchsia.component.decl_llcpp",
     "//sdk/fidl/fuchsia.component.runner:fuchsia.component.runner_llcpp",
+    "//sdk/fidl/fuchsia.device.composite:fuchsia.device.composite_cpp",
     "//sdk/fidl/fuchsia.device.manager:fuchsia.device.manager_cpp",
     "//sdk/fidl/fuchsia.driver.development:fuchsia.driver.development_llcpp",
     "//sdk/fidl/fuchsia.driver.host:fuchsia.driver.host_llcpp",
     "//sdk/fidl/fuchsia.driver.index:fuchsia.driver.index_llcpp",
+    "//src/lib/ddk",
     "//src/lib/storage/vfs/cpp",
     "//zircon/system/ulib/inspect",
   ]
diff --git a/src/devices/bin/driver_manager/v2/composite_assembler.cc b/src/devices/bin/driver_manager/v2/composite_assembler.cc
index 4ebd225..305aa90 100644
--- a/src/devices/bin/driver_manager/v2/composite_assembler.cc
+++ b/src/devices/bin/driver_manager/v2/composite_assembler.cc
@@ -5,6 +5,7 @@
 #include "src/devices/bin/driver_manager/v2/composite_assembler.h"
 
 #include "src/devices/lib/log/log.h"
+#include "src/lib/storage/vfs/cpp/service.h"
 
 namespace dfv2 {
 
@@ -129,6 +130,14 @@
     assembler->properties_.push_back(property.Build());
   }
 
+  // Add the composite value.
+  {
+    auto property = fuchsia_driver_framework::wire::NodeProperty::Builder(assembler->arena_);
+    property.key(fuchsia_driver_framework::wire::NodePropertyKey::WithIntValue(BIND_COMPOSITE));
+    property.value(fuchsia_driver_framework::wire::NodePropertyValue::WithIntValue(1));
+    assembler->properties_.push_back(property.Build());
+  }
+
   // Make the primary fragment first.
   auto fragment =
       CompositeDeviceFragment::Create(descriptor.fragments()[descriptor.primary_fragment_index()]);
@@ -198,8 +207,9 @@
   binder_->Bind(*node.value(), nullptr);
 }
 
-CompositeDeviceManager::CompositeDeviceManager(DriverBinder* binder, async_dispatcher_t* dispatcher)
-    : binder_(binder), dispatcher_(dispatcher) {}
+CompositeDeviceManager::CompositeDeviceManager(DriverBinder* binder, async_dispatcher_t* dispatcher,
+                                               fit::function<void()> rebind_callback)
+    : binder_(binder), dispatcher_(dispatcher), rebind_callback_(std::move(rebind_callback)) {}
 
 zx_status_t CompositeDeviceManager::AddCompositeDevice(
     std::string name, fuchsia_device_manager::CompositeDeviceDescriptor descriptor) {
@@ -209,16 +219,59 @@
     return assembler.error_value();
   }
   assemblers_.push_back(std::move(assembler.value()));
+
+  RebindNodes();
   return ZX_OK;
 }
 
+void CompositeDeviceManager::RebindNodes() {
+  // Take our composite nodes and run them through the device groups again.
+  std::list<std::weak_ptr<Node>> nodes = std::move(nodes_);
+  for (auto& weak_node : nodes) {
+    auto node = weak_node.lock();
+    if (!node) {
+      continue;
+    }
+    BindNode(node);
+  }
+
+  rebind_callback_();
+}
+
 bool CompositeDeviceManager::BindNode(std::shared_ptr<Node> node) {
+  bool did_match = false;
   for (auto& assembler : assemblers_) {
     if (assembler->BindNode(node)) {
-      return true;
+      // We do not break here because DFv1 composites allow for MULTIBIND.
+      // For example, the sysmem fragment can match multiple composite devices.
+      // To support that, nodes can bind to multiple composite devices.
+      did_match = true;
     }
   }
-  return false;
+
+  if (did_match) {
+    nodes_.push_back(node->weak_from_this());
+  }
+  return did_match;
+}
+
+zx::status<> CompositeDeviceManager::Publish(const fbl::RefPtr<fs::PseudoDir>& svc_dir) {
+  const auto service =
+      [this](fidl::ServerEnd<fuchsia_device_composite::DeprecatedCompositeCreator> request) {
+        fidl::BindServer<fidl::Server<fuchsia_device_composite::DeprecatedCompositeCreator>>(
+            dispatcher_, std::move(request), this);
+        return ZX_OK;
+      };
+  zx_status_t status = svc_dir->AddEntry(
+      fidl::DiscoverableProtocolName<fuchsia_device_composite::DeprecatedCompositeCreator>,
+      fbl::MakeRefCounted<fs::Service>(service));
+  return zx::make_status(status);
+}
+
+void CompositeDeviceManager::AddCompositeDevice(AddCompositeDeviceRequest& request,
+                                                AddCompositeDeviceCompleter::Sync& completer) {
+  zx_status_t status = AddCompositeDevice(request.name(), request.args());
+  completer.Reply(zx::make_status(status));
 }
 
 }  // namespace dfv2
diff --git a/src/devices/bin/driver_manager/v2/composite_assembler.h b/src/devices/bin/driver_manager/v2/composite_assembler.h
index 49a9ef2..37f75896 100644
--- a/src/devices/bin/driver_manager/v2/composite_assembler.h
+++ b/src/devices/bin/driver_manager/v2/composite_assembler.h
@@ -5,10 +5,12 @@
 #ifndef SRC_DEVICES_BIN_DRIVER_MANAGER_V2_COMPOSITE_ASSEMBLER_H_
 #define SRC_DEVICES_BIN_DRIVER_MANAGER_V2_COMPOSITE_ASSEMBLER_H_
 
+#include <fidl/fuchsia.device.composite/cpp/fidl.h>
 #include <fidl/fuchsia.device.manager/cpp/fidl.h>
 
 #include "src/devices/bin/driver_manager/binding.h"
 #include "src/devices/bin/driver_manager/v2/node.h"
+#include "src/lib/storage/vfs/cpp/pseudo_dir.h"
 
 namespace dfv2 {
 
@@ -26,7 +28,7 @@
   bool BindNode(std::shared_ptr<Node> node);
 
   std::shared_ptr<Node> bound_node() { return bound_node_.lock(); }
-  std::string_view name() { return name_; }
+  std::string_view name() const { return name_; }
 
  private:
   std::string name_;
@@ -72,11 +74,12 @@
 };
 
 // This class manages all of the `CompositeDeviceAssemblers` that exist.
-class CompositeDeviceManager {
+class CompositeDeviceManager : fidl::Server<fuchsia_device_composite::DeprecatedCompositeCreator> {
  public:
   // Create a CompositeDeviceManager. `binder` is unowned and must outlive the
   // manager class.
-  CompositeDeviceManager(DriverBinder* binder, async_dispatcher_t* dispatcher);
+  CompositeDeviceManager(DriverBinder* binder, async_dispatcher_t* dispatcher,
+                         fit::function<void()> rebind_callback);
 
   zx_status_t AddCompositeDevice(std::string name,
                                  fuchsia_device_manager::CompositeDeviceDescriptor descriptor);
@@ -86,9 +89,25 @@
   // a composite device, then there is no need to bind it to a driver.
   bool BindNode(std::shared_ptr<Node> node);
 
+  // Publish capabilities to the outgoing directory.
+  // CompositeDeviceManager must outlive `svc_dir` because it will be used
+  // in callbacks when other components connect to the capabilities.
+  zx::status<> Publish(const fbl::RefPtr<fs::PseudoDir>& svc_dir);
+
  private:
+  void AddCompositeDevice(AddCompositeDeviceRequest& request,
+                          AddCompositeDeviceCompleter::Sync& completer) override;
+
+  void RebindNodes();
+
   DriverBinder* binder_;
   async_dispatcher_t* dispatcher_;
+  fit::function<void()> rebind_callback_;
+
+  // A list of nodes that have been bound to composite devices.
+  // In DFv1 a node can be bound to multiple composite devices, so we keep
+  // these around for rebinding.
+  std::list<std::weak_ptr<Node>> nodes_;
   std::vector<std::unique_ptr<CompositeDeviceAssembler>> assemblers_;
 };
 
diff --git a/src/devices/bin/driver_manager/v2/composite_assembler_test.cc b/src/devices/bin/driver_manager/v2/composite_assembler_test.cc
index b92fb10..dbcf51c 100644
--- a/src/devices/bin/driver_manager/v2/composite_assembler_test.cc
+++ b/src/devices/bin/driver_manager/v2/composite_assembler_test.cc
@@ -30,7 +30,7 @@
   TestBinder binder([](auto& node) {});
   auto node =
       std::make_shared<dfv2::Node>("parent", std::vector<dfv2::Node*>(), &binder, dispatcher());
-  dfv2::CompositeDeviceManager manager(&binder, dispatcher());
+  dfv2::CompositeDeviceManager manager(&binder, dispatcher(), []() {});
   ASSERT_FALSE(manager.BindNode(node));
 }
 
@@ -38,7 +38,7 @@
   TestBinder binder([](auto& node) {});
   auto node =
       std::make_shared<dfv2::Node>("parent", std::vector<dfv2::Node*>(), &binder, dispatcher());
-  dfv2::CompositeDeviceManager manager(&binder, dispatcher());
+  dfv2::CompositeDeviceManager manager(&binder, dispatcher(), []() {});
 
   fuchsia_device_manager::CompositeDeviceDescriptor descriptor;
   fuchsia_device_manager::DeviceFragment fragment;
@@ -58,7 +58,7 @@
   TestBinder binder([&bind_was_called](auto& node) { bind_was_called = true; });
   auto node =
       std::make_shared<dfv2::Node>("parent", std::vector<dfv2::Node*>(), &binder, dispatcher());
-  dfv2::CompositeDeviceManager manager(&binder, dispatcher());
+  dfv2::CompositeDeviceManager manager(&binder, dispatcher(), []() {});
 
   fuchsia_device_manager::CompositeDeviceDescriptor descriptor;
   fuchsia_device_manager::DeviceFragment fragment;
@@ -89,7 +89,7 @@
   TestBinder binder([&bind_was_called](auto& node) { bind_was_called = true; });
   auto node =
       std::make_shared<dfv2::Node>("parent", std::vector<dfv2::Node*>(), &binder, dispatcher());
-  dfv2::CompositeDeviceManager manager(&binder, dispatcher());
+  dfv2::CompositeDeviceManager manager(&binder, dispatcher(), []() {});
 
   fuchsia_device_manager::CompositeDeviceDescriptor descriptor;
   fuchsia_device_manager::DeviceFragment fragment;
@@ -114,10 +114,13 @@
   ASSERT_EQ(kCompositeName, child->name());
   ASSERT_EQ(1ul, child->parents().size());
 
-  ASSERT_EQ(1ul, child->properties().size());
+  ASSERT_EQ(2ul, child->properties().size());
   ASSERT_EQ(kPropId, child->properties()[0].key().int_value());
   ASSERT_EQ(kPropValue, child->properties()[0].value().int_value());
 
+  ASSERT_EQ(static_cast<uint32_t>(BIND_COMPOSITE), child->properties()[1].key().int_value());
+  ASSERT_EQ(1ul, child->properties()[1].value().int_value());
+
   // Check that our node no longer matches now that the composite has been created.
   ASSERT_FALSE(manager.BindNode(node));
 }
@@ -129,7 +132,7 @@
       std::make_shared<dfv2::Node>("parent", std::vector<dfv2::Node*>(), &binder, dispatcher());
   auto node2 =
       std::make_shared<dfv2::Node>("parent2", std::vector<dfv2::Node*>(), &binder, dispatcher());
-  dfv2::CompositeDeviceManager manager(&binder, dispatcher());
+  dfv2::CompositeDeviceManager manager(&binder, dispatcher(), []() {});
 
   fuchsia_device_manager::CompositeDeviceDescriptor descriptor;
   // Create two fragments
@@ -160,10 +163,13 @@
   ASSERT_EQ(kCompositeName, child->name());
   ASSERT_EQ(2ul, child->parents().size());
 
-  ASSERT_EQ(1ul, child->properties().size());
+  ASSERT_EQ(2ul, child->properties().size());
   ASSERT_EQ(kPropId, child->properties()[0].key().int_value());
   ASSERT_EQ(kPropValue, child->properties()[0].value().int_value());
 
+  ASSERT_EQ(static_cast<uint32_t>(BIND_COMPOSITE), child->properties()[1].key().int_value());
+  ASSERT_EQ(1ul, child->properties()[1].value().int_value());
+
   // Check that our node no longer matches now that the composite has been created.
   ASSERT_FALSE(manager.BindNode(node));
 }
@@ -175,7 +181,7 @@
       std::make_shared<dfv2::Node>("parent", std::vector<dfv2::Node*>(), &binder, dispatcher());
   auto node2 =
       std::make_shared<dfv2::Node>("parent2", std::vector<dfv2::Node*>(), &binder, dispatcher());
-  dfv2::CompositeDeviceManager manager(&binder, dispatcher());
+  dfv2::CompositeDeviceManager manager(&binder, dispatcher(), []() {});
 
   fuchsia_device_manager::CompositeDeviceDescriptor descriptor;
   // Create two fragments
@@ -225,7 +231,7 @@
   TestBinder binder([&bind_was_called](auto& node) { bind_was_called = true; });
   auto node =
       std::make_shared<dfv2::Node>("parent", std::vector<dfv2::Node*>(), &binder, dispatcher());
-  dfv2::CompositeDeviceManager manager(&binder, dispatcher());
+  dfv2::CompositeDeviceManager manager(&binder, dispatcher(), []() {});
 
   fuchsia_device_manager::CompositeDeviceDescriptor descriptor;
   fuchsia_device_manager::DeviceFragment fragment;
@@ -245,18 +251,63 @@
 
   ASSERT_TRUE(manager.BindNode(node));
 
-  // Check that we created a child node.
+  // Check that both parents were created.
   ASSERT_TRUE(bind_was_called);
-  ASSERT_EQ(1ul, node->children().size());
+  ASSERT_EQ(2ul, node->children().size());
+
   auto child = node->children().front();
   ASSERT_EQ(kCompositeName, child->name());
   ASSERT_EQ(1ul, child->parents().size());
-
   ASSERT_EQ(kPropId, child->properties()[0].key().int_value());
   ASSERT_EQ(kPropValue, child->properties()[0].value().int_value());
 
   // Match to the second composite device.
+  child = node->children().back();
+  ASSERT_EQ(kCompositeName2, child->name());
+  ASSERT_EQ(1ul, child->parents().size());
+
+  // Check that our node no longer matches now that both composites have been created.
+  ASSERT_FALSE(manager.BindNode(node));
+}
+
+// Check that adding a composite after a node has already matched works.
+TEST_F(CompositeAssemblerTest, AddCompositeAfterNode) {
+  bool bind_was_called = false;
+  TestBinder binder([&bind_was_called](auto& node) { bind_was_called = true; });
+  auto node =
+      std::make_shared<dfv2::Node>("parent", std::vector<dfv2::Node*>(), &binder, dispatcher());
+  dfv2::CompositeDeviceManager manager(&binder, dispatcher(), []() {});
+
+  fuchsia_device_manager::CompositeDeviceDescriptor descriptor;
+  fuchsia_device_manager::DeviceFragment fragment;
+  fragment.name() = kFragmentName;
+  fragment.parts().emplace_back();
+  fragment.parts()[0].match_program().emplace_back();
+  fragment.parts()[0].match_program()[0] = fuchsia_device_manager::BindInstruction BI_MATCH();
+  descriptor.fragments().push_back(fragment);
+
+  descriptor.props().emplace_back();
+  descriptor.props()[0].id() = kPropId;
+  descriptor.props()[0].value() = kPropValue;
+
+  // Create two composite device assemblers.
+  manager.AddCompositeDevice(std::string(kCompositeName), descriptor);
+
+  // Bind our node.
   ASSERT_TRUE(manager.BindNode(node));
+  ASSERT_TRUE(bind_was_called);
+  ASSERT_EQ(1ul, node->children().size());
+
+  auto child = node->children().front();
+  ASSERT_EQ(kCompositeName, child->name());
+  ASSERT_EQ(1ul, child->parents().size());
+  ASSERT_EQ(kPropId, child->properties()[0].key().int_value());
+  ASSERT_EQ(kPropValue, child->properties()[0].value().int_value());
+
+  // Add a new composite device. The Manager should check already bound nodes,
+  // so it should create a new composite device immediately,
+  manager.AddCompositeDevice(std::string(kCompositeName2), descriptor);
+
   ASSERT_EQ(2ul, node->children().size());
   child = node->children().back();
   ASSERT_EQ(kCompositeName2, child->name());
diff --git a/src/devices/bin/driver_manager/v2/driver_runner.cc b/src/devices/bin/driver_manager/v2/driver_runner.cc
index 3da17d7..6242700 100644
--- a/src/devices/bin/driver_manager/v2/driver_runner.cc
+++ b/src/devices/bin/driver_manager/v2/driver_runner.cc
@@ -157,7 +157,9 @@
     : realm_(std::move(realm), dispatcher),
       driver_index_(std::move(driver_index), dispatcher),
       dispatcher_(dispatcher),
-      root_node_(std::make_shared<Node>("root", std::vector<Node*>{}, this, dispatcher)) {
+      root_node_(std::make_shared<Node>("root", std::vector<Node*>{}, this, dispatcher)),
+      composite_device_manager_(this, dispatcher,
+                                [this]() { this->TryBindAllOrphansUntracked(); }) {
   inspector.GetRoot().CreateLazyNode(
       "driver_runner", [this] { return Inspect(); }, &inspector);
 }
@@ -216,7 +218,13 @@
     LOGF(ERROR, "Failed to add directory entry '%s': %s",
          fidl::DiscoverableProtocolName<frunner::ComponentRunner>, zx_status_get_string(status));
   }
-  return zx::make_status(status);
+
+  auto composite_publish = composite_device_manager_.Publish(svc_dir);
+  if (composite_publish.is_error()) {
+    return composite_publish.take_error();
+  }
+
+  return zx::ok();
 }
 
 zx::status<> DriverRunner::StartRootDriver(std::string_view url) {
@@ -384,6 +392,11 @@
 }
 
 void DriverRunner::Bind(Node& node, std::shared_ptr<BindResultTracker> result_tracker) {
+  // Check the DFv1 composites first, and don't bind to others if they match.
+  if (composite_device_manager_.BindNode(node.shared_from_this())) {
+    return;
+  }
+
   auto match_callback = [this, weak_node = node.weak_from_this(), result_tracker](
                             fidl::WireUnownedResult<fdi::DriverIndex::MatchDriver>& result) {
     auto shared_node = weak_node.lock();
diff --git a/src/devices/bin/driver_manager/v2/driver_runner.h b/src/devices/bin/driver_manager/v2/driver_runner.h
index fc094c3..941d0b6 100644
--- a/src/devices/bin/driver_manager/v2/driver_runner.h
+++ b/src/devices/bin/driver_manager/v2/driver_runner.h
@@ -23,6 +23,7 @@
 
 #include <fbl/intrusive_double_list.h>
 
+#include "src/devices/bin/driver_manager/v2/composite_assembler.h"
 #include "src/devices/bin/driver_manager/v2/driver_component.h"
 #include "src/devices/bin/driver_manager/v2/driver_host.h"
 #include "src/devices/bin/driver_manager/v2/node.h"
@@ -96,6 +97,8 @@
   async_dispatcher_t* const dispatcher_;
   std::shared_ptr<Node> root_node_;
 
+  CompositeDeviceManager composite_device_manager_;
+
   std::unordered_map<zx_koid_t, Node&> driver_args_;
   std::unordered_multimap<DriverUrl, CompositeArgs> composite_args_;
   fbl::DoublyLinkedList<std::unique_ptr<DriverHostComponent>> driver_hosts_;
diff --git a/src/devices/bin/driver_playground/BUILD.gn b/src/devices/bin/driver_playground/BUILD.gn
index fdcc682..3f2f417 100644
--- a/src/devices/bin/driver_playground/BUILD.gn
+++ b/src/devices/bin/driver_playground/BUILD.gn
@@ -37,7 +37,7 @@
   sources = [ "src/main.cc" ]
   deps = [
     ":lib",
-    "//sdk/lib/sys/component/llcpp",
+    "//sdk/lib/sys/component/cpp",
     "//zircon/system/ulib/async-loop:async-loop-cpp",
     "//zircon/system/ulib/async-loop:async-loop-default",
   ]
diff --git a/src/devices/bin/driver_playground/src/main.cc b/src/devices/bin/driver_playground/src/main.cc
index 565ee0e..a982889 100644
--- a/src/devices/bin/driver_playground/src/main.cc
+++ b/src/devices/bin/driver_playground/src/main.cc
@@ -4,7 +4,7 @@
 
 #include <lib/async-loop/cpp/loop.h>
 #include <lib/async-loop/default.h>
-#include <lib/sys/component/llcpp/outgoing_directory.h>
+#include <lib/sys/component/cpp/outgoing_directory.h>
 
 #include "src/devices/bin/driver_playground/src/playground.h"
 #include "src/devices/lib/log/log.h"
diff --git a/src/devices/bin/driver_runtime/dispatcher.cc b/src/devices/bin/driver_runtime/dispatcher.cc
index e105818..fe91453 100644
--- a/src/devices/bin/driver_runtime/dispatcher.cc
+++ b/src/devices/bin/driver_runtime/dispatcher.cc
@@ -484,6 +484,10 @@
     GetDispatcherCoordinator().CacheUnboundIrq(std::move(irq));
   }
 
+  // We want |fdf_dispatcher_get_current_dispatcher| to work in cancellation and shutdown callbacks.
+  driver_context::PushDriver(owner_, this);
+  auto pop_driver = fit::defer([]() { driver_context::PopDriver(); });
+
   // We remove one item at a time from the shutdown queue, in case someone
   // tries to cancel a wait (which has not been canceled yet) from within a
   // canceled callback. We don't use fbl::AutoLock as we want to be able to
diff --git a/src/devices/bin/driver_runtime/dispatcher_test.cc b/src/devices/bin/driver_runtime/dispatcher_test.cc
index d1e2d66..d7ff5c4 100644
--- a/src/devices/bin/driver_runtime/dispatcher_test.cc
+++ b/src/devices/bin/driver_runtime/dispatcher_test.cc
@@ -1576,10 +1576,12 @@
                             [&] { task_complete.Signal(); }));
   ASSERT_OK(task_complete.Wait());
 
+  // This should remove the in-flight irq, unbind the irq and call the handler with ZX_ERR_CANCELED.
+  fdf_dispatcher_shutdown_async(fdf_dispatcher);
+
+  // We can now unblock the first dispatcher.
   complete_task.Signal();
 
-  // This should unbind the irq and call the handler with ZX_ERR_CANCELED.
-  fdf_dispatcher_shutdown_async(fdf_dispatcher);
   ASSERT_OK(shutdown_observer->WaitUntilShutdown());
   ASSERT_OK(irq_completion.Wait());
   fdf_dispatcher_destroy(fdf_dispatcher);
@@ -2278,6 +2280,49 @@
   fdf_internal_wait_until_dispatcher_idle(dispatcher2);
 }
 
+TEST_F(DispatcherTest, GetCurrentDispatcherShutdownCallback) {
+  libsync::Completion shutdown_completion;
+  auto shutdown_handler = [&](fdf_dispatcher_t* shutdown_dispatcher) mutable {
+    ASSERT_EQ(shutdown_dispatcher, fdf_dispatcher_get_current_dispatcher());
+    shutdown_completion.Signal();
+  };
+
+  fdf::Dispatcher dispatcher;
+
+  {
+    driver_context::PushDriver(CreateFakeDriver());
+    auto pop_driver = fit::defer([]() { driver_context::PopDriver(); });
+
+    auto dispatcher_with_status = fdf::Dispatcher::Create(0, shutdown_handler);
+    ASSERT_FALSE(dispatcher_with_status.is_error());
+    dispatcher = *std::move(dispatcher_with_status);
+  }
+
+  zx::event event;
+  ASSERT_OK(zx::event::create(0, &event));
+
+  async::WaitOnce wait(event.get(), ZX_USER_SIGNAL_0);
+
+  // Registered, but not yet signaled.
+  async_dispatcher_t* async_dispatcher = dispatcher.async_dispatcher();
+  ASSERT_NOT_NULL(async_dispatcher);
+
+  libsync::Completion wait_complete;
+  ASSERT_OK(wait.Begin(async_dispatcher, [&wait_complete, event = std::move(event)](
+                                             async_dispatcher_t* dispatcher, async::WaitOnce* wait,
+                                             zx_status_t status, const zx_packet_signal_t* signal) {
+    ASSERT_STATUS(status, ZX_ERR_CANCELED);
+    ASSERT_EQ(dispatcher, fdf_dispatcher_get_current_dispatcher());
+    wait_complete.Signal();
+  }));
+
+  // Shutdown the dispatcher, which should schedule cancellation of the channel read.
+  dispatcher.ShutdownAsync();
+
+  ASSERT_OK(wait_complete.Wait(zx::time::infinite()));
+  ASSERT_OK(shutdown_completion.Wait());
+}
+
 TEST_F(DispatcherTest, HasQueuedTasks) {
   fdf_dispatcher_t* dispatcher;
   ASSERT_NO_FATAL_FAILURE(CreateDispatcher(0, "scheduler_role", CreateFakeDriver(), &dispatcher));
diff --git a/src/devices/internal/drivers/fragment/BUILD.gn b/src/devices/internal/drivers/fragment/BUILD.gn
index 44d25d5..6bfe75f 100644
--- a/src/devices/internal/drivers/fragment/BUILD.gn
+++ b/src/devices/internal/drivers/fragment/BUILD.gn
@@ -40,7 +40,6 @@
     "//sdk/banjo/fuchsia.hardware.ge2d:fuchsia.hardware.ge2d_banjo_cpp",
     "//sdk/banjo/fuchsia.hardware.gpio:fuchsia.hardware.gpio_banjo_cpp",
     "//sdk/banjo/fuchsia.hardware.hdmi:fuchsia.hardware.hdmi_banjo_cpp",
-    "//sdk/banjo/fuchsia.hardware.i2c:fuchsia.hardware.i2c_banjo_cpp",
     "//sdk/banjo/fuchsia.hardware.isp:fuchsia.hardware.isp_banjo_cpp",
     "//sdk/banjo/fuchsia.hardware.mipicsi:fuchsia.hardware.mipicsi_banjo_cpp",
     "//sdk/banjo/fuchsia.hardware.platform.device:fuchsia.hardware.platform.device_banjo_cpp",
@@ -119,7 +118,6 @@
     "//sdk/banjo/fuchsia.hardware.ethernet.board:fuchsia.hardware.ethernet.board_banjo_cpp",
     "//sdk/banjo/fuchsia.hardware.gpio:fuchsia.hardware.gpio_banjo_cpp",
     "//sdk/banjo/fuchsia.hardware.hdmi:fuchsia.hardware.hdmi_banjo_cpp",
-    "//sdk/banjo/fuchsia.hardware.i2c:fuchsia.hardware.i2c_banjo_cpp",
     "//sdk/banjo/fuchsia.hardware.platform.device:fuchsia.hardware.platform.device_banjo_cpp",
     "//sdk/banjo/fuchsia.hardware.power:fuchsia.hardware.power_banjo_cpp",
     "//sdk/banjo/fuchsia.hardware.power.sensor:fuchsia.hardware.power.sensor_banjo_cpp",
diff --git a/src/devices/internal/drivers/fragment/fragment-proxy.cc b/src/devices/internal/drivers/fragment/fragment-proxy.cc
index 06f8368..3bd2345 100644
--- a/src/devices/internal/drivers/fragment/fragment-proxy.cc
+++ b/src/devices/internal/drivers/fragment/fragment-proxy.cc
@@ -52,9 +52,6 @@
     case ZX_PROTOCOL_HDMI:
       proto->ops = &hdmi_protocol_ops_;
       return ZX_OK;
-    case ZX_PROTOCOL_I2C:
-      proto->ops = &i2c_protocol_ops_;
-      return ZX_OK;
     case ZX_PROTOCOL_PDEV:
       proto->ops = &pdev_protocol_ops_;
       return ZX_OK;
@@ -448,105 +445,6 @@
   Rpc(&req.header, sizeof(req), &resp.header, sizeof(resp), &handle, 1, nullptr, 0, nullptr);
 }
 
-void FragmentProxy::I2cTransact(const i2c_op_t* op_list, size_t op_count,
-                                i2c_transact_callback callback, void* cookie) {
-  size_t writes_length = 0;
-  size_t reads_length = 0;
-  for (size_t i = 0; i < op_count; ++i) {
-    if (op_list[i].is_read) {
-      reads_length += op_list[i].data_size;
-    } else {
-      writes_length += op_list[i].data_size;
-    }
-  }
-  if (!writes_length && !reads_length) {
-    callback(cookie, ZX_ERR_INVALID_ARGS, nullptr, 0);
-    return;
-  }
-
-  size_t req_length = sizeof(I2cProxyRequest) + op_count * sizeof(I2cProxyOp) + writes_length;
-  if (req_length >= kProxyMaxTransferSize) {
-    return callback(cookie, ZX_ERR_BUFFER_TOO_SMALL, nullptr, 0);
-  }
-
-  TRACE_DURATION("i2c", "I2c FragmentProxy I2cTransact");
-  uint8_t req_buffer[kProxyMaxTransferSize];
-  auto req = reinterpret_cast<I2cProxyRequest*>(req_buffer);
-  req->header.proto_id = ZX_PROTOCOL_I2C;
-  req->op = I2cOp::TRANSACT;
-  req->op_count = op_count;
-  if (TRACE_ENABLED()) {
-    req->trace_id = TRACE_NONCE();
-    TRACE_FLOW_BEGIN("i2c", "I2c FragmentProxy I2cTransact Flow", req->trace_id);
-  }
-
-  auto rpc_ops = reinterpret_cast<I2cProxyOp*>(&req[1]);
-  ZX_ASSERT(op_count < I2C_MAX_RW_OPS);
-  for (size_t i = 0; i < op_count; ++i) {
-    rpc_ops[i].length = op_list[i].data_size;
-    rpc_ops[i].is_read = op_list[i].is_read;
-    rpc_ops[i].stop = op_list[i].stop;
-  }
-  uint8_t* p_writes = reinterpret_cast<uint8_t*>(rpc_ops) + op_count * sizeof(I2cProxyOp);
-  for (size_t i = 0; i < op_count; ++i) {
-    if (!op_list[i].is_read) {
-      memcpy(p_writes, op_list[i].data_buffer, op_list[i].data_size);
-      p_writes += op_list[i].data_size;
-    }
-  }
-
-  const size_t resp_length = sizeof(I2cProxyResponse) + reads_length;
-  if (resp_length >= kProxyMaxTransferSize) {
-    callback(cookie, ZX_ERR_INVALID_ARGS, nullptr, 0);
-    return;
-  }
-  uint8_t resp_buffer[kProxyMaxTransferSize];
-  auto* rsp = reinterpret_cast<I2cProxyResponse*>(resp_buffer);
-  size_t actual;
-  auto status = Rpc(&req->header, static_cast<uint32_t>(req_length), &rsp->header,
-                    static_cast<uint32_t>(resp_length), nullptr, 0, nullptr, 0, &actual);
-  if (status != ZX_OK) {
-    callback(cookie, status, nullptr, 0);
-    return;
-  }
-
-  // TODO(voydanoff) This proxying code actually implements i2c_transact synchronously
-  // due to the fact that it is unsafe to respond asynchronously on the devmgr rxrpc channel.
-  // In the future we may want to redo the plumbing to allow this to be truly asynchronous.
-
-  if (actual != resp_length) {
-    status = ZX_ERR_INTERNAL;
-  } else {
-    status = rsp->header.status;
-  }
-  i2c_op_t read_ops[I2C_MAX_RW_OPS];
-  size_t read_ops_cnt = 0;
-  uint8_t* p_reads = reinterpret_cast<uint8_t*>(rsp + 1);
-  for (size_t i = 0; i < op_count; ++i) {
-    if (op_list[i].is_read) {
-      read_ops[read_ops_cnt] = op_list[i];
-      read_ops[read_ops_cnt].data_buffer = p_reads;
-      read_ops_cnt++;
-      p_reads += op_list[i].data_size;
-    }
-  }
-  callback(cookie, status, read_ops, read_ops_cnt);
-}
-
-zx_status_t FragmentProxy::I2cGetMaxTransferSize(size_t* out_size) {
-  I2cProxyRequest req = {};
-  I2cProxyResponse resp = {};
-  req.header.proto_id = ZX_PROTOCOL_I2C;
-  req.op = I2cOp::GET_MAX_TRANSFER_SIZE;
-
-  auto status = Rpc(&req.header, sizeof(req), &resp.header, sizeof(resp));
-  if (status != ZX_OK) {
-    return status;
-  }
-  *out_size = resp.size;
-  return ZX_OK;
-}
-
 zx_status_t FragmentProxy::PDevGetMmio(uint32_t index, pdev_mmio_t* out_mmio) {
   PdevProxyRequest req = {};
   PdevProxyResponse resp = {};
diff --git a/src/devices/internal/drivers/fragment/fragment-proxy.h b/src/devices/internal/drivers/fragment/fragment-proxy.h
index 5714203..98e9dfb 100644
--- a/src/devices/internal/drivers/fragment/fragment-proxy.h
+++ b/src/devices/internal/drivers/fragment/fragment-proxy.h
@@ -12,7 +12,6 @@
 #include <fuchsia/hardware/ethernet/board/cpp/banjo.h>
 #include <fuchsia/hardware/gpio/cpp/banjo.h>
 #include <fuchsia/hardware/hdmi/cpp/banjo.h>
-#include <fuchsia/hardware/i2c/cpp/banjo.h>
 #include <fuchsia/hardware/platform/device/cpp/banjo.h>
 #include <fuchsia/hardware/power/cpp/banjo.h>
 #include <fuchsia/hardware/power/sensor/cpp/banjo.h>
@@ -44,7 +43,6 @@
                       public ddk::EthBoardProtocol<FragmentProxy>,
                       public ddk::GpioProtocol<FragmentProxy>,
                       public ddk::HdmiProtocol<FragmentProxy>,
-                      public ddk::I2cProtocol<FragmentProxy>,
                       public ddk::CodecProtocol<FragmentProxy>,
                       public ddk::DaiProtocol<FragmentProxy>,
                       public ddk::PDevProtocol<FragmentProxy>,
@@ -107,9 +105,6 @@
   zx_status_t GpioSetDriveStrength(uint64_t ds_ua, uint64_t* out_actual_ds_ua);
   zx_status_t GpioGetDriveStrength(uint64_t* ds_ua);
   void HdmiConnect(zx::channel chan);
-  void I2cTransact(const i2c_op_t* op_list, size_t op_count, i2c_transact_callback callback,
-                   void* cookie);
-  zx_status_t I2cGetMaxTransferSize(size_t* out_size);
   zx_status_t PDevGetMmio(uint32_t index, pdev_mmio_t* out_mmio);
   zx_status_t PDevGetInterrupt(uint32_t index, uint32_t flags, zx::interrupt* out_irq);
   zx_status_t PDevGetBti(uint32_t index, zx::bti* out_bti);
diff --git a/src/devices/internal/drivers/fragment/fragment.cc b/src/devices/internal/drivers/fragment/fragment.cc
index f55e7f2..79a20e0 100644
--- a/src/devices/internal/drivers/fragment/fragment.cc
+++ b/src/devices/internal/drivers/fragment/fragment.cc
@@ -238,80 +238,6 @@
   }
 }
 
-void Fragment::I2cTransactCallback(void* cookie, zx_status_t status, const i2c_op_t* op_list,
-                                   size_t op_count) {
-  auto* ctx = static_cast<I2cTransactContext*>(cookie);
-  ctx->result = status;
-  if (status == ZX_OK && ctx->read_buf && ctx->read_length) {
-    memcpy(ctx->read_buf, op_list[0].data_buffer, ctx->read_length);
-  }
-
-  sync_completion_signal(&ctx->completion);
-}
-
-zx_status_t Fragment::RpcI2c(const uint8_t* req_buf, uint32_t req_size, uint8_t* resp_buf,
-                             uint32_t* out_resp_size, zx::handle* req_handles,
-                             uint32_t req_handle_count, zx::handle* resp_handles,
-                             uint32_t* resp_handle_count) {
-  TRACE_DURATION("i2c", "I2c FragmentProxy RpcI2c");
-  if (!i2c_client_.proto_client().is_valid()) {
-    return ZX_ERR_NOT_SUPPORTED;
-  }
-  auto* req = reinterpret_cast<const I2cProxyRequest*>(req_buf);
-  if (req_size < sizeof(*req)) {
-    zxlogf(ERROR, "%s received %u, expecting %zu", __func__, req_size, sizeof(*req));
-    return ZX_ERR_INTERNAL;
-  }
-  auto* resp = reinterpret_cast<I2cProxyResponse*>(resp_buf);
-  *out_resp_size = sizeof(*resp);
-  TRACE_FLOW_END("i2c", "I2c FragmentProxy I2cTransact Flow", req->trace_id);
-
-  switch (req->op) {
-    case I2cOp::TRANSACT: {
-      i2c_op_t i2c_ops[I2C_MAX_RW_OPS];
-      auto* rpc_ops = reinterpret_cast<const I2cProxyOp*>(&req[1]);
-      auto op_count = req->op_count;
-      if (op_count > std::size(i2c_ops)) {
-        return ZX_ERR_BUFFER_TOO_SMALL;
-      }
-      auto* write_buf = reinterpret_cast<const uint8_t*>(&rpc_ops[op_count]);
-      size_t read_length = 0;
-
-      for (size_t i = 0; i < op_count; i++) {
-        if (rpc_ops[i].is_read) {
-          i2c_ops[i].data_buffer = nullptr;
-          read_length += rpc_ops[i].length;
-        } else {
-          i2c_ops[i].data_buffer = write_buf;
-          write_buf += rpc_ops[i].length;
-        }
-        i2c_ops[i].data_size = rpc_ops[i].length;
-        i2c_ops[i].is_read = rpc_ops[i].is_read;
-        i2c_ops[i].stop = rpc_ops[i].stop;
-      }
-
-      I2cTransactContext ctx = {};
-      ctx.read_buf = &resp[1];
-      ctx.read_length = read_length;
-
-      i2c_client_.proto_client().Transact(i2c_ops, op_count, I2cTransactCallback, &ctx);
-      auto status = sync_completion_wait(&ctx.completion, ZX_TIME_INFINITE);
-      if (status == ZX_OK) {
-        status = ctx.result;
-      }
-      if (status == ZX_OK) {
-        *out_resp_size = static_cast<uint32_t>(sizeof(*resp) + read_length);
-      }
-      return status;
-    }
-    case I2cOp::GET_MAX_TRANSFER_SIZE:
-      return i2c_client_.proto_client().GetMaxTransferSize(&resp->size);
-    default:
-      zxlogf(ERROR, "%s: unknown I2C op %u", __func__, static_cast<uint32_t>(req->op));
-      return ZX_ERR_INTERNAL;
-  }
-}
-
 zx_status_t Fragment::RpcPdev(const uint8_t* req_buf, uint32_t req_size, uint8_t* resp_buf,
                               uint32_t* out_resp_size, zx::handle* req_handles,
                               uint32_t req_handle_count, zx::handle* resp_handles,
@@ -840,10 +766,6 @@
       status = RpcHdmi(req_buf, actual, resp_buf, &resp_len, req_handles, req_handle_count,
                        resp_handles, &resp_handle_count);
       break;
-    case ZX_PROTOCOL_I2C:
-      status = RpcI2c(req_buf, actual, resp_buf, &resp_len, req_handles, req_handle_count,
-                      resp_handles, &resp_handle_count);
-      break;
     case ZX_PROTOCOL_PDEV:
       status = RpcPdev(req_buf, actual, resp_buf, &resp_len, req_handles, req_handle_count,
                        resp_handles, &resp_handle_count);
@@ -961,13 +883,6 @@
       hdmi_client_.proto_client().GetProto(static_cast<hdmi_protocol_t*>(out_protocol));
       return ZX_OK;
     }
-    case ZX_PROTOCOL_I2C: {
-      if (!i2c_client_.proto_client().is_valid()) {
-        return ZX_ERR_NOT_SUPPORTED;
-      }
-      i2c_client_.proto_client().GetProto(static_cast<i2c_protocol_t*>(out_protocol));
-      return ZX_OK;
-    }
     case ZX_PROTOCOL_CODEC: {
       if (!codec_client_.proto_client().is_valid()) {
         return ZX_ERR_NOT_SUPPORTED;
diff --git a/src/devices/internal/drivers/fragment/fragment.h b/src/devices/internal/drivers/fragment/fragment.h
index a63c047..42658bb 100644
--- a/src/devices/internal/drivers/fragment/fragment.h
+++ b/src/devices/internal/drivers/fragment/fragment.h
@@ -16,7 +16,6 @@
 #include <fuchsia/hardware/ge2d/cpp/banjo.h>
 #include <fuchsia/hardware/gpio/cpp/banjo.h>
 #include <fuchsia/hardware/hdmi/cpp/banjo.h>
-#include <fuchsia/hardware/i2c/cpp/banjo.h>
 #include <fuchsia/hardware/isp/cpp/banjo.h>
 #include <fuchsia/hardware/mipicsi/cpp/banjo.h>
 #include <fuchsia/hardware/pci/cpp/banjo.h>
@@ -77,7 +76,6 @@
         eth_board_client_(parent, ZX_PROTOCOL_ETH_BOARD),
         gpio_client_(parent, ZX_PROTOCOL_GPIO),
         hdmi_client_(parent, ZX_PROTOCOL_HDMI),
-        i2c_client_(parent, ZX_PROTOCOL_I2C),
         codec_client_(parent, ZX_PROTOCOL_CODEC),
         dai_client_(parent, ZX_PROTOCOL_DAI),
         pdev_client_(parent, ZX_PROTOCOL_PDEV),
@@ -112,12 +110,6 @@
   zx_status_t DdkGetProtocol(uint32_t proto_id, void* out_protocol);
 
  private:
-  struct I2cTransactContext {
-    sync_completion_t completion;
-    void* read_buf;
-    size_t read_length;
-    zx_status_t result;
-  };
   struct CodecTransactContext {
     sync_completion_t completion;
     zx_status_t status;
@@ -140,9 +132,6 @@
   zx_status_t RpcHdmi(const uint8_t* req_buf, uint32_t req_size, uint8_t* resp_buf,
                       uint32_t* out_resp_size, zx::handle* req_handles, uint32_t req_handle_count,
                       zx::handle* resp_handles, uint32_t* resp_handle_count);
-  zx_status_t RpcI2c(const uint8_t* req_buf, uint32_t req_size, uint8_t* resp_buf,
-                     uint32_t* out_resp_size, zx::handle* req_handles, uint32_t req_handle_count,
-                     zx::handle* resp_handles, uint32_t* resp_handle_count);
   zx_status_t RpcPdev(const uint8_t* req_buf, uint32_t req_size, uint8_t* resp_buf,
                       uint32_t* out_resp_size, zx::handle* req_handles, uint32_t req_handle_count,
                       zx::handle* resp_handles, uint32_t* resp_handle_count);
@@ -188,15 +177,11 @@
                              uint32_t req_handle_count, zx::handle* resp_handles,
                              uint32_t* resp_handle_count);
 
-  static void I2cTransactCallback(void* cookie, zx_status_t status, const i2c_op_t* op_list,
-                                  size_t op_count);
-
   ProtocolClient<ddk::AmlogicCanvasProtocolClient, amlogic_canvas_protocol_t> canvas_client_;
   ProtocolClient<ddk::ClockProtocolClient, clock_protocol_t> clock_client_;
   ProtocolClient<ddk::EthBoardProtocolClient, eth_board_protocol_t> eth_board_client_;
   ProtocolClient<ddk::GpioProtocolClient, gpio_protocol_t> gpio_client_;
   ProtocolClient<ddk::HdmiProtocolClient, hdmi_protocol_t> hdmi_client_;
-  ProtocolClient<ddk::I2cProtocolClient, i2c_protocol_t> i2c_client_;
   ProtocolClient<ddk::CodecProtocolClient, codec_protocol_t> codec_client_;
   ProtocolClient<ddk::DaiProtocolClient, dai_protocol_t> dai_client_;
   ProtocolClient<ddk::PDevProtocolClient, pdev_protocol_t> pdev_client_;
diff --git a/src/devices/internal/drivers/fragment/proxy-protocol.h b/src/devices/internal/drivers/fragment/proxy-protocol.h
index 0d54b57..8215df7 100644
--- a/src/devices/internal/drivers/fragment/proxy-protocol.h
+++ b/src/devices/internal/drivers/fragment/proxy-protocol.h
@@ -300,31 +300,6 @@
   EthBoardOp op;
 };
 
-// ZX_PROTOCOL_I2C proxy support.
-enum class I2cOp {
-  TRANSACT,
-  GET_MAX_TRANSFER_SIZE,
-};
-
-struct I2cProxyRequest {
-  ProxyRequest header;
-  I2cOp op;
-  size_t op_count;
-  uint32_t flags;
-  uint64_t trace_id;
-};
-
-struct I2cProxyResponse {
-  ProxyResponse header;
-  size_t size;
-};
-
-struct I2cProxyOp {
-  size_t length;
-  bool is_read;
-  bool stop;
-};
-
 enum class SpiOp {
   TRANSMIT,
   RECEIVE,
diff --git a/src/devices/lib/compat/BUILD.gn b/src/devices/lib/compat/BUILD.gn
index 3f5cb01..201ebdb 100644
--- a/src/devices/lib/compat/BUILD.gn
+++ b/src/devices/lib/compat/BUILD.gn
@@ -17,7 +17,7 @@
     "//sdk/fidl/fuchsia.driver.compat:fuchsia.driver.compat_llcpp",
     "//sdk/lib/driver2:devfs_exporter",
     "//sdk/lib/driver2:llcpp",
-    "//sdk/lib/sys/component/llcpp",
+    "//sdk/lib/sys/component/cpp",
     "//zircon/system/ulib/service:service-llcpp",
   ]
 }
diff --git a/src/devices/lib/compat/compat.cc b/src/devices/lib/compat/compat.cc
index a402180..fc4d2fe 100644
--- a/src/devices/lib/compat/compat.cc
+++ b/src/devices/lib/compat/compat.cc
@@ -186,7 +186,7 @@
   // If the child goes out of scope, we should close the devfs connection.
   child->AddCallback(std::make_shared<fit::deferred_callback>(
       [this, name = std::string(child->name()), dev_node]() {
-        (void)outgoing_->RemoveNamedProtocol(name);
+        (void)outgoing_->RemoveProtocol(name);
         vfs_->CloseAllConnectionsForVnode(*dev_node, {});
         devfs_exports_->RemoveEntry(name);
       }));
diff --git a/src/devices/lib/compat/compat.h b/src/devices/lib/compat/compat.h
index c7fb187..18af7d2 100644
--- a/src/devices/lib/compat/compat.h
+++ b/src/devices/lib/compat/compat.h
@@ -14,7 +14,7 @@
 #include <lib/fit/defer.h>
 #include <lib/fpromise/promise.h>
 #include <lib/service/llcpp/service_handler.h>
-#include <lib/sys/component/llcpp/outgoing_directory.h>
+#include <lib/sys/component/cpp/outgoing_directory.h>
 
 #include <unordered_map>
 #include <unordered_set>
diff --git a/src/devices/misc/drivers/compat/api.cc b/src/devices/misc/drivers/compat/api.cc
index b7fc8f6..28899a0 100644
--- a/src/devices/misc/drivers/compat/api.cc
+++ b/src/devices/misc/drivers/compat/api.cc
@@ -180,6 +180,12 @@
   bool has_fragment =
       std::find(dev->fragments().begin(), dev->fragments().end(), name) != dev->fragments().end();
 
+  // TODO(fxbug.dev/103306): We don't actually have composite support yet, so if the
+  // device isn't a composite, just pretend we found it.
+  if (dev->fragments().empty()) {
+    has_fragment = true;
+  }
+
   // TODO(fxbug.dev/103734): Fix sysmem routing and remove this.
   if (!has_fragment && proto_id != ZX_PROTOCOL_SYSMEM) {
     return ZX_ERR_NOT_FOUND;
diff --git a/src/devices/misc/drivers/compat/sysmem_test.cc b/src/devices/misc/drivers/compat/sysmem_test.cc
index 5fd098b..5a81ab3 100644
--- a/src/devices/misc/drivers/compat/sysmem_test.cc
+++ b/src/devices/misc/drivers/compat/sysmem_test.cc
@@ -4,7 +4,7 @@
 
 #include <fidl/fuchsia.sysmem/cpp/wire_test_base.h>
 #include <lib/driver2/logger.h>
-#include <lib/sys/component/llcpp/outgoing_directory.h>
+#include <lib/sys/component/cpp/outgoing_directory.h>
 
 #include <fbl/ref_ptr.h>
 #include <gtest/gtest.h>
diff --git a/src/devices/misc/drivers/packaged/BUILD.gn b/src/devices/misc/drivers/packaged/BUILD.gn
index bdd7de2..79b80a3 100644
--- a/src/devices/misc/drivers/packaged/BUILD.gn
+++ b/src/devices/misc/drivers/packaged/BUILD.gn
@@ -15,7 +15,7 @@
   deps = [
     "//sdk/lib/driver2:inspect",
     "//sdk/lib/driver2:llcpp",
-    "//sdk/lib/sys/component/llcpp",
+    "//sdk/lib/sys/component/cpp",
     "//src/devices/lib/driver:driver_runtime",
   ]
 }
diff --git a/src/devices/misc/drivers/packaged/packaged_driver.cc b/src/devices/misc/drivers/packaged/packaged_driver.cc
index 91ba408..5f3e1bd 100644
--- a/src/devices/misc/drivers/packaged/packaged_driver.cc
+++ b/src/devices/misc/drivers/packaged/packaged_driver.cc
@@ -7,7 +7,7 @@
 #include <lib/driver2/namespace.h>
 #include <lib/driver2/record_cpp.h>
 #include <lib/driver2/structured_logger.h>
-#include <lib/sys/component/llcpp/outgoing_directory.h>
+#include <lib/sys/component/cpp/outgoing_directory.h>
 
 #include <optional>
 
diff --git a/src/devices/tests/composite-driver-v1/test-root/BUILD.gn b/src/devices/tests/composite-driver-v1/test-root/BUILD.gn
index 3ee15a0..774826d 100644
--- a/src/devices/tests/composite-driver-v1/test-root/BUILD.gn
+++ b/src/devices/tests/composite-driver-v1/test-root/BUILD.gn
@@ -20,7 +20,7 @@
   sources = [ "test_root.cc" ]
   deps = [
     ":bind",
-    "//sdk/lib/sys/component/llcpp",
+    "//sdk/lib/sys/component/cpp",
     "//src/devices/lib/driver",
     "//src/devices/tests/composite-driver-v1:fuchsia.composite.test_llcpp",
     "//src/lib/ddktl",
diff --git a/src/devices/tests/composite-driver-v1/test-root/test_root.h b/src/devices/tests/composite-driver-v1/test-root/test_root.h
index 9a09f4a..b813f92 100644
--- a/src/devices/tests/composite-driver-v1/test-root/test_root.h
+++ b/src/devices/tests/composite-driver-v1/test-root/test_root.h
@@ -9,7 +9,7 @@
 #include <lib/async-loop/cpp/loop.h>
 #include <lib/async-loop/default.h>
 #include <lib/inspect/cpp/inspect.h>
-#include <lib/sys/component/llcpp/outgoing_directory.h>
+#include <lib/sys/component/cpp/outgoing_directory.h>
 
 #include <ddktl/device.h>
 
diff --git a/src/devices/tests/v2/devfs_exporter/BUILD.gn b/src/devices/tests/v2/devfs_exporter/BUILD.gn
index ba249c9..bb4fc30 100644
--- a/src/devices/tests/v2/devfs_exporter/BUILD.gn
+++ b/src/devices/tests/v2/devfs_exporter/BUILD.gn
@@ -30,7 +30,7 @@
     ":root_bind",
     "//sdk/lib/driver2:devfs_exporter",
     "//sdk/lib/driver2:llcpp",
-    "//sdk/lib/sys/component/llcpp",
+    "//sdk/lib/sys/component/cpp",
     "//src/devices/lib/driver:driver_runtime",
     "//zircon/system/ulib/service:service-llcpp",
   ]
diff --git a/src/devices/tests/v2/devfs_exporter/root_driver.cc b/src/devices/tests/v2/devfs_exporter/root_driver.cc
index f1abd79..106e35a 100644
--- a/src/devices/tests/v2/devfs_exporter/root_driver.cc
+++ b/src/devices/tests/v2/devfs_exporter/root_driver.cc
@@ -12,7 +12,7 @@
 #include <lib/driver2/record_cpp.h>
 #include <lib/fpromise/bridge.h>
 #include <lib/fpromise/scope.h>
-#include <lib/sys/component/llcpp/outgoing_directory.h>
+#include <lib/sys/component/cpp/outgoing_directory.h>
 
 namespace fdf {
 using namespace fuchsia_driver_framework;
diff --git a/src/devices/tests/v2/driver-runtime/BUILD.gn b/src/devices/tests/v2/driver-runtime/BUILD.gn
index 10d15ad..0426a81 100644
--- a/src/devices/tests/v2/driver-runtime/BUILD.gn
+++ b/src/devices/tests/v2/driver-runtime/BUILD.gn
@@ -39,7 +39,7 @@
     "//sdk/lib/driver2:llcpp",
     "//sdk/lib/driver_runtime",
     "//sdk/lib/driver_runtime:driver_runtime_cpp",
-    "//sdk/lib/sys/component/llcpp",
+    "//sdk/lib/sys/component/cpp",
     "//src/devices/bind/fuchsia.test:fuchsia.test_cpp",
     "//src/devices/lib/driver:driver_runtime",
     "//zircon/system/ulib/service:service-llcpp",
diff --git a/src/devices/tests/v2/driver-runtime/root-driver.cc b/src/devices/tests/v2/driver-runtime/root-driver.cc
index 0795ef7..b7d5a1d 100644
--- a/src/devices/tests/v2/driver-runtime/root-driver.cc
+++ b/src/devices/tests/v2/driver-runtime/root-driver.cc
@@ -17,7 +17,7 @@
 #include <lib/fdf/dispatcher.h>
 #include <lib/fpromise/bridge.h>
 #include <lib/fpromise/scope.h>
-#include <lib/sys/component/llcpp/outgoing_directory.h>
+#include <lib/sys/component/cpp/outgoing_directory.h>
 
 #include <bind/fuchsia/test/cpp/bind.h>
 
diff --git a/src/devices/tests/v2/dynamic_offers/BUILD.gn b/src/devices/tests/v2/dynamic_offers/BUILD.gn
index 68b9b40..f074fec 100644
--- a/src/devices/tests/v2/dynamic_offers/BUILD.gn
+++ b/src/devices/tests/v2/dynamic_offers/BUILD.gn
@@ -33,7 +33,7 @@
     ":root_bind",
     "//sdk/fidl/fuchsia.component.decl:fuchsia.component.decl_llcpp",
     "//sdk/lib/driver2:llcpp",
-    "//sdk/lib/sys/component/llcpp",
+    "//sdk/lib/sys/component/cpp",
     "//src/devices/bind/fuchsia.test:fuchsia.test_cpp",
     "//src/devices/lib/driver:driver_runtime",
     "//zircon/system/ulib/service:service-llcpp",
diff --git a/src/devices/tests/v2/dynamic_offers/root_driver.cc b/src/devices/tests/v2/dynamic_offers/root_driver.cc
index 174066df..56fd1ba 100644
--- a/src/devices/tests/v2/dynamic_offers/root_driver.cc
+++ b/src/devices/tests/v2/dynamic_offers/root_driver.cc
@@ -12,7 +12,7 @@
 #include <lib/driver2/record_cpp.h>
 #include <lib/fpromise/bridge.h>
 #include <lib/fpromise/scope.h>
-#include <lib/sys/component/llcpp/outgoing_directory.h>
+#include <lib/sys/component/cpp/outgoing_directory.h>
 
 #include <bind/fuchsia/test/cpp/bind.h>
 
diff --git a/src/devices/tests/v2/interop/root_driver.cc b/src/devices/tests/v2/interop/root_driver.cc
index c2c7827..e1052c5 100644
--- a/src/devices/tests/v2/interop/root_driver.cc
+++ b/src/devices/tests/v2/interop/root_driver.cc
@@ -12,7 +12,7 @@
 #include <lib/fpromise/bridge.h>
 #include <lib/fpromise/result.h>
 #include <lib/fpromise/scope.h>
-#include <lib/sys/component/llcpp/outgoing_directory.h>
+#include <lib/sys/component/cpp/outgoing_directory.h>
 
 #include <bind/fuchsia/test/cpp/bind.h>
 
diff --git a/src/devices/tests/v2/runtime-dispatcher/BUILD.gn b/src/devices/tests/v2/runtime-dispatcher/BUILD.gn
index 0c99c3c..60413f5 100644
--- a/src/devices/tests/v2/runtime-dispatcher/BUILD.gn
+++ b/src/devices/tests/v2/runtime-dispatcher/BUILD.gn
@@ -33,7 +33,7 @@
     ":root-bind",
     "//sdk/fidl/fuchsia.component.decl:fuchsia.component.decl_llcpp",
     "//sdk/lib/driver2:llcpp",
-    "//sdk/lib/sys/component/llcpp",
+    "//sdk/lib/sys/component/cpp",
     "//src/devices/bind/fuchsia.test:fuchsia.test_cpp",
     "//src/devices/lib/driver:driver_runtime",
     "//zircon/system/ulib/service:service-llcpp",
diff --git a/src/devices/tests/v2/runtime-dispatcher/root-driver.cc b/src/devices/tests/v2/runtime-dispatcher/root-driver.cc
index e47a9fa..ee48623 100644
--- a/src/devices/tests/v2/runtime-dispatcher/root-driver.cc
+++ b/src/devices/tests/v2/runtime-dispatcher/root-driver.cc
@@ -12,7 +12,7 @@
 #include <lib/driver2/record_cpp.h>
 #include <lib/fpromise/bridge.h>
 #include <lib/fpromise/scope.h>
-#include <lib/sys/component/llcpp/outgoing_directory.h>
+#include <lib/sys/component/cpp/outgoing_directory.h>
 
 #include <bind/fuchsia/test/cpp/bind.h>
 
diff --git a/src/devices/tests/v2/services/BUILD.gn b/src/devices/tests/v2/services/BUILD.gn
index 7214fb1..929e49dd 100644
--- a/src/devices/tests/v2/services/BUILD.gn
+++ b/src/devices/tests/v2/services/BUILD.gn
@@ -30,7 +30,7 @@
     ":root_bind",
     "//sdk/fidl/fuchsia.component.decl:fuchsia.component.decl_llcpp",
     "//sdk/lib/driver2:llcpp",
-    "//sdk/lib/sys/component/llcpp",
+    "//sdk/lib/sys/component/cpp",
     "//src/devices/lib/driver:driver_runtime",
   ]
 }
diff --git a/src/devices/tests/v2/services/root_driver.cc b/src/devices/tests/v2/services/root_driver.cc
index ebb318e..6e8d300 100644
--- a/src/devices/tests/v2/services/root_driver.cc
+++ b/src/devices/tests/v2/services/root_driver.cc
@@ -7,7 +7,7 @@
 #include <lib/driver2/logger.h>
 #include <lib/driver2/namespace.h>
 #include <lib/driver2/record_cpp.h>
-#include <lib/sys/component/llcpp/outgoing_directory.h>
+#include <lib/sys/component/cpp/outgoing_directory.h>
 
 namespace fdf {
 using namespace fuchsia_driver_framework;
diff --git a/src/diagnostics/config/sampler/sysmem.json b/src/diagnostics/config/sampler/sysmem.json
index 2a4392a..6ecc2f3 100644
--- a/src/diagnostics/config/sampler/sysmem.json
+++ b/src/diagnostics/config/sampler/sysmem.json
@@ -4,55 +4,49 @@
             "event_codes": [
                 1
             ],
-            "metric_id": 4,
-            "metric_type": "Occurrence",
-            "selector": "bootstrap/driver_manager:root/sysmem/heaps/SysmemContiguousPool:free_at_high_water_mark",
-            "use_legacy_cobalt": true
+            "metric_id": 104,
+            "metric_type": "Integer",
+            "selector": "bootstrap/driver_manager:root/sysmem/heaps/SysmemContiguousPool:free_at_high_water_mark"
         },
         {
             "event_codes": [
                 1
             ],
-            "metric_id": 5,
-            "metric_type": "Occurrence",
-            "selector": "bootstrap/driver_manager:root/sysmem/heaps/SysmemContiguousPool:used_size",
-            "use_legacy_cobalt": true
+            "metric_id": 105,
+            "metric_type": "Integer",
+            "selector": "bootstrap/driver_manager:root/sysmem/heaps/SysmemContiguousPool:used_size"
         },
         {
             "event_codes": [
                 2
             ],
-            "metric_id": 4,
-            "metric_type": "Occurrence",
-            "selector": "bootstrap/driver_manager:root/sysmem/heaps/SysmemAmlogicProtectedPool:free_at_high_water_mark",
-            "use_legacy_cobalt": true
+            "metric_id": 104,
+            "metric_type": "Integer",
+            "selector": "bootstrap/driver_manager:root/sysmem/heaps/SysmemAmlogicProtectedPool:free_at_high_water_mark"
         },
         {
             "event_codes": [
                 2
             ],
-            "metric_id": 5,
-            "metric_type": "Occurrence",
-            "selector": "bootstrap/driver_manager:root/sysmem/heaps/SysmemAmlogicProtectedPool:used_size",
-            "use_legacy_cobalt": true
+            "metric_id": 105,
+            "metric_type": "Integer",
+            "selector": "bootstrap/driver_manager:root/sysmem/heaps/SysmemAmlogicProtectedPool:used_size"
         },
         {
             "event_codes": [
                 3
             ],
-            "metric_id": 4,
-            "metric_type": "Occurrence",
-            "selector": "bootstrap/driver_manager:root/sysmem/heaps/tee_secure:free_at_high_water_mark",
-            "use_legacy_cobalt": true
+            "metric_id": 104,
+            "metric_type": "Integer",
+            "selector": "bootstrap/driver_manager:root/sysmem/heaps/tee_secure:free_at_high_water_mark"
         },
         {
             "event_codes": [
                 3
             ],
-            "metric_id": 5,
-            "metric_type": "Occurrence",
-            "selector": "bootstrap/driver_manager:root/sysmem/heaps/tee_secure:used_size",
-            "use_legacy_cobalt": true
+            "metric_id": 105,
+            "metric_type": "Integer",
+            "selector": "bootstrap/driver_manager:root/sysmem/heaps/tee_secure:used_size"
         }
     ],
     "poll_rate_sec": 600,
diff --git a/src/graphics/bin/agis/agis.cc b/src/graphics/bin/agis/agis.cc
index fd3ae5d..51aa864 100644
--- a/src/graphics/bin/agis/agis.cc
+++ b/src/graphics/bin/agis/agis.cc
@@ -7,50 +7,104 @@
 #include <lib/async-loop/default.h>
 #include <lib/fidl/cpp/binding_set.h>
 #include <lib/sys/cpp/component_context.h>
-#include <lib/syslog/cpp/log_settings.h>
 #include <lib/syslog/cpp/macros.h>
-#include <netinet/in.h>
-#include <zircon/system/ulib/fbl/include/fbl/unique_fd.h>
+#include <lib/zx/object.h>
 
+#include <map>
 #include <unordered_map>
 #include <unordered_set>
 
+#include <sdk/lib/syslog/cpp/log_settings.h>
+
 namespace {
-// Value struct for |registry| below.
-struct RegistryValue {
-  RegistryValue(zx_koid_t process_koid_in, std::string process_name_in, zx::socket agi_socket_in)
-      : process_koid(process_koid_in),
+// ID provided by the client to make a vtc registration unique per client.
+using ClientId = uint64_t;
+
+// ID generated by this AGIS fidl service for each vtc registration.
+using GlobalId = uint32_t;
+
+// Value struct for |registry| map.
+struct Entry {
+  Entry(GlobalId global_id_in, std::string process_name_in, zx_koid_t process_koid_in,
+        zx::socket vulkan_socket_in,
+        fuchsia::gpu::agis::ComponentRegistry::GetVulkanSocketCallback vulkan_socket_callback_in =
+            nullptr)
+      : global_id(global_id_in),
         process_name(std::move(process_name_in)),
-        agi_socket(agi_socket_in.release()) {}
-  zx_koid_t process_koid;
+        process_koid(process_koid_in),
+        vulkan_socket(vulkan_socket_in.release()),
+        vulkan_socket_callback(std::move(vulkan_socket_callback_in)) {}
+  GlobalId global_id;
   std::string process_name;
-  zx::socket agi_socket;
+  zx_koid_t process_koid;
+  zx::socket vulkan_socket;
+  fuchsia::gpu::agis::ComponentRegistry::GetVulkanSocketCallback vulkan_socket_callback;
 };
 
-// Map ids to |RegistryValue|s.
-std::unordered_map<uint64_t, RegistryValue> registry;
+// The global registry that maps global ids to |Entry|s. Valid global ids are greater
+// than zero.
+std::unordered_map<GlobalId, std::shared_ptr<Entry>> registry;
+
+// Erase a registered |id| from |registry|.
+bool EraseEntry(ClientId id, std::unordered_map<ClientId, std::shared_ptr<Entry>> *session) {
+  // Find |id| in the session.
+  auto session_iter = session->find(id);
+  if (session_iter == session->end())
+    return false;
+  auto global_id = session_iter->second->global_id;
+  session->erase(session_iter);
+
+  // Remove |global_id| from the |registry|.
+  auto registry_iter = registry.find(global_id);
+  if (registry_iter == registry.end())
+    return false;
+  registry.erase(registry_iter);
+
+  return true;
+}
+
+bool PeerEndpointIsOpen(const zx::socket &endpoint) {
+  if (!endpoint.is_valid()) {
+    return false;
+  }
+  auto status =
+      zx_object_wait_one(endpoint.get(), ZX_SOCKET_PEER_CLOSED, ZX_TIME_INFINITE_PAST, nullptr);
+
+  if (status == ZX_ERR_TIMED_OUT) {
+    return true;
+  }
+
+  // Report abnormal status for remaining possible return values.
+  if (!(status == ZX_OK || status == ZX_ERR_CANCELED)) {
+    FX_SLOG(ERROR, "PeerEndpointIsOpen", KV("status", status));
+  }
+
+  return false;
+}
+
 }  // namespace
 
+//
+// Register and unregister Vulkan traceable components.
+//
 class ComponentRegistryImpl final : public fuchsia::gpu::agis::ComponentRegistry {
  public:
   ~ComponentRegistryImpl() override {
-    for (const auto &key : keys_) {
-      registry.erase(key);
+    for (auto session_pair : session_) {
+      auto global_id = session_pair.second->global_id;
+      auto i = registry.find(global_id);
+      if (i == registry.end()) {
+        FX_SLOG(ERROR, "Corrupt registry");
+      }
+      registry.erase(global_id);
     }
   }
 
   // Add entries to the |registry| map.
-  void Register(uint64_t id, zx_koid_t process_koid, std::string process_name,
+  void Register(ClientId id, zx_koid_t process_koid, std::string process_name,
                 RegisterCallback callback) override {
     fuchsia::gpu::agis::ComponentRegistry_Register_Result result;
 
-    auto matched_iter = registry.find(id);
-    if (matched_iter != registry.end()) {
-      result.set_err(fuchsia::gpu::agis::Error::ALREADY_REGISTERED);
-      callbac