<a id="ref-sdk-internals"></a>

# SDK internals

<!-- @artefact SDK -->

<a id="ref-sdk-directory"></a>

## Source directory

All files that go into an SDK should be placed in a *source directory*
where you’ll run **SDKcraft**
to initialize, define, pack and publish the SDK.

<a id="ref-sdk-platform"></a>

## SDK platform

<!-- @artefact SDK platforms -->

A platform describes where an SDK can be built and installed.

The components describing a platform are:

- The base image: used to build SDKs and initialize workshops.
- The CPU architecture: `amd64`, `arm64`, `armhf`, `i386`,
  `ppc64el`, `riscv64`, or `s390x`.

The easiest way to define a platform
is to name it after the CPU architecture:

```yaml
# ...
base: ubuntu@24.04
platforms:
  amd64:
  arm64:
```

The above SDK can be built on `amd64` or `arm64` machines
and installed in `ubuntu@24.04` workshops with the same architecture.

The `base` can also be moved into the platform names:

```yaml
# ...
platforms:
  ubuntu@24.04:amd64:
  ubuntu@24.04:arm64:
```

More complex scenarios can be described using the following attributes:

| Key         | Value   | Description                                                                                                                                                                                                                                                                                                                                                                             |
|-------------|---------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `build-on`  | array   | List of supported CPU architectures<br/>that can build the SDK for the platform.<br/><br/>If the SDK has no `base` or `build-base`,<br/>each entry must be prefixed by a valid base and a colon,<br/>e.g., `ubuntu@22.04:amd64`.<br/>This has no effect on the supported build machines,<br/>because **SDKcraft** performs builds in containers.<br/>The prefix must match `build-for`. |
| `build-for` | string  | CPU architecture the SDK is expected to run on,<br/>or `all` if the SDK can run on all supported architectures.<br/>SDK authors are responsible for ensuring compatibility.<br/><br/>If the SDK has no `base` or `build-base`,<br/>each entry must be prefixed by a valid base and a colon,<br/>e.g., `ubuntu@24.04:riscv64`.<br/>The prefix must match every entry in `build-on`.      |

Architecture-independent SDKs require the complex format:

```yaml
# ...
platforms:
  all:
    build-on: [amd64, arm64, riscv64]
    build-for: all
```

<a id="ref-sdk-parts"></a>

## SDK parts

<!-- @artefact sdkcraft (CLI) -->
<!-- @artefact SDK part -->

Parts can be thought of as the building blocks of **Workshop** and **SDKcraft**.
Each part in the `sdkcraft.yaml` [definition](https://ubuntu.com/workshop/docs//reference/definition-files/sdk-definition.md#ref-sdk-definition)
describes a specific component or piece of the SDK being packaged,
providing a way to modularize the package and manage its dependencies.

**SDKcraft** is built as a
[craft-application](https://github.com/canonical/craft-application/),
which affects how `parts` are implemented.
However, note that `stage-packages` and `stage-snaps`
aren’t enabled yet;
instead, rely on the [hooks](#ref-sdk-hooks)
to implement custom logic of package and snap installation.

For a complete reference of `parts` and their properties,
refer to the corresponding Craft Parts
[documentation section](https://documentation.ubuntu.com/craft-parts/latest/common/craft-parts/reference/part_properties/).

<a id="ref-sdk-plugs-slots"></a>

## SDK plugs and slots

<!-- @artefact interface plug -->
<!-- @artefact interface slot -->

Currently, **Workshop** and **SDKcraft** support the following interface plugs:

- [Camera](#ref-camera-interface)
- [Custom device](#ref-custom-device-interface)
- [Desktop](#ref-desktop-interface)
- [GPU](#ref-gpu-interface)
- [Mount](#ref-mount-interface)
- [SSH](#ref-ssh-interface)
- [Tunnel](#ref-tunnel-interface)

Slots can only be defined for the `mount` interface.

<a id="ref-camera-interface"></a>

### Camera interface

<!-- @artefact camera interface -->

A camera plug in the definition must specify the plug name and the interface:

```yaml
 # ...
 plugs:
   <NAME>:
     interface: camera
```

This makes the host’s cameras directly available inside the workshop
as video capture devices.

#### NOTE
See the [explanation](https://ubuntu.com/workshop/docs//explanation/interfaces/camera-interface.md#exp-camera-interface) for more details.

<a id="ref-custom-device-interface"></a>

### Custom device interface

<!-- @artefact custom-device interface -->

A custom-device plug in the definition
must specify the plug name, the interface,
and a `subsystem` attribute:

```yaml
 # ...
 plugs:
   <NAME>:
     interface: custom-device
     subsystem: <SUBSYSTEM>
```

This makes host devices from the given subsystem directly available inside the workshop.

#### NOTE
See the [explanation](https://ubuntu.com/workshop/docs//explanation/interfaces/custom-device-interface.md#exp-custom-device-interface) for more details.

<a id="ref-desktop-interface"></a>

### Desktop interface

<!-- @artefact desktop interface -->

A desktop plug in the definition must specify the plug name and the interface:

```yaml
 # ...
 plugs:
   <NAME>:
     interface: desktop
```

This makes the host’s Wayland socket directly available inside the workshop.

#### NOTE
See the [explanation](https://ubuntu.com/workshop/docs//explanation/interfaces/desktop-interface.md#exp-desktop-interface) for more details.

<a id="ref-gpu-interface"></a>

### GPU interface

<!-- @artefact GPU interface -->

A GPU plug in the definition must specify the plug name and the interface:

```yaml
 # ...
 plugs:
   gpu:
     interface: gpu
```

This makes the host’s GPUs directly available inside the workshop
via the GPU pass-through mechanism.

#### NOTE
See the [explanation](https://ubuntu.com/workshop/docs//explanation/interfaces/gpu-interface.md#exp-gpu-interface) for more details.

<a id="ref-mount-interface"></a>

### Mount interface

<!-- @artefact mount interface -->

A mount plug in the definition must specify the plug name, the interface, and the target directory.
The plug can specify permissions and ownership for the target, and whether it is read-only:

```yaml
 # ...
 plugs:
   <NAME>:
     interface: mount
     workshop-target: <WORKSHOP DIRECTORY>
     mode: <OCTAL FILE MODE> # optional
     uid: <USER ID> # optional
     gid: <GROUP ID> # optional
     read-only: <true | false> # optional
```

<!-- @artefact $SDK -->

This mounts a directory automatically created by **Workshop** on the host
to the `workshop-target` directory.
The `$SDK` variable can be used to refer to the SDK installation path
inside the workshop.
The host directory will be created under the path
designated by the `$XDG_DATA_HOME` variable.
The workshop directory will be created using the given `mode`, `uid`, and `gid`.

A mount *slot* in the definition must specify the slot name, the interface,
and the *source* directory:

```yaml
 # ...
 slots:
   <NAME>:
     interface: mount
     workshop-source: <WORKSHOP DIRECTORY>
```

<!-- @artefact $SDK -->

This exposes the `workshop-source` directory inside the workshop
to be mounted to another directory within the workshop.
The `$SDK` variable can be used to refer to the SDK installation path
inside the workshop.

#### NOTE
See the [explanation](https://ubuntu.com/workshop/docs//explanation/interfaces/mount-interface.md#exp-mount-interface) for more details.

<a id="ref-ssh-interface"></a>

### SSH interface

<!-- @artefact SSH interface -->

An SSH plug in the definition must specify the plug name and the interface:

```yaml
 # ...
 plugs:
   ssh-agent:
     interface: ssh-agent
```

This proxies the host’s SSH keys and configuration inside the workshop
via a Unix domain socket.

#### NOTE
See the [explanation](https://ubuntu.com/workshop/docs//explanation/interfaces/ssh-interface.md#exp-ssh-interface) for more details.

<a id="ref-tunnel-interface"></a>

### Tunnel interface

<!-- @artefact tunnel interface -->

A tunnel plug in the definition must specify the plug name, the interface and optionally an endpoint:

```yaml
# ...
plugs:
  <NAME>:
    interface: tunnel
    endpoint: <ENDPOINT>
```

Similarly, a tunnel *slot* in the definition must specify the slot name, the interface and optionally an endpoint:

```yaml
# ...
slots:
  <NAME>:
    interface: tunnel
    endpoint: <ENDPOINT>
```

When a tunnel interface plug is connected to a slot,
clients can connect to the address of the plug.
The connection will be forwarded to the address of the slot.
Regular SDKs define the workshop side of the connection,
leaving the host system to the `system` SDK.

The supported protocols are TCP, UDP and Unix domain sockets.
Unix domain sockets are compatible with TCP, but UDP plugs can only connect to UDP slots.

TCP and UDP endpoints look like `<IPv4>:<PORT>/<PROTOCOL>` or `'[<IPv6>]:<PORT>/<PROTOCOL>'`.
**Workshop** doesn’t resolve hostnames,
but supports the aliases `localhost`, `ip6-localhost` and `ip6-loopback`.

Unix domain socket endpoints are either paths to a socket file or abstract sockets of the form `'@<STRING>'`.
The `$HOME` and `$XDG_RUNTIME_DIR` variables can be used in paths.

Attributes can be abbreviated by omitting `tcp` and `localhost`:

| Address              | Alternatives                                                  |
|----------------------|---------------------------------------------------------------|
| `127.0.0.1:1234/tcp` | `localhost:1234/tcp`, `1234/tcp`, `127.0.0.1:1234`, `1234`    |
| `0.0.0.0:1234/tcp`   | `0.0.0.0:1234`                                                |
| `'[::1]:1234/tcp'`   | `ip6-localhost:1234/tcp`, `ip6-loopback:1234`, `'[::1]:1234'` |
| `127.0.0.1:1234/udp` | `localhost:1234/udp`, `1234/udp`                              |
| `'[::]:1234/udp'`    |                                                               |
| `/run/service.sock`  |                                                               |
| `'@abstract'`        |                                                               |

Port numbers may also be omitted,
but only on one side of a connection.
For such connections,
both sides use the same port.

#### NOTE
See the [explanation](https://ubuntu.com/workshop/docs//explanation/interfaces/tunnel-interface.md#exp-tunnel-interface) for more details.

<a id="ref-sdk-hooks"></a>

## SDK hooks

**Workshop** supports the following lifecycle hooks,
which can be defined when the SDK is built using **SDKcraft**:

<!-- @artefact workshopctl -->
<!-- @artefact check-health -->
<!-- @artefact workshop status -->
<!-- @artefact restore-state -->
<!-- @artefact save-state -->
<!-- @artefact SDK base image -->
<!-- @artefact setup-base -->
<!-- @artefact workshop base image -->
<!-- @artefact setup-project -->

| Name            | When **Workshop** runs it                                                                                                                                                                                                                                                                                                                                                                                                                          | What it does                                                                                                                                                                                                                                                                                                  |
|-----------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `setup-base`    | At [workshop launch](https://ubuntu.com/workshop/docs//reference/cli/workshop.md#ref-workshop-launch), [workshop refresh](https://ubuntu.com/workshop/docs//reference/cli/workshop.md#ref-workshop-refresh):<br/>after unpacking the base image<br/>and starting the workshop,<br/>but before mounting the project directory<br/>and connecting plugs and slots.                                                                                   | Configures system packages and services required by the SDK.                                                                                                                                                                                                                                                  |
| `setup-project` | At [workshop launch](https://ubuntu.com/workshop/docs//reference/cli/workshop.md#ref-workshop-launch), [workshop refresh](https://ubuntu.com/workshop/docs//reference/cli/workshop.md#ref-workshop-refresh), [workshop restore](https://ubuntu.com/workshop/docs//reference/cli/workshop.md#ref-workshop-restore):<br/>after mounting the project directory<br/>and auto-connecting plugs and slots<br/>but before the workshop is set to *Ready*. | Configures the user environment for the SDK to become operational.                                                                                                                                                                                                                                            |
| `save-state`    | At [workshop refresh](https://ubuntu.com/workshop/docs//reference/cli/workshop.md#ref-workshop-refresh), [workshop restore](https://ubuntu.com/workshop/docs//reference/cli/workshop.md#ref-workshop-restore):<br/>before destroying the old workshop.                                                                                                                                                                                             | Saves SDK-specific data to the [state directory](#ref-sdk-state).<br/>The hook itself comes from the *old* SDK revision.                                                                                                                                                                                      |
| `restore-state` | At [workshop refresh](https://ubuntu.com/workshop/docs//reference/cli/workshop.md#ref-workshop-refresh), [workshop restore](https://ubuntu.com/workshop/docs//reference/cli/workshop.md#ref-workshop-restore):<br/>after running `setup-project` hooks for *all* SDKs.                                                                                                                                                                             | Restores SDK-specific data from the [state directory](#ref-sdk-state).<br/>The hook itself comes from the *new* SDK revision.                                                                                                                                                                                 |
| `check-health`  | At [workshop launch](https://ubuntu.com/workshop/docs//reference/cli/workshop.md#ref-workshop-launch):<br/>after running `setup-project` hooks for *all* SDKs.<br/><br/>At [workshop refresh](https://ubuntu.com/workshop/docs//reference/cli/workshop.md#ref-workshop-refresh), [workshop restore](https://ubuntu.com/workshop/docs//reference/cli/workshop.md#ref-workshop-restore):<br/>after running `restore-state` hooks for *all* SDKs.     | Sets the state of the SDK<br/>(`okay`, `waiting` or `error`)<br/>using [workshopctl](https://ubuntu.com/workshop/docs//reference/cli/workshopctl.md#ref-workshopctl-cli),<br/>which affects the [status](https://ubuntu.com/workshop/docs//reference/workshop-status.md#ref-workshop-status) of the workshop. |

Each hook is defined as a **bash** script of the same name
under `hooks/` in the [source directory](#ref-sdk-directory).
Inside the workshop,
the SDK is mounted at `/var/lib/workshop/sdk/<SDK>/`
and hooks are stored in the `sdk/hooks/` subdirectory.
Most hooks run as `root`
and use that subdirectory as the working directory.
The exception is `setup-project`,
which runs as the `workshop` user
in the `/project/` directory.

A hook can signal an error by returning a nonzero exit code;
a zero code indicates success.
The options `errexit` and `pipefail`
are set by default,
so most commands which return a nonzero exit code
cause the hook to exit with the same code.
If `--verbose` is passed to **workshop launch** or **workshop refresh**,
the option `xtrace` is also set.

#### NOTE
The hooks aren’t mentioned in the [SDK definition](https://ubuntu.com/workshop/docs//reference/definition-files/sdk-definition.md#ref-sdk-definition);
**SDKcraft** automatically enumerates them when packing the SDK.

An SDK’s position in the [workshop definition](https://ubuntu.com/workshop/docs//reference/definition-files/workshop-definition.md#ref-workshop-definition)
determines when its hooks execute.
SDKs are always processed in the following order:
`system`, user-listed SDKs, `sketch`.
Each hook waits for the previous one to complete before executing.

<a id="ref-sdk-state"></a>

## SDK state

<!-- @artefact SDK state -->

An SDK can store any data specific to it within the workshop.
For this purpose, an environment variable named `$SDK_STATE_DIR`
is exposed by **Workshop** at runtime;
it resolves to an internal directory in the workshop,
which `save-state` and `restore-state`
can use to preserve and recover the data respectively.

#### NOTE
The `$SDK_STATE_DIR` variable is only available
to the `save-state` and `restore-state` SDK hooks.
It is not accessible to the `workshop` user, the SDK itself,
or in the workshop definition.

The state directory is a dedicated volume created by **Workshop** at runtime
for each SDK in every workshop,
and is removed when the workshop stops.
The `*-state` hooks can use it
to store or retrieve any arbitrary data required by the SDK.

<a id="ref-sdk-channels"></a>

## SDK channels

<!-- @artefact SDK channel -->

When SDKs are published by their creators and consumed by workshops,
different versions and releases are tracked through the use of channels.
A channel is a combination of a track, a risk, and an optional branch,
e.g., `latest/beta`.

Tracks allow multiple published versions of an SDK to exist in parallel;
while no specific scheme is enforced,
it is desirable to use a semantic version, e.g., `1.2.3`,
or the `latest` keyword,
which maps to the latest published version and serves as the default.

Risks represent a choice of maturity levels for a particular track:

- `stable`: indicates that the software can be used in production
- `candidate`: for software that’s being tested prior to stable deployment
- `beta`: for software that can be used outside of production
- `edge`: for unstable software that’s still in active development;
  nothing is guaranteed

Branches are short-lived subdivisions of a channel
intended for experimentation, e.g. 1.2.3/edge/issue-56789.
After 30 days of no activity, a branch will be closed automatically.

#### ATTENTION
SDK channels should not be confused with SDK revisions.

## See also

Explanation:

- [Base image](https://ubuntu.com/workshop/docs//explanation/workshops/concepts.md#exp-base)
- [Interface concepts](https://ubuntu.com/workshop/docs//explanation/interfaces/concepts.md#exp-interface-concepts)
- [SDKs](https://ubuntu.com/workshop/docs//explanation/index.md#exp-sdks)
- [SDK state](https://ubuntu.com/workshop/docs//explanation/sdks/concepts.md#exp-sdk-state)
- [Workshop definition](https://ubuntu.com/workshop/docs//explanation/workshops/concepts.md#exp-workshop-definition)
