The hub provides access to detailed structural information about component instances at runtime. The hub provides information such as:
The hub’s structure is mostly read-only. It is not possible to create, rename, delete, or otherwise modify directories and files which form the structure of the hub itself. However, the outgoing directory of an instance may include mutable directories, files, and services which can be accessed through the hub.
The root of a hub directory is always scoped to a particular realm.
The hub of a realm gives information about:
The hub of a realm cannot give information about the parent realm. This structure makes it easy to constrain the parts of the hub particular clients can access.
If a realm requests access to its hub, it can also access the hub of all of its descendant realms, by traversing down the children/
subdirectory.
Like other capabilities, hub access is requested with a use
declaration in the component manifest. This example shows the manifest file of a component that accesses its own hub.
{ "program": { "binary": "bin/program" }, "use": [ { "runner": "elf" }, { "directory": "/hub", "from": "framework", }, ] }
Since the hub is a directory capability, with the right routing it can be exposed upwards, offered downwards or even shared between sibling components. See the advanced routing section for more details.
Once the capability is added to the manifest, a component can access the hub from /hub
.
/hub
The directory structure of /hub
looks like:
/hub ├── children ├── deleting ├── exec ├── component_type ├── id └── url
A hub contains all the information pertaining to a component instance and its realm.
Directories:
Files:
/hub/component_type
: Contains the string static
or dynamic
. A component is static when its definition exists in the parent’s component manifest at compile time. A component is dynamic when the parent defines the component at runtime./hub/id
: Contains a non-negative integer corresponding to the version of a particular instance. A new instance with the same name is a new version and will be given a new ID./hub/url
: Contains the URL string corresponding to this component. The URL is defined by the parent either in its component manifest file or at runtime./hub/children
The directory structure of /hub/children
looks like:
/hub/children ├── foo | ├── children | ├── deleting | ├── exec | ├── component_type | ├── id | └── url ├── bar | ├── children | ├── deleting | ├── exec | ├── component_type | ├── id | └── url └── baz:qux ├── children ├── deleting ├── component_type ├── id └── url
The /hub/children
directory contains the hubs of all created child instances, whether they be static, dynamic, running or stopped.
Note: A child does not have access to the hub of its parent unless its parent offers the hub capability to it. For more information, see advanced routing.
The hub gives a name to each child’s hub directory based on this format:
<instance name>
<collection name>:<instance name>
/hub/deleting
The directory structure of /hub/deleting
looks like:
/hub/deleting ├── baz:qux:1 | ├── children | ├── deleting | ├── component_type | ├── id | └── url └── baz:qux:2 ├── children ├── deleting ├── exec ├── component_type ├── id └── url
Since deletion is not an atomic process, the deleting directory contains information about children that are in the process of being deleted. When a child’s deletion is complete, it is removed from this directory and ceases to exist in its parent’s hub. The full deletion process is explained later in this document.
The hub gives a name to each child’s hub directory based on this format:
<instance name>:<instance id>
<collection name>:<instance name>:<instance id>
Unlike the /hub/children
directory, the instance ID is a part of the format for the /hub/deleting
directory because multiple versions of the same instance may be in the process of deletion and the instance ID is used to distinguish between them.
/hub/exec
The directory structure of /hub/exec
looks like:
/hub/exec ├── exposed ├── in ├── out ├── runtime ├── used └── resolved_url
The /hub/exec
directory is only visible when the instance is started since it contains information about the current execution of the instance.
Directories:
Files:
/hub/exec/resolved_url
: The component's resolved URL in text format./hub/exec/exposed
The instance's exposed services as listed in its manifest file. A component can connect directly to these services from the hub by opening the provided path.
/hub/exec/in
The instance's incoming namespace, as supplied by the component manager. This contains a listing of services and directories accessible to the given component instance. A component can open the provided path to connect directly to these services from the Hub.
/hub/exec/out/
The instance's outgoing namespace, served by the instance itself. A component can connect directly to these services from the hub by opening the provided path.
/hub/exec/runtime
The directory structure of /hub/exec/runtime
looks like:
/hub/exec/runtime └── elf ├── args | ├── 0 | └── 1 ├── process-id └── job-id
If a component has an ELF runtime, then it will have a /hub/exec/runtime/elf
directory.
Directories:
args
: command-line arguments, presented as a series of files from 0
onward.Files:
process-id
: the instance's process id in text format.job-id
: the instance's job id in text format./hub/exec/used
Lists services used at least once by the instance. A service is considered “used” when it is requested by the instance and has been routed successfully by the component manager.
In this example, the parent component instance passes its view of the hub to hub_client
which then maps it as /parent_hub
in its namespace. hub_client
can inspect information about its parent and siblings through /parent_hub
.
In the parent component manifest:
{ "offer": [ // Route the root hub to hub_client. { "directory": "/hub", "from": "framework", "to": "#hub_client", }, // Route the ELF runner capability to hub_client. { "runner": "elf", "from": "parent", "to": "#hub_client", }, ], "children": [ { "name": "hub_client", "url": "fuchsia-pkg://fuchsia.com/hub_test#meta/hub_client.cm", "startup": "eager", }, ],
In hub_client.cml
:
{ "program": { "binary": "bin/hub_client", }, "use": [ { "runner": "elf" }, { "directory": "/hub", "from": "parent", "as": "/parent_hub" } ] }
In this example, hub_client_sibling
exposes its view of the hub to its parent. The realm, in turn, offers that view of the hub as /sibling_hub
to hub_client
. hub_client
maps that view of the hub to its incoming namespace.
In hub_client_sibling.cml
:
{ "program": { "binary": "bin/hub_client_sibling", }, "use": [ { "runner": "elf" }, ], "expose": [ { "directory": "/hub", "from": "framework", }, ], }
In the parent component manifest file:
{ // Route hub_client_sibling's view of the hub to hub_client. "offer": [ { "directory": "/hub", "from": "#hub_client_sibling", "to": "#hub_client", "as": "/sibling_hub", } ], "children": [ { "name": "hub_client_sibling", "url": "fuchsia-pkg://fuchsia.com/hub_test#meta/hub_client_sibling.cm", "startup": "eager", }, { "name": "hub_client", "url": "fuchsia-pkg://fuchsia.com/hub_test#meta/hub_client.cm", "startup": "eager", }, ], }
In hub_client.cml:
{ "program": { "binary": "bin/hub_client", }, "use": [ { "runner": "elf" }, { "directory": "/sibling_hub", "from": "parent", } ] }
To illustrate how the hub changes in response to lifecycle events, consider an example involving 3 components named A
, B
and C
. The manifest of each component is given below:
A.cmx
{ "program": { "binary": "bin/A" }, "use": [ { "directory": "/hub", "from": "framework", }, ] "collections": [ { "name": "coll", "durability": "transient", }, ] }
B.cmx
{ "program": { "binary": "bin/B" }, "children": [ { "name": "baz", "url": "fuchsia-pkg://fuchsia.com/example#meta/C.cm" "startup": "eager", } ] }
C.cmx
// C.cmx { "program": { "binary": "bin/C" } }
Also consider 3 instances, one for each component:
A
-> foo
B
-> bar
C
-> baz
foo
can perform various actions that modify the hub:
foo
can see its own hub by listing the contents of /hub
. In Rust, this looks like:
let entries = std::fs::read_dir("/hub")?; for entry in entries { println!("{}", entry?.path()); }
Without making any changes to the component hierarchy, the hub will look like the following:
/hub ├── children ├── deleting ├── exec | ├── exposed | ├── in | ├── out | ├── runtime | └── resolved_url ├── component_type => "static" ├── id => "0" └── url => "fuchsia-pkg://fuchsia.com/example#meta/A.cm"
A component must connect to the Realm framework service to manipulate its dynamic children. In Rust, this looks like:
let realm = connect_to_service::<fsys::RealmMarker>()?;
foo
can define an instance of B
called bar
under the collection coll
. In Rust, this looks like:
// Define the child and the collection let mut collection_ref = fsys::CollectionRef { name: String::from("coll") }; let child_decl = fsys::ChildDecl { name: Some(String::from("bar")), url: Some(String::from("fuchsia-pkg://fuchsia.com/example#meta/B.cm")), startup: Some(fsys::StartupMode::Lazy), }; // Create the child realm.create_child(&mut collection_ref, child_decl).await??;
After executing this code, the hub changes to the following:
/hub ├── children | └── coll:bar | ├── children | ├── deleting | ├── component_type => "dynamic" | ├── id => "1" | └── url => "fuchsia-pkg://fuchsia.com/example#meta/B.cm" ├── deleting ├── exec | ├── exposed | ├── in | ├── out | ├── runtime | └── resolved_url ├── component_type => "static" ├── id => "0" └── url => "fuchsia-pkg://fuchsia.com/example#meta/A.cm"
Note: The instance baz
(which is a child of bar
) does not appear in the hub yet.
Note: The instance bar
has not been started, so it does not have an exec
directory.
foo
can now bind to the instance bar
and start it. In Rust, this looks like the following:
// Create a reference to the dynamic child let mut child_ref = fsys::ChildRef { name: "bar".to_string(), collection: Some("coll".to_string()), }; // Create a proxy for the exposed directory let (dir_proxy, server_end) = create_proxy::<DirectoryMarker>()?; // Bind to the child realm.bind_child(&mut child_ref, server_end).await??;
After executing this code, the hub changes to the following:
/hub ├── children | └── coll:bar | ├── children | | └── baz | | ├── children | | ├── deleting | | ├── exec | | | ├── exposed | | | ├── in | | | ├── out | | | ├── runtime | | | ├── used | | | └── resolved_url | | ├── component_type => "static" | | ├── id => "0" | | └── url => "fuchsia-pkg://fuchsia.com/example#meta/C.cm" | ├── deleting | ├── exec | | ├── exposed | | ├── in | | ├── out | | ├── runtime | | ├── used | | └── resolved_url | ├── component_type => "dynamic" | ├── id => "1" | └── url => "fuchsia-pkg://fuchsia.com/example#meta/B.cm" ├── deleting ├── exec | ├── exposed | ├── in | ├── out | ├── runtime | ├── used | └── resolved_url ├── component_type => "static" ├── id => "0" └── url => "fuchsia-pkg://fuchsia.com/example#meta/A.cm"
Note: The instance baz
is automatically created and started as a static child of bar
.
Note: The instances bar
and baz
now have exec
directories since they both have been started.
foo
can destroy the instance bar
. In Rust, this looks like the following:
// Create a reference to the dynamic child let mut child_ref = fsys::ChildRef { name: "bar".to_string(), collection: Some("coll".to_string()), }; // Destroy the child realm.destroy_child(&mut child_ref).await??;
The above code begins the deletion process for bar
. This process has several stages, most of which occur asynchronously. As a result, the hub’s structure changes several times.
bar
is marked for deletion. The hub changes to the following:
/hub ├── children ├── deleting | └── coll:bar:1 | ├── children | | └── baz | | ├── children | | ├── deleting | | ├── exec | | | ├── exposed | | | ├── in | | | ├── out | | | ├── runtime | | | ├── used | | | └── resolved_url | | ├── component_type => "static" | | ├── id => "0" | | └── url => "fuchsia-pkg://fuchsia.com/example#meta/C.cm" | ├── deleting | ├── exec | | ├── exposed | | ├── in | | ├── out | | ├── runtime | | ├── used | | └── resolved_url | ├── component_type => "dynamic" | ├── id => "1" | └── url => "fuchsia-pkg://fuchsia.com/example#meta/B.cm" ├── exec | ├── exposed | ├── in | ├── out | ├── runtime | ├── used | └── resolved_url ├── component_type => "static" ├── id => "0" └── url => "fuchsia-pkg://fuchsia.com/example#meta/A.cm"
baz
is stopped. The hub changes to the following:
/hub ├── children ├── deleting | └── coll:bar:1 | ├── children | | └── baz | | ├── children | | ├── deleting | | ├── component_type => "static" | | ├── id => "0" | | └── url => "fuchsia-pkg://fuchsia.com/example#meta/C.cm" | ├── deleting | ├── exec | | ├── exposed | | ├── in | | ├── out | | ├── runtime | | ├── used | | └── resolved_url | ├── component_type => "dynamic" | ├── id => "1" | └── url => "fuchsia-pkg://fuchsia.com/example#meta/B.cm" ├── exec | ├── exposed | ├── in | ├── out | ├── runtime | ├── used | └── resolved_url ├── component_type => "static" ├── id => "0" └── url => "fuchsia-pkg://fuchsia.com/example#meta/A.cm"
bar
is stopped. The hub changes to the following:
/hub ├── children ├── deleting | └── coll:bar | ├── children | | └── baz | | ├── children | | ├── deleting | | ├── component_type => "static" | | ├── id => "0" | | └── url => "fuchsia-pkg://fuchsia.com/example#meta/C.cm" | ├── deleting | ├── component_type => "dynamic" | ├── id => "1" | └── url => "fuchsia-pkg://fuchsia.com/example#meta/B.cm" ├── exec | ├── exposed | ├── in | ├── out | ├── runtime | ├── used | └── resolved_url ├── component_type => "static" ├── id => "0" └── url => "fuchsia-pkg://fuchsia.com/example#meta/A.cm"
baz
is marked for deletion. The hub changes to the following:
/hub ├── children ├── deleting | └── coll:bar:1 | ├── children | ├── deleting | | └── baz:0 | | ├── children | | ├── deleting | | ├── component_type => "static" | | ├── id => "0" | | └── url => "fuchsia-pkg://fuchsia.com/example#meta/C.cm" | ├── component_type => "dynamic" | ├── id => "1" | └── url => "fuchsia-pkg://fuchsia.com/example#meta/B.cm" ├── exec | ├── exposed | ├── in | ├── out | ├── runtime | ├── used | └── resolved_url ├── component_type => "static" ├── id => "0" └── url => "fuchsia-pkg://fuchsia.com/example#meta/A.cm"
baz
is deleted. The hub changes to the following:
/hub ├── children ├── deleting | └── coll:bar:1 | ├── children | ├── deleting | ├── component_type => "dynamic" | ├── id => "1" | └── url => "fuchsia-pkg://fuchsia.com/example#meta/B.cm" ├── exec | ├── exposed | ├── in | ├── out | ├── runtime | ├── used | └── resolved_url ├── component_type => "static" ├── id => "0" └── url => "fuchsia-pkg://fuchsia.com/example#meta/A.cm"
bar
is deleted. The hub changes to the following:
/hub ├── children ├── deleting ├── exec | ├── exposed | ├── in | ├── out | ├── runtime | ├── used | └── resolved_url ├── component_type => "static" ├── id => "0" └── url => "fuchsia-pkg://fuchsia.com/example#meta/A.cm"