blob: b817a5e3e18137b6755573390c0c0f8c49047646 [file] [log] [blame] [view]
{% set rfcid = "RFC-0124" %}
{% include "docs/contribute/governance/rfcs/_common/_rfc_header.md" %}
# {{ rfc.name }} - {{ rfc.title }}
<!-- *** DO NOT EDIT ABOVE THIS LINE -->
{% import 'docs/_common/_doc_widgets.md' as widgets %}
<!--
*** This should begin with an H2 element (for example, ## Summary).
-->
## Summary
This RFC describes a mechanism to make source or prebuilt artifacts from the
Fuchsia source tree or from petal repositories available for the assembly of a
Fuchsia [glossary.product] outside of the Fuchsia source tree.
The mechanism comprises two essential aspects:
1. **Description** of the artifacts using metainformation suitable to select
compatible or otherwise desirable versions and variants of each artifact into
the product to be assembled.
2. **Propagation** of the artifacts such that they can be identified,
referenced, and accessed from both the source repository where they originate
and the integration repository where they are used.
## Motivation
As laid out in the [Decentralized Product Integration roadmap][roadmap]
document, it is desired to assemble products outside of the Fuchsia source
tree. To accomplish this, it is necessary to execute the [standalone image
assembly tools][rfc0072] outside of the Fuchsia source tree, and to supply them
with the necessary source or prebuilt artifacts. The Fuchsia source tree
supplies, among other artifacts, Zircon (the Fuchsia kernel) and system
packages. Various petal repositories supply packages for additional components
such as product specific components and runners.
The Fuchsia source tree artifacts are produced in a regular cadence of versions.
At the same times, versions of the Fuchsia SDK are produced as well, which are
regularly imported by the petal repositores and used in turn to produce their
own Fuchsia artifacts in the petal repositories' cadence of versions.
The release cadences of the petal repositories in general are not aligned with
the release cadence of the Fuchsia source tree, and identifiers of the versions
differ. A petal may choose to align its release candence with Fuchsia and for
example produce a release for every release of the Fuchsia SDK.
Each such artifact version is also potentially produced in different variants,
for example executables for various processor architectures, debug vs. optimized
variants, variants designated by some "stable" vs. "latest" labels to
distinguish trunk releases from stabilization branch releases, or variants with
build time features enabled or disabled.
In order to assemble a product from such artifacts in an integration repository
that is separate from the Fuchsia source tree, the artifacts must be made
available to the integration repository. Moreover, the artifacts needed by the
product integration must, for each version and variant of the product, be
selected at suitable versions and variants of the artifacts.
The current process of product integration is called Global Integration. The
prebuilt artifacts from petal repositories are imported back into the Fuchsia
source tree in a process called rolling. The Fuchsia products are then assembled
as final build products from all artifacts from the Fuchsia source tree at the
most recent source revision, together with the prebuilt packages from the petal
repositories at the pinned version that has reached the integration repository.
This process is shown in Figure 1.
**Figure 1 - Global Integration Process ([legend](#appendix))**
![Relationship of all cooperating parts in current system][fig1]
This process has several drawbacks:
* Product assembly configuration is kept in the Fuchsia source tree, rather than
under the control of the [glossary.product-owner].
* The version of the Fuchsia system is always ahead of the version of the
Fuchsia SDK that any petal artifact was built with, because the Fuchsia system
is built from the most recent source revision of the Fuchsia source tree, but
the SDK of the petal artifacts had been built from an earlier revision.
* If artifacts from different petals have version compatibility requirements
between each other (for example the Flutter runner and an aot-compiled Flutter
application), these can only be maintained by specifically rolling them
together in the same roller.
* Even if a petal artifact is under the control of a product owner, the version
at which it is used for product integration is not, because the rolling
process that makes a new version of an artifact available for product
integration is coordinated with all other rollers for all other petal
artifacts, and can be blocked by failing integration tests for any other
product.
The proposed Decentralized Product Integration process improves upon those
drawbacks as a direct conseqence of enabling product assembly outside the
Fuchsia repository:
* Product assembly configuration is kept in a separate integration repository
for each product, which can be controlled by the product owner.
* The Fuchsia artifacts can be selected at a version compatible with the Fuchsia
SDK that the petal artifacts were built with, to achieve ABI compatibility
between all the artifacts of the product. For example, something resembling
ABI revision as stipulated in [RFC-0002][rfc0002] and corresponding to the
required 6 week ABI compatibility window is indicated by the first component
of the Fuchsia release version number, which is incremented every time a
Fuchsia release branch is created. Thus it will be possible to align all
artifacts in a product on equal first components of Fuchsia release version
numbers of the SDK they were built with.
* Artifact versions from different petal repositories can be selected directly
such that they satisfy mutual compatibility requirements, and can be advanced
to next versions according to directly specified constraints, without the need
to set up a dedicated roller for the pair of artifacts. For example, a flutter
app and the corresponding flutter runner can be selected on equal flutter and
dart release version numbers directly.
* Products can select the version and variant of all contributing artifacts
following their own release cadence without blocking on other products
selecting that version too.
## Stakeholders
_Facilitator:_ abarth@google.com
_Reviewers:_ aaronwood@google.com (Assembly), etryzelaar@google.com (SWD),
wittrock@google.com (SWD), atyfto@google.com (Fuchsia Build Infrastructure),
marvinpaul@google.com (TUF Server Infrastructure), jsankey@google.com
(Security), enoharemaien@google.com (Security & Privacy), schilit@google.com
(related RFC author).
_Consulted:_ Members of Software Delivery team, Managed OS team, and
Infrastructure team.
_Socialization:_ A draft of this RFC was sent to the FEC discuss mailing list
for comment, in addition to discussion with the consulted teams.
## Design
Before the design overview is presented, some terminology and concepts are
introduced or clarified.
### Terminology
In the following paragraphs **Bold Terms** are the ones whose meaning is
explained by the surrounding text.
The **Fuchsia Source Tree** comprises multiple different git repositories that
are configured together into one source tree by a tool called **jiri**, based on
configuration that is kept in a git repository called **integration.git**. The
most important of those git repositories is **fuchsia.git**, but there are many
others, including third party ones. The jiri configuration also controls the
inclusion of prebuilt artifacts into dedicated parts of the source tree. The
prebuilt artifacts themselves are stored under revision control in a storage
service called **CIPD**, and the jiri configuration that describes the location
and revision of the prebuilt artifacts in CIPD is under git revision control in
the integration.git repository.
A regularly running build job called a **Roller** attempts to update the version
of a prebuilt artifact included in the Fuchsia Source Tree. If all desired build
products of the Fuchsia tree can be built and tested with the updated version of
the artifact, the change to the Fuchsia tree is committed, and the updated-to
version of the artifact is now the **pinned** one. There can be separate rollers
for different prebuilt artifacts.
In the status quo, the Fuchsia Source Tree simultaneously takes the role of both
the Source Repository for many of the artifacts used in Fuchsia Products and the
**Integration Repository** where each Fuchsia Product is assembled from its
parts.
**Artifact** refers to any ingredient used in the assembly of a product. Such
artifacts are obtained from **Source Repositories**, either directly as one of
the source items, or as a build product obtained from executing the build
process in the repository. Most such artifacts are prebuilt Fuchsia packages,
but they also comprise Zircon, the Fuchsia kernel, and may soon also comprise
[subassembly specification][rfc-subassembly] files. The Zircon kernel and
prebuilt packages are prebuilt artifacts; a subassembly specification file is a
source artifact.
The artifacts obtained from one Source Repository are eventually **released** to
an **Artifact Store**. Such releases can be made manually, or they can happen
automatically in a scheduled cadence. Either way the resulting artifacts are
published to a dedicated Artifact Store for the source repository. The Artifact
Store can be any place where the artifacts can be found from outside their
source repository, without having to refer to the source repository, let alone
execute its build process. As such, Artifact Stores can be e.g. a CIPD
directory, a GCS bucket, or a TUF repository. For the process designed here, the
specific kind of Artifact Store is not essential.
**TUF Repositories** are used for [Software Delivery to Fuchsia
devices][fuchsia-swd], of newly rebuilt packages to developer Fuchsia devices or
emulators in development, and of OTA updates to Fuchsia devices in
production. The specific way of using TUF in Fuchsia on top of a content
addressed storage service is especially suitable for storing [Fuchsia
packages][fuchsia-package], and since many of the artifacts are Fuchsia
packages, there is a preference in this design to use TUF Repositories as the
Artifact Store.
**Attributes** are key-value properties that describe artifacts such that their
versions and variants can be discerned. The key describes a dimension of
variability and the value is the place along that dimension the artifact is
at. Attributes uniformly describe both versions and variants of artifacts. In
most situations it's not necessary to distinguish attributes that describe
versions from attributes that describe variants. The publisher of an artifact
can specify attributes like the following:
* The CPU architecture variant, `x64` vs. `arm64`.
* The `debug` vs. `optimized` compilation variant.
* The commit version of the source repository from which the artifact was built.
* Build parameters with which the artifact was built.
* The Fuchsia SDK version the artifact was built with.
* The semantic version of the built artifact. For example, a Chromium release
version such as `M88.xxx`, or a Cast release version such as `1.56.xxx`.
* The API level and ABI revisions as stipulated by [RFC-0002][rfc0002] of the
Fuchsia system Artifacts, once implemented.
* The API level or SDK version provided by a petal, which are out of scope of
[RFC-0002][rfc0002] yet as important as the system ABI. For example, the
release versions of the Flutter SDK a Flutter application was built with.
**Transitive Attributes**: Some attributes describe *transitive* properties of
the artifacts, in that they pertain to properties of the *inputs* the artifacts
were built from. For example, the Fuchsia SDK version, or the Flutter version
are actually properties of precursor artifacts used to build the described
artifact. It is anticipated that such transitive properties extend even
further. Therefore, attribute values are allowed to be objects which contain
key-value pairs in turn, recursively.
**Dependency Attributes**: Dependencies (including their versions and variants)
of artifacts can be expressed as attributes too. However, Fuchsia packages are
[*hermetic*][fuchsia-package-hermetic] and as such already contain their
dependencies. Therefore, dependencies will usually not appear often in
attributes of Fuchsia artifacts.
### Status Quo
In the current Global Integration process, as illustrated in Figure 2, the petal
build process uses a [CIPD][cipd] directory as its *artifact store*. The
artifact store keeps the artifacts under a path name (called "package" in CIPD,
with no direct relation to Fuchsia packages). CIPD supports the ability to store
multiple "instances" under the same path name of the "package", and to attach
"tags" and "refs" to the instances. The variant meta information is encoded in
the pathname components of the stored artifact. The version metainformation is
encoded in tags and refs of CIPD package instances. The product build process
obtains the pinned version of the artifacts through rolling and always builds
the product from the pinned versions of the petal artifacts.
**Figure 2 - Current system structure**
![Relationship of all cooperating parts in current system][fig2]
### Propagation of extended metainformation through the artifact store
The design proposed here changes the structure of the artifact store in order to
keep metainformation attributes of the released artifacts. Products can then
select suitable artifacts based on criteria specified in terms of attribute
values.
The design involves three additional config files, and three tools to operate on
the config files to interact with the artifact stores. The files are
`artifact_groups.json`, kept in the artifact store; `artifact_spec.json` and
`artifact_lock.json`, kept in the product integration repository. The tools
`upload`, `update`, and `fetch` operate on the files and the artifacts, as shown
in Figure 3, as follows:
1. Each new release of a group of artifacts from a petal or Fuchsia Build
Process creates a new artifact group inside the artifact store associated
with the source repository. The attributes of the released artifacts are
recorded in the file `artifact_groups.json`, which is also kept in the
artifact store.
* The Petal Build Process uses the `upload` tool to publish newly released
artifacts to the artifact store and to update the `artifact_groups.json`
file.
2. In the product integration repository, an `artifact_spec.json` file keeps
the lists from which artifact stores to obtain artifacts for a product
assembly in this integration repository, and with what attributes.
* The `update` tool is used to select the set of artifacts to actually be
used for the assembly using the `artifact_spec.json` file from the
integration repository and the `artifact_groups.json` files from all
artifact stores listed in the `artifact_spec.json` file.
3. The set of artifacts selected by the `update` tool is stored in the
`artifact_lock.json` file.
* The `fetch` tool downloads all the artifacts mentioned in the
`artifact_lock.json` file in order to make them available to the Product
Build Process in the integration repository.
How these tools relate to the execution of build rules depends on the specific
build system used in the product integration repository. See the
[implementation](#implementation) section for more discussion.
The tools allow the presence of multiple `artifact_spec.json` and
`artifact_lock.json` files in the integration repository.
**Figure 3 - Relationship of all the cooperating parts**
![Relationship of all cooperating parts in proposed system][fig3]
### Logical structure inside the artifact store
The resulting logical structure of artifacts and artifact groups inside an
artifact store looks like the following example:
```
└── artifact_groups
|
├── 92d483e5-ac7d-4029-a7db-e2ee6a8365c7
| |
| ├── web_engine
| |
| └── cast_runner
|
└── c907ff3f-cb15-4a7f-bb79-8cc23c0ff445
|
├── web_engine
|
└── cast_runner
```
Each release of a group of artifacts appears with an opaque name, and each
artifact appears as an entry in the group with a recognizable name.
This structure is recorded, together with the associated attributes of the
release and of the individual artifacts as a JSON data structure in the
`artifact_groups.json` file, described in more detail below.
### Uses of artifact and group names
The JSON data use the group names and artifact names to associate the recorded
attributes with the artifacts they describe. The names of both groups and
artifacts are used to reference them in expressions that select the artifacts
into a product assembly workspace, in the file `artifact_spec.json`. The
artifact names are also used as file names under which the `fetch` tool stores
their locally downloaded copies.
However, neither name is used to designate the storage location of an artifact
in the artifact store. Instead, all artifacts are stored under their content
addresses, and the content address is recorded for each artifact in
`artifact_groups.json` as the `merkle` property.
The content address used is the [Fuchsia **merkle root**][fuchsia-merkleroot] of
the immediate content of the artifact. Some artifacts, specifically Fuchsia
packages, are represented by further storage components ("blobs") that are
transitively contained in the immediate content of the artifact. These
additional components are stored under their content addresses, but not
explicitly mentioned in `artifact_groups.json`.
### Uniqueness invariants
There are a few uniqueness invariants to be maintained in the Artifact
Store. These invariants guarantee that artifacts can be unambiguously selected
by referencing their names and attributes:
1. The group name must be unique inside the artifact store over its entire
history.
2. The artifact name must be unique within each group.
3. The attributes of each artifact in one group must be unique among all
artifacts of the same name in all groups currently in the artifact store.
These uniqueness invariants are maintained by the `upload` tool when a new
artifact group is added to the artifact store. The `update` tool also checks the
uniqueness and refuses to compute an update if the invariants are not satisfied
for all the `artifact_group.json` files it accesses. The artifact store itself
does not need to play any role in maintaining these invariants.
### Group names are opaque
It's tempting to name artifact groups after attribute values to make the
artifact store more intelligible or navigable. However, this only works well
when there are *few* attributes, and a *fixed* set of them. Once the attribute
set scales to *many* attributes, and an *open* set of them, constructing names
from them becomes tedious purely because of the combinatorial explosion of the
number of possible permutations even of a single set of attribute values (i.e.,
is architecture first or last or in the middle?). Also reading such names is no
longer easy on the eye, and maintaining the uniqueness invariant, especially
across history, also is more cumbersome.
Therefore, since this design specifically aims to support large and open
attribute sets, group names will be chosen to be opaque and to satisfy the
required uniqueness invariants. In the example above the group names are random
UUIDs. An alternative would be to use monotonically increasing padded integers.
### The `artifact_groups.json` file
The `artifact_groups.json` file lists all the groups, the contained artifacts,
and the associated attributes inside that artifact store.
The following example shows the `artifact_groups.json` corresponding to the
artifact store example shown above. Attributes are associated with an artifact
group, in which case all the attributes apply to all artifacts in the
group. Attributes can also be applied to individual artifacts only.
The set of attributes itself is not prescribed by this design. All attributes
are chosen and assigned by the build and release process that produces the
artifacts in the source repository. Attributes assigned are merely required to
maintain the uniqueness invariant above.
```
{
"schema_version": "https://fuchsia.dev/schemas/artifact_groups_schema.json",
"version": 15,
"artifact_groups": [
{
"name": "92d483e5-ac7d-4029-a7db-e2ee6a8365c7",
"attributes": {
"petal": "chromium.org",
"version": "chrominum_release_20210304",
"architecture": "arm64",
"sdk_version": "2.20210303.3.1",
"creation_time": "1622696983",
"commit": "b26e44e9910608a2d0aec9b38e003a04a2da06df"
},
"artifacts": [
{
"name": "web_engine",
"merkle":"90f67b10ded655852acb78a852ac5451486fc1e7378ce53368386244ce8f6e66",
"type": "package",
"attributes": {
"runner_version": "2.20210301.1.3"
}
},
{
"name": "cast_runner",
"merkle": "3394db36d228f4c719d055c394938c5a881ca6eea7ad3af0ad342e764cadc8b3",
"type": "package"
}
]
},
{
"name": "c907ff3f-cb15-4a7f-bb79-8cc23c0ff445",
"attributes": {
"petal": "chromium.org",
"version": "chrominum_release_20210402",
"architecture": "arm64",
"sdk_version": "2.20210303.3.4",
"creation_time": "1622157425",
"commit": "2dd76ad2298dfb869ef83c10b84b62485dc8a573"
},
"artifacts": [
{
"name": "web_engine",
"merkle": "acb78a852ac5451486fc1e7378ce53368386244ce8f6e6690f67b10ded655852",
"type": "package",
"attributes": {
"runner_version": "2.20210225.1.4"
}
},
{
"name": "cast_runner",
"merkle": "19d055c394938c5a881ca6eea7ad3af0ad342e764cadc8b33394db36d228f4c7",
"type": "package"
}
]
}
]
}
```
Each group object in the `artifact_groups` list contains 3 properties: `name` is
the name of the group, `artifacts` is the list of artifacts in the group, and
`attributes` are the attributes shared by all artifacts in the group.
Attributes that apply to only a single artifact are included in the object that
represents that artifact directly. Attributes of this artifact are appended to
attributes of the group to get the complete set of artifact attributes.
The version number is a monotonically increasing number. This number is used by
the `update` tool to protect against the accidental roll back to an older
version of the `artifact_groups.json` file.
### Mutability of artifact groups and their attributes
In typical usage, each released build of a set of artifacts would be recorded as
a new group. Thus group names, the set of artifacts in them, and most of their
attributes are conceptually immutable. Exceptions would be attributes that
reflect information about artifacts that arise after the artifacts are
released. An example would be the changing test result status for artifacts from
manual tests of the artifacts carried out after they are released. Attributes
that reflect such information may be updated.
In principle, however, nothing precludes artifact group contents as well as
names or attributes from changing over time. It is conceivable that this could
be usefully employed in a product assembly pipeline, but no use will be made of
it in the proposed application (see the [implementation](#implementation)
section below).
### Product Build Process
The Product Build Process in the integration repository establishes an
`artifact_spec.json` file, called "spec file" below. This file lists the
artifacts needed by the product to be assembled in the integration repository,
the artifact stores to obtain them from, and the patterns and constraints of the
attributes of the artifacts. The type of the artifact store is described as
well, and multiple different types of artifact stores can co-exist in one spec
file.
Specifically, prebuilt artifacts can be stored in the integration repository
itself, and can be referenced in the spec file as from an artifact store of type
"local".
The Product Build Process in the integration repository employs the `update`
tool to match the `artifact_groups.json` files in all the artifact stores
mentioned in the spec file, and compute the set of specific artifact variants
and versions to use. This set is recorded in an `artifact_lock.json` file. This
file is meant to be committed as a source file in the repository.
The `fetch` tool reads the `artifact_lock.json` file and downloads all artifacts
to the integration repository. The artifacts are not meant to be submitted as a
source file to the repository. However, the artifacts are identified in the
`artifact_lock.json` file by their content addresses taken from the
`artifact_groups.json` files. This ensures that the committed content of
`artifact_lock.json` fully determines the content of the artifacts supplied to
the build, and thus that the product assembly build is hermetic and reproducible
with regard to the artifacts selected in the `artifact_lock.json` file.
## Implementation
The design proposed here will first be implemented to [move the workstation
product integration to a separate repository][rfc0095].
The required details of the `artifact_spec.json` and how it is processed
against the `artifact_groups.json` files in order to produce the
`artifact_lock.json` file is described in [another RFC][rfc-spec].
### Migration of existing petals and products
As long as petal prebuilts are still needed in the Fuchsia source tree (set up
from integration.git), they need to be uploaded to both CIPD for consumption by
the gn build rules in the Fuchsia source tree and to the artifact store used by
out of tree products. Eventually some petal prebuilts will no longer be needed
in the Fuchsia source tree and the upload to CIPD can stop.
There is no intention to replace the mechanisms to setup the Fuchsia source tree
itself using jiri, as all the Fuchsia platform artifacts as well as the SDK and
some products continue to be built from the Fuchsia source tree.
### Artifact Store
The implementation for workstation and its petals will use TUF repositories as
the artifact stores. The only entry in the `targets.json` file of the TUF
repository is for the `artifact_groups.json` file. The artifacts themselves are
stored in the content addressed blob store maintained by the TUF server
infrastructure used for Fuchsia, and their corresponding blob addresses recorded
in the `artifact_groups.json` file.
Additionally, the artifacts could also be listed in `targets.json` under the
directories and artifact names, but nothing in this design requires it. (This is
just mentioned for historical completeness because it used to be done in case
someone would expect the `targets.json` entries. See the
[alternatives](#alternatives) section below.)
### Fuchsia package structure dependencies
The three tools proposed here that interact with the artifact stores (upload,
update, fetch) operate on Fuchsia packages and as such, make assumptions about
the structure of Fuchsia packages. Specifically it is assumed that a Fuchsia
package consists of a `meta.far` blob that contains the content addresses,
expressed as [merkle roots][fuchsia-merkleroot], of the other blobs that also
belong to the package. The list of blobs referenced in the `meta.far` is
obtained using tools supplied by the Fuchsia SDK, and thus the artifact storage
tools (upload, update, fetch) do not depend on the internal structure of the
`meta.far`.
### Relation of tools with build rules
How the `upload`, `update`, and `fetch` tools relate to the execution of build
rules depends on the specific build system in the source repository, and the
product integration repository, as well as on what infastructure the build
executes on.
The `upload` tool is expected to be executed after the build system finished
building.
Usually, the `update` tool is expected to be used outside of build rules, before
the build executes.
The `fetch` tool may either be used before build rules execute, or if the build
system supports it, as part of the build system execution. For the workstation,
[`bazel`][rfc-bazel] is used as the build system, which supports setting up the
input file set in so called "workspace rules".
### Names of the tool executables
The tools `upload`, `update`, `fetch` may eventually be implemented under other
names than the conceptual names used in this design. Specifically, they may be
implemented as plugins to the `ffx` tool, or as tools to be produced by `bazel`
build rules. An initial prototype implements them as python scripts called
`artifact_upload.py`, `artifact_update.py`, and `artifact_fetch.py`.
### File syntax
The Syntax of the `artifact_groups.json` file is given by the
following JSON schema. The JSON schemata of the `artifact_spec.json` and
`artifact_lock.json` files are documented in a [separate RFC][rfc-spec].
```
{
"$schema": "https://json-schema.org/draft/2019-09/schema",
"$id": "https://fuchsia.dev/schemas/artifact_groups_schema.json",
"type": "object",
"additionalProperties": false,
"required": [
"contents",
"schema_version",
"version"
],
"properties": {
"schema_version": {
"type": "string"
},
"version": {
"type": "integer"
},
"artifact_groups": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"name": {
"type": "string"
},
"attributes": {
"type": "object"
},
"artifacts": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"name",
"merkle",
"type",
],
"properties": {
"name": {
"type": "string"
},
"merkle": {
"type": "string"
},
"attributes": {
"type": "object"
},
"type": {
"type": "string"
}
}
}
}
}
}
}
}
}
```
### File semantics
The file format semantics for `artifact_groups.json` is described here. The
detailed semantics of `artifact_spec.json` and `artifact_lock.json` are again
documented in a [separate RFC][rfc-spec].
```
{
"schema_version": SCHEMA_VERSION,
"version": VERSION,
"artifact_groups": [
ARTIFACT_GROUP,
...
]
}
```
SCHEMA\_VERSION
> A string that indicates the url of JSON schema of the `artifact_groups.json`.
VERSION
> An integer that indicates the version number of the `artifact_groups.json`.
This number should increase when a newer version is published.
ARTIFACT\_GROUP
> Each ARTIFACT\_GROUP is an object whose format is the following:
```
{
"name": NAME,
"attributes": ATTRIBUTES,
"artifacts": [
ARTIFACT,
...
]
}
```
NAME
> A String that provides a means to reference this artifact group.
ATTRIBUTES.
> An object defined by publisher.
ARTIFACT
> Each ARTIFACT is an object whose format is the following:
```
{
"name": NAME,
"merkle": MERKLE,
"type": TYPE,
"attributes": ATTRIBUTES
},
```
NAME
> A String that contains the name of this artifact.
MERKLE
> A String of the merkle root of the artifact.
TYPE
> A String that contains the type of this artifact. If the `TYPE` is
> `"package"`, then this artifact in the blob referenced by its merkle root is
> the `meta.far` of a fuchsia package, and the blobs transitively referenced in
> the `meta.far` are always handled together with the meta.far blob. Otherwise,
> the artifact consists only of the single blob whose merkle is listed.
ATTRIBUTES
> An object similar to ATTRIBUTES defined above. This is where the artifact
> specific attributes are defined.
## Security Considerations
The design in this RFC does not change the trust model between the existing
actors. This mechanism only changes the storage structure, not the trust
relationship between the actors using the artifact stores, or between the actors
and the artifact stores.
### Trust Model
As in the status quo using CIPD as artifact store, the product build process
needs to trust the artifact store. This trust is currently based on the access
control and authentication the artifact store applies to the petal and Fuchsia
build processes whence the artifacts are supplied to the store, and on the
reliance on the build infrastructure and environment to resolve the configured
names of the artifact stores to access the right artifact stores.
Once more artifact stores and product integration repositories are established,
the bases for trust in them need to be assessed, and trustworthiness of the
assembled product needs to be assessed correspondingly.
In the future, additional mechanisms above the currently used ones may be
established, for example validation of signatures such as the ones on the TUF
repositories. The mechanism designed here can support the propagation of
relevant information based on which to assess trustworthiness, but the creation
of such information is orthogonal.
Regarding TUF signatures specifically, it is important to note that such a
signature only ever supplies evidence supporting the trustworthiness of the
repository that hosts the artifacts. By itself it does not constitute a complete
basis to assess the trustworthiness of the artifacts obtained from it, because
that additionally depends on the control of the signing keys, the
trustworthiness of the build process that produces the artifacts, and the
trustworthiness of the transitive sources and tools the build process uses as
inputs. In the case of the TUF implementation currently used for Fuchsia
products, which signs any artifact uploaded to a service that maintains the
repository, it also depends on the integrity of the ACLs and the authentication
mechanism that let clients access that service.
### Multiple petal build processes publish to same artifact store
Publishing source repositories are recommended to not share an artifact store,
so they don't have to share trust relationship with the server, no matter how it
is established. One example is for TUF implementation, publishers don't have to
share signing keys.
There are also potential compromises introduced by multiple petal build
processes publishing to the same artifact store. One example is one publisher
might overwrite an artifact published by another. But this is out of the scope
of this RFC, as in this RFC all the petal build processes publishing to an
artifact store are operated by the same organization and thus can be coordinated
properly. If we want to expand this to multiple organizations, a different
coordination model needs to be designed.
## Performance
As the number of items in the `artifact_groups.json` file gets larger, the time
complexity of finding the group that matches all the required attributes
increases at least linearly. If there are constraints across artifacts, it might
be supra-linear because the update tool needs to join multiple
`artifact_groups.json` files to do the selection. Artifact publishers may need
to evict artifact groups that are too old to help improve performance of the
client side update tool.
Download times for artifacts once selected may be considerable for large
products. This is no different from the status quo. The design presented here
has the advantage over the Global Integration that only prebuilt artifacts for
one product at a time need to be downloaded by the `fetch` tool to any one
integration repository. In current Global Integration, the union of prebuilt
artifacts for all known products needs to be downloaded during a `jiri update`.
## Privacy considerations
The only new data being stored in the artifact store is the
`artifact_groups.json` file. It is up to the publisher of the artifact store to
not publish PII in the `artifact_groups.json` file.
## Testing
The libraries used by the tools, e.g. for matching attribute patterns in
`artifact_spec.json` files against `artifact_groups.json` files, will be tested
by unittests.
For the operation of the tools as executables there will be integration tests
operating from locally kept golden files.
For the end-to-end testing, there will be a "Hello World" product in its own
repository that continuously exercises the machinery and can be monitored for
failures.
## Drawbacks, Alternatives, and Unknowns {#alternatives}
### Using other types of artifact store
Aside from TUF, there are other artifact stores that can be used. Examples are:
* CIPD,
* static HTTPS Server,
* GCS bucket,
* Git repository.
The appropriate structures to locate the artifacts under names that are their
content addresses can be created inside all these artifact stores, and metadata
can be maintained in an `artifact_groups.json` file, in the same way as in the
TUF repository used in the implementation proposed above.
### TUF repository per release
Another alternative that was pursued in an earlier version of this design is a
TUF repository for each release of artifacts. Instead of creating a group for
all the artifacts of a release, the `upload` tool creates a new TUF repository
to host the artifacts. A well known lookup repository contains the
`artifact_groups.json` file recording the attributes. Instead of group names, it
would contain repository names for each artifact group.
The advantage of this method would have been the support for products that use
ephemeral packages that are delivered to the device only when they are first
used. Instead of or in addition to including packages in such release
repositories in the product assembly, the repository would have been included as
a package source in the product, used to resolve package URLs at runtime.
This use of the design was rejected because such products don't currently exist,
and the current mechanisms for software delivery both on device and in the TUF
infrastructure, although designed for such use, have never been proven to be
usable in production in this way, and are expected to evolve further before this
would be feasible. So there was a strong desire to create no lock-in to the
current state of software delivery. Likewise, the TUF infrastructure was not
believed to handle a large number of TUF repositories well.
### All artifacts listed in `targets.json` of the TUF repository
Originally it was envisoned that all artifacts (and especially all Fuchsia
packages) would be listed in the `targets.json` file of the TUF repository, and
the directories that group artifacts in one release were actual directories
containing TUF repository targets. That information was duplicative with the
information already recorded in `artifact_groups.json` and served no purpose, so
it was dropped for clarity, as well as to evade a size limit imposed on
`targets.json` that is not imposed on files merely listed in `targets.json`.
### Unknowns
There are currently unanswered questions about possible extensions of the
application of the presented design.
* Can this mechanism also be applied to the Fuchsia SDK? The Fuchsia SDK is a
prerequisite artifact for the creation of petal components as well as out of
tree products in the same way as prebuilt artifacts are prerequisite for
creation of products, and it needs to be selected very similarly according to
variant and version based on attributes that describe both. So the question
poses itself whether petal build processes should declare their use of the SDK
in an `artifact_spec.json` file, and whether the product build process should
include the Fuchsia SDK in its `artifact_spec.json` file.
* Can this mechanism also be applied to petal SDKs, i.e. the SDKs that one petal
provides to other petals, such as the Flutter SDK? This is not required to
enable decentralized product integration, but could simplify the resulting
distributed build process. For example, by checking the fetched Flutter SDK
version in the lock file of a petal repository, the upload tool can figure out
the correct Flutter SDK version attribute values of the uploaded artifacts.
* Can this mechanism also be used to publish built products? In the workstation
implementation, fuchsia workstation product could be released to an artifact
store. This uploaded product could later be used for differential assembly.
* Can the description mechanism be used to transitively establish binary
transparency for all binary artifacts involved in the creation of a product,
going back to the prebuilt artifacts included in the product, the prebuilt
artifacts of the tools used to assemble the product, the tools to create the
prebuilt artifacts, etc. recursively.
## Documentation
This document serves as the initial documentation. There will be tools, and they
will have help pages. The workstation repository setup will serve as blueprint
and demonstration for subsequent use.
## Prior art and references
Product integration for **other operating systems** usually happens from source
trees with less extensive use of prebuilts, and thus fewer requirements to
concurrently propagate multiple variants and versions of such prebuilts between
repositories.
**Build systems** "select" the compatible "version" of a dependency by just
rebuilding it at the current version, in the variant determined by the currently
applied build parameters. The design presented here can be understood as the
generalization of a build system to operate asynchronously and distributed.
### Related documents
* **[Decentralized Product Integration][roadmap]** Fuchsia Roadmap entry.
* **[Platform Versioning][rfc0002]** RFC
* **[Standalone Image Assembly Tool][rfc0072]** RFC
* **[Workstation Out Of Tree][rfc0095]** RFC
* **[Out-Of-Tree Bazel SDK][rfc-bazel]** RFC Draft
* **[Decentralized Product Integration, Artifacts Selection Specification][rfc-spec]** RFC Draft
* **[Product Assembly From Subassemblies][rfc-subassembly]** RFC Draft
## Appendix
**Figure 4 - Legend of Global Integration Figure**
![Legend of Global Integration Figure][fig4]
<!-- xrefs -->
[roadmap]: /docs/contribute/roadmap/2021/decentralized_product_integration.md
[rfc0002]: 0002_platform_versioning.md
[rfc0072]: 0072_standalone_image_assembly_tool.md
[rfc0095]: 0095_build_and_assemble_workstation_out_of_tree.md
[rfc-bazel]: https://fuchsia-review.googlesource.com/c/fuchsia/+/560368
[rfc-subassembly]: https://fuchsia-review.googlesource.com/c/fuchsia/+/553664
[rfc-spec]: https://fuchsia-review.googlesource.com/c/fuchsia/+/542243
[fig1]: resources/0124_decentralized_product_integration_artifact_description_and_propagation/fig_1.png
[fig2]: resources/0124_decentralized_product_integration_artifact_description_and_propagation/fig_2.png
[fig3]: resources/0124_decentralized_product_integration_artifact_description_and_propagation/fig_3.png
[fig4]: resources/0124_decentralized_product_integration_artifact_description_and_propagation/fig_4.png
[fuchsia-swd]: /docs/concepts/packages/software_update_system.md
[fuchsia-package]: /docs/concepts/packages/package.md
[fuchsia-package-hermetic]: /docs/concepts/software_model.md#distributing-components-through-packages
[fuchsia-merkleroot]: /docs/concepts/security/merkleroot.md
[cipd]: https://chromium.googlesource.com/infra/luci/luci-go/+/HEAD/cipd/README.md
[glossary.product]: /docs/glossary/README.md#product
[glossary.product-owner]: /docs/glossary/README.md#product-owner