Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -394,13 +394,25 @@ classes:
rank: 80
attributes:
repository:
description: Repository location for the component.
description: >-
OCI repository URI for the component (e.g., oci://registry.example.com/org/component-name).
MUST be used for Helm, Compose, and Quadlet components.
rank: 10
range: string
required: true
revision:
description: Revision version for the component.
rank: 20
description: >-
OCI tag identifying the component version within the repository
(e.g., "1.0.0", "2.3.1", "1.0.0-rc1", "1.0.0_build.123").
MUST be a valid SemVer 2.0 string without a leading `v`.
SemVer build metadata separator `+` MUST be stored as `_`
because `+` is not a valid OCI tag character.
Implementations comparing this value against a SemVer string
MUST convert `_` back to `+` before comparison.
range: string
required: true
rank: 20
pattern: "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:_([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$"
wait:
description: If True, indicates the device waits for the component installation to complete.
rank: 30
Expand All @@ -409,14 +421,6 @@ classes:
description: Time to wait for component installation to complete, formatted as "##m##s".
rank: 40
range: string
packageLocation:
description: The URL indicating the Compose package's location. It should be a direct path to the compose.yaml or compose.yaml file archived in tar.gz
rank: 50
range: string
keyLocation:
description: URL for the public key used to validate a digitally signed package.
rank: 60
range: string

Parameter:
description: >-
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ deploymentProfiles:
components:
- name: digitron-orchestrator-docker
properties:
packageLocation: https://northsitarida.com/digitron/docker/digitron-orchestrator.tar.gz
keyLocation: https://northsitarida.com/digitron/docker/public-key.asc
repository: oci://northstarida.azurecr.io/compose/digitron-orchestrator
revision: 1.0.9
parameters:
idpName:
targets:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ deploymentProfiles:
components:
- name: digitron-orchestrator-docker
properties:
packageLocation: https://northsitarida.com/digitron/docker/digitron-orchestrator.tar.gz
keyLocation: https://northsitarida.com/digitron/docker/public-key.asc
repository: oci://northstarida.azurecr.io/compose/digitron-orchestrator
revision: 1.0.9
parameters:
idpName:
value: "test"
Expand Down
10 changes: 4 additions & 6 deletions src/specification/applications/resources/index.md.jinja2
Original file line number Diff line number Diff line change
Expand Up @@ -111,14 +111,12 @@ The expected properties for the suppported deployment types are indicated below.

- Properties for `compose` components

> **Investigation Needed**: We need to have more discussion about how Compose should be handled and what is required here.

| Attribute | Type | Required? | Description |
| --- | --- | --- | --- |
| packageLocation | string | Y | The URL indicating the Compose package's location. It should be a direct path to the compose.yaml or compose file archived in tar.gz |
| keyLocation | string | N | The public key used to validated the digitally signed package. It is highly recommend to digitally sign the package. When signing the package PGP MUST be used.|
| wait | bool | N | If `True`, indicates the device MUST wait until the Compose file has finished starting up before starting the next Compose file. The default is `True`. The Workload Fleet Management Client MUST support `True` and MAY support `False`. Only applies if multiple `compose` components are provided.|
| timeout | string | N | The time to wait for the component's installation to complete. If the installation does not completed before the timeout occurs the installation process fails. The format is "##m##s" indicating the total number of minutes and seconds to wait.|
| repository | string | Y | OCI repository URI for the Compose Archive (e.g., `oci://registry.example.com/org/component-name`). MUST be used for Compose components. See [Compose Component Registry](application-registry.md#compose-component-registry) for details.|
| revision | string | Y | OCI tag identifying the component version. MUST be a valid SemVer 2.0 string without a leading `v`. Build metadata separator `+` MUST be stored as `_` because `+` is not a valid OCI tag character.|
| wait | bool | N | If `True`, indicates the device MUST wait until all containers in the Compose project reach running state before reporting success. The default is `True`. The Workload Fleet Management Client MUST support `True` and MAY support `False`. Only applies if multiple `compose` components are provided.|
| timeout | string | N | The time to wait for the component's installation to complete. If the installation does not complete before the timeout occurs the installation process fails. The format is "##m##s" indicating the total number of minutes and seconds to wait.|

## Defining configurable application parameters

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,13 +142,22 @@ classes:
Component:
description: A class representing a component of a deployment profile.
rank: 50
# abstract: true
attributes:
name:
description: The name of the component.
# identifier: true
required: true
rank: 10
revision:
description: >-
OCI tag identifying the component version within the repository
(e.g., "1.0.0", "2.3.1", "1.0.0-rc1", "1.0.0_build.123").
MUST be a valid SemVer 2.0 string without a leading `v`.
SemVer build metadata separator `+` MUST be stored as `_`
because `+` is not a valid OCI tag character.
range: string
required: false
rank: 15
pattern: "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:_([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$"
properties:
description: Properties associated with the component.
range: Property
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ spec:
components:
- name: digitron-orchestrator-docker
properties:
keyLocation: https://northsitarida.com/digitron/docker/public-key.asc
packageLocation: https://northsitarida.com/digitron/docker/digitron-orchestrator.tar.gz
repository: oci://northstarida.azurecr.io/compose/digitron-orchestrator
revision: 1.0.9
parameters:
adminName:
value: Some One
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ spec:
components:
- name: digitron-orchestrator-docker
properties:
keyLocation: https://northsitarida.com/digitron/docker/public-key.asc
packageLocation: https://northsitarida.com/digitron/docker/digitron-orchestrator.tar.gz
repository: oci://northstarida.azurecr.io/compose/digitron-orchestrator
revision: 1.0.9
parameters:
adminName:
value: Some One
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ spec:
components:
- name: digitron-orchestrator-docker
properties:
keyLocation: https://northsitarida.com/digitron/docker/public-key.asc
packageLocation: https://northsitarida.com/digitron/docker/digitron-orchestrator.tar.gz
repository: oci://northstarida.azurecr.io/compose/digitron-orchestrator
revision: 1.0.9
parameters:
adminName:
value: Some One
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,8 @@ The following response example is a Margo-specific OCI image manifest following
|``application/vnd.margo.app.descriptionFile.v1+{file format}``| MUST be used to mark a layer in the OCI image manifest as pointing to description file of a Margo Application Package |
|``application/vnd.margo.app.licenseFile.v1+{file format}``| MUST be used to mark a layer in the OCI image manifest as pointing to the license file of a Margo Application Package|
|``application/vnd.margo.app.releaseNotes.v1+{file format}``| MUST be used to mark a layer in the OCI image manifest as pointing to the release notes file of a Margo Application Package|
|``application/vnd.org.margo.component.compose+json``| MUST be used as the **artifactType** in the OCI image manifest for a Margo Compose Archive |
|``application/vnd.org.margo.component.compose.tar+gzip``| MUST be used as the layer blob **mediaType** for a Margo Compose Archive |


#### Margo-Specific Annotation Keys
Expand All @@ -243,4 +245,79 @@ The following response example is a Margo-specific OCI image manifest following

Retrieving the different files that compose an Application Package MUST be implemented according to the ["Pulling blobs" section of the OCI_spec](https://github.com/opencontainers/distribution-spec/blob/main/spec.md#pulling-blobs).

Also for this purpose available tools and libraries can be used for an implementation with low complexity.
Also for this purpose available tools and libraries can be used for an implementation with low complexity.

## Compose Component Registry

Compose components MUST be stored in an OCI-compliant Component Registry and referenced via `repository` (an `oci://` URI) and `revision` (an OCI tag matching SemVer 2.0) in the ApplicationDescription and Desired State manifests.

The OCI image manifest for a Compose component MUST use `artifactType` = `application/vnd.org.margo.component.compose+json`. The single layer blob MUST use `mediaType` = `application/vnd.org.margo.component.compose.tar+gzip`.

### Compose Archive Structure

A Compose Archive is a gzip-compressed tar archive (`.tar.gz` or `.tgz`) that packages a Compose application for deployment on edge devices. The archive MUST conform to the following structural requirements.

#### Directory Layout

The archive MUST contain exactly one top-level directory.

The directory name SHOULD match the component `name` as specified in the ApplicationDescription for human readability, but implementations MUST NOT depend on the directory name for discovery.

Discovery algorithm: enter the single top-level directory; locate the file named `compose.yaml`.

The top-level directory MAY contain any number of subdirectories (e.g., `configs/`, `certs/`, `scripts/`). All referenced files MUST resolve within the top-level directory.

The top-level directory MUST contain a file named `compose.yaml`. The Compose file MUST conform to the Compose Specification as currently published.

> **Note:** The Compose file MUST be named `compose.yaml`. The alternative names `compose.yml`, `docker-compose.yaml`, and `docker-compose.yml` are NOT valid within a Margo Compose Archive.

Files referenced by `compose.yaml` via `env_file` entries and `configs` (file source) MUST be included within the archive and MUST be referenced using relative paths that resolve within the top-level directory.

Bind-mount volume paths declared in `volumes` are runtime paths and MUST NOT be included in the archive.

Files for `secrets` (file source) MUST NOT be included in the archive. Secret provisioning is out of scope and is the responsibility of the device or WFM implementation at deployment time.

#### Security Constraints

- Symlinks MUST NOT target paths outside the top-level directory.
- Hard links MUST NOT reference paths outside the top-level directory.
- Absolute paths MUST NOT appear in the archive entries.
- File names MUST NOT contain path traversal sequences (`../`).
- Implementations SHOULD normalize file permissions during archive extraction. Implementations MUST NOT preserve setuid, setgid, or sticky bits from archive entries.
- WFM and device implementations MUST validate these constraints before extracting or deploying the archive.

#### Integrity Verification

When stored in an OCI-compliant Component Registry, the Compose Archive tarball is the content of a single layer blob. Integrity verification at the transport layer is provided by the OCI content-addressable digest as mandated by the [OCI Distribution Specification v1.1.0](https://github.com/opencontainers/distribution-spec/blob/v1.1.0/spec.md). Implementations MUST verify the OCI digest after pulling the blob and before extracting the archive.

### Publishing Workflow

To publish a Compose Archive to an OCI-compliant Component Registry, use `oras push` ([ORAS — OCI Registry as Storage](https://oras.land/)). This is the RECOMMENDED publishing tool for Margo Compose Archives.

> **Warning**: `docker compose publish` (Docker Compose 2.34.0+) MUST NOT be used to publish Margo Compose components. It produces a structurally incompatible OCI artifact: `artifactType: application/vnd.docker.compose.project`, multiple layers (one per file), and SHA256-hashed file paths. This format cannot be consumed by a Margo-compliant WFM or device implementation.

Example:

```bash
oras push registry.example.com/org/myapp:1.0.0 \
--artifact-type application/vnd.org.margo.component.compose+json \
myapp-1.0.0-compose.tar.gz:application/vnd.org.margo.component.compose.tar+gzip
```

Reference the artifact in the ApplicationDescription:

```yaml
components:
- name: myapp
properties:
repository: oci://registry.example.com/org/myapp
revision: "1.0.0"
```

### WFM Reconciliation and `wait` Semantics for Compose

If `wait` is set to `true` for a Compose component, the device MUST wait until all containers in the Compose project reach **running** state before reporting the deployment as successful. This is equivalent to `docker compose up` or `podman-compose up` completing synchronously without `--detach`.

If any container exits with a non-zero exit code during startup, the deployment MUST be reported as failed immediately.

If health checks are defined in `compose.yaml`, implementations SHOULD additionally wait for all containers to reach **healthy** state before reporting success.
Loading