Skip to main content

App developer guide

App bundle structure

Each app must be bundled as a zip archive (commonly used with the suffix .wave or .zip) consisting of:

  • app.toml - required; the platform configuration file
  • static/ - static asset directory, including the app icon (a png file starting with icon) and screenshots (files starting with screenshot)
  • requirements.txt - pip-managed dependencies of the app (can contain references to .whl files included in the .wave using paths relative to the archive root)
  • packages.txt - OS-managed dependencies of the app
  • .appignore - specifies which files to ignore while bundling your app (the format is similar to .gitignore but with few exceptions)
  • app source code

You can quickly create a .wave archive by running h2o bundle in your app git repository (see the CLI section).

info

H2O AI Cloud supports the following runtimes:

  • Python 3.8 | CPU | deb11_py38_wlatest
  • Python 3.9 | CPU | deb11_py39_wlatest
  • Python 3.10 | CPU | deb11_py310_wlatest
  • Python 3.8 | GPU | ub2004_cuda114_cudnn8_py38_wlatest
  • Python 3.10 | GPU | ub2204_cuda121_cudnn8_py310_wlatest

app.toml

Each app archive has to contain an app.toml configuration file in the TOML format, placed in the root of the .wave archive, example:

[App]
Name = "ai.h2o.wave.my-app"
Version = "0.0.1"
Title = "My awesome app"
Description = "This is my awesome app"
LongDescription = "LongDescription.md"
Tags = ["DATA_SCIENCE"]
InstanceLifecycle = "ON_DEMAND"

[Runtime]
Module = "app.run"
VolumeMount = "/data"
VolumeSize = "1Gi"
ResourceVolumeSize = "2Gi"
MemoryLimit = "500Mi"
MemoryReservation = "400Mi"
CPULimit = "1.5"
CPUReservation = "500m"
GPUCount = 1
GPUType = ""
EnableOIDC = false
EnableSHM = false
RoutingMode = "DOMAIN"

[[Env]]
Name = "ENVIRONMENT_VARIABLE_NAME"
Secret = "SecretName"
SecretKey = "SecretKeyName"

[[Env]]
Name = "ANOTHER_ENVIRONMENT_VARIABLE_NAME"
Value = "some value"

[[File]]
Path = "some/path.file"
Secret = "FileSecretName"
SecretKey = "FileSecretKeyName"

[[File]]
Path = "/another/path.file"
Value = '''
some
string
'''

Required attributes

[App]

AttributeTypeDescription
NamestringA machine-oriented unique identifier of the app (links different app versions together)
VersionstringA semver version of the app
note

{{App.Name}} and {{App.Version}} must be 63 chars or less and match the regex ^([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]$

[Runtime]

AttributeTypeDescription
ModulestringThe name of the main Python module of the app, that is, the app should be started via python3 -m $module_name (this is only required if the value of the App.InstanceLifecycle attribute is not LINK; see Link apps for more information)
note

{{App.Name}}-{{App.Version}} must be 63 chars or less and match the regex ^([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]$

Optional attributes

[App]

AttributeTypeDescription
TitlestringA human-oriented name of the app for presentation in UI/CLI
DescriptionstringA single-line description of the app for presentation in UI/CLI
LongDescriptionstringA path to a file, relative to the archive root, containing additional multi-line markdown description of the app. Although there is no actual restriction of the Markdown format, it is recommended to limit it to bullet-point lists (*), H3 headings (###), and hyperlinks ([]()).
Tagslist of stringsTags to automatically assign to the app upon importing. Apps can be identified by tag name. If the tag is listed as a category tag in the server configuration, the app will be assigned that category upon import.
InstanceLifecyclestringIdentifies the instance lifecycle, which can be set to [ON_DEMAND] or [MANAGED]. This config defaults to ON_DEMAND when empty.

[Runtime]

AttributeTypeDescription
RuntimeVersionstringThe name of the runtime version that the app will run on top of (similar to a docker base image, see Runtime environment). This config defaults to the platform's default when left empty/unspecified. Valid values differ based on the platform deployment and configuration.
VolumeMountstringThe absolute path of the volume used to persist app instance data across restarts
VolumeSizestringThe volume size. This config value must conform to the k8s resource model
ResourceVolumeSizestringThe volume used to persist internal app resources (such as Python venv) across restarts. This is only recommended for production-quality apps with sizeable resources, due to cluster node limits. This config value must conform to the k8s resource model.
MemoryLimitstringA hard limit on the maximum amount of memory an instance can use before it is OOM-killed. This config defaults to service-wide settings managed by Admins (it is recommended to be conservative with these limits) and must conform to the k8s resource model.
MemoryReservationstringThe amount of memory required to schedule an instance of the app. This config defaults to service-wide settings managed by Admins (it is recommended to be conservative with these limits) and must conform to the k8s resource model.
CPULimitstringMaximum CPU usage that an instance of the app can use. This config defaults to service-wide settings managed by Admins (it is recommended to be conservative with these limits) and must conform to the k8s resource model.
CPUReservationstringThe number of CPU units required to schedule an instance of the app. This config defaults to service-wide settings managed by Admins (it is recommended to be conservative with these limits) and must conform to the k8s resource model.
GPUCountintThe number of GPU units required to schedule an instance of the app
GPUTypestringGPU type required for the app. This config defaults to the platform's default when left empty/unspecified. Valid values differ based on the platform deployment and configuration.
EnableOIDCboolEnable Wave to be set up with OIDC authorization, thereby giving access to the user's authentication and authorization information from your app (see Wave security for details).
EnableSHMboolEnable extended docker shared memory for the app; some Libraries like pyTorch use Shared Memory for Multiprocessing (see this Kubernetes issue for more details on this topic.
RoutingModestringThe routing mode to be used for instances of the app can be set to either DOMAIN or, BASE_URL. This config defaults to DOMAIN when empty (see App routing mode for details).
AppModestringApp mode can be set to python or container. This config defaults to wave when empty.
PortintPort number of the app only valid when AppMode is python or container.
CustomImagestringIf AppMode is container set the a custom container for this App and must be imported using h2o admin app import
AppArgs[]stringIf AppMode is container set arguments to start the container.
AppEntryPoint[]stringIf AppMode is container set the entrypoint for the container.

[Env]

This struct contains configs that request for a literal value/secret to be injected into an instance at startup-time as an Env variable (see Utilizing secrets for more details).

AttributeTypeDescription
NamestringThe name of the Env variable to the injected into the Python process. Names prefixed with H2O_CLOUD or prefixed with H2O_WAVE are disallowed (except H2O_WAVE_APP_MODE and names allowed by the administrator). See Configuring your app for a full list of environment variables that you can configure.
SecretstringThe name of the shared secret to use. Each secret can contain multiple key-value pairs but cannot be used together with the Value config.
OptionalboolIf set to true the secret will not be required to exist to be imported. This config cannot be used together with Value config.
SecretKeystringThe name of the key within the secret that is to be used. This config cannot be used together with the Value config.
ValuestringThe literal value of the Env variable. This config cannot be used together with the Secret or SecretKey configs.

[File]

This struct contains configs that request for a literal value/secret to be injected into an instance at startup-time as a file (see Utilizing secrets for more details).

AttributeTypeDescription
PathstringThe path to inject the file into. A relative path is relative to the directory with the app code as determined by the platform. The path dir cannot be / or . (only subdirs are allowed) and it must be unique across all other File configurations. Note that the /resources path dir is disallowed.
SecretstringThe name of the shared secret to use. Each secret can contain multiple key-value pairs but cannot be used together with the Value config.
OptionalboolIf set to true the secret will not be required to exist to be imported. This config cannot be used together with Value config.
SecretKeystringThe name of the key within the secret that is to be used. This config cannot be used together with the Value config.
ValuestringThe literal value of the Env variable. This config cannot be used together with the Secret or SecretKey configs.

[Link]

This struct is to be filled only if the value of App.InstanceLifecycle is LINK (see Link apps for details).

AttributeTypeDescription
AddressstringFull URL of the app link.

Runtime environment

The platform executes each app in an environment given by its RuntimeVersion. The RuntimeVersion determines the OS, Python version, location of the app code/venv, etc.

Developers can also specify the pip-managed dependencies of the app via standard requirements.txt (can contain references to .whl files included in the .wave using paths relative to the archive root)

Developers can also specify the OS-managed dependencies of the app via packages.txt in a format similar to requirements.txt: one package name per line. These will be installed as given using the package manager given by the RuntimeVersion (e.g., apt-get install).

Developers can further customize the runtime environment by Utilizing secrets.

The h2o env runtime-version list command will list the runtime-versions available for use.

$ h2o env runtime-version list
NAME STATUS
deb11_py310_wlatest Default
ub2004_cuda114_cudnn8_py38_wlatest
...
note

The platform does not currently provide any provisions for developers to customize the OS and Python versions beyond choosing a specific RuntimeVersion.

We are actively working on improving this.

Standard environment variables

When running in an actual App Store deployment, each app instance will be configured with several standard environment variables specifying its context (these might be empty when running locally, e.g., via h2o exec), including:

  • H2O_CLOUD_ENVIRONMENT - typically URL of the App Store deployment
  • H2O_CLOUD_INSTANCE_ID - ID of the app instance
  • H2O_CLOUD_APP_NAME - name of the app
  • H2O_CLOUD_APP_VERSION - version of the app
  • H2O_CLOUD_APP_ID - ID of the app

How-To

Update an app to a newer version

The "Catalog" page shows apps with visibility ALL_USERS, so rolling out a new app version is done by:

  1. importing a new version of the app as PRIVATE
  2. testing the new version
  3. changing the visibility of the new version to ALL USERS
  4. (optional) changing the visibility of the old version to PRIVATE

This is based on the Basic concepts:

Apps are mostly immutable, meaning once uploaded, they cannot be changed (except for visibility). To "update" an app, one has to upload a new version.

and:

Internally, AI App Store treats every app name/version combination as a separate entity. The UI then uses the app name to link several versions together; however each can have different title, description, owner, instances, etc.

An important corollary is that instances of the old app version are not affected by the update (as they are completely separate from the new app version). The update only prevents users from starting new instances of the old version.

Pause or restart an app instance

The h2o instance suspend <instanceId> command pauses a running instance of a particular app. The app status changes to "Paused". You can configure ResourceVolumeSize in the app.toml file to utilize Wave checkpointing.

$ h2o instance suspend 2efe9ed7-2bdd-4d02-9be6-f73a196d663d
ID 2efe9ed7-2bdd-4d02-9be6-f73a196d663d
App
App ID 492dbac1-3230-413e-852f-11cb82b57436
Created At 2022-09-16 08:28:04
Updated At 2022-12-12 09:03:23
Status SUSPENDED
Visibility ALL_USERS
URL https://2efe9ed7-2bdd-4d02-9be6-f73a196d663d.cloud.h2o.ai
Owner oshini.nugapitiya@h2o.ai
Readonly false
Suspendable true
Suspend After

The h2o instance resume <instanceId> command restarts a paused or suspended instance of a particular app. The app status changes to "Deployed". Any files that are saved to disk are not available after the restart unless they are in the directory in VolumeMount.

note

VolumeMount cannot be an existing folder in the App Bundle.

::note There is no guarantee that the Wave on_shutdown hook will be given time to complete when an app is suspended, the underlying Kubernetes pod moves nodes, or a node experiences a failure. :::

$ h2o instance resume 2efe9ed7-2bdd-4d02-9be6-f73a196d663d
ID 2efe9ed7-2bdd-4d02-9be6-f73a196d663d
App
App ID 492dbac1-3230-413e-852f-11cb82b57436
Created At 2022-09-16 08:28:04
Updated At 2022-12-12 08:56:32
Status DEPLOYED
Visibility ALL_USERS
URL https://2efe9ed7-2bdd-4d02-9be6-f73a196d663d.cloud.h2o.ai
Owner oshini.nugapitiya@h2o.ai
Readonly false
Suspendable true
Suspend After 2022-12-12 16:56:31

Utilize app secrets

Developers can pass secrets registered with the platform to apps, exposed as environment variables using the [[Env]] section within the app.toml or as files using the [[File]] section. Each specific value from the secret that you want to use in your app needs its own environmental variable or file path.

[[Env]]
Name = "USER_ENV_VAR"
Secret = "private-secret"
SecretKey = "user"

[[File]]
Path = "some/password.file"
Secret = "private-secret"
SecretKey = "password"
import os


@app('/')
async def serve(q: Q):
environment_variable = 'USER_ENV_VAR'
default_value = 'user'
value = os.getenv(environment_variable, default_value)
q.page['my_card'] = ui.form_card(box='1 1 4 4', items=[ui.text(value)])
await q.page.save()

This lets developers parametrize their apps with links to external dependencies (e.g., S3, Driverless AI) securely, while allowing easy overrides for local development or deployments outside the platform.

See CLI documentation for instructions on manipulating secrets.

note

Apps imported into the App Store directly can reference only PRIVATE secrets of the same user or ALL_USERS secrets.

APP secrets are reserved for apps imported via the H2O marketplace.

App routing mode

The app routing mode (Runtime.RoutingMode in app.toml) determines how the app instances' UI is exposed to end users. The currently supported values are

  • DOMAIN - each app instance is exposed on its own subdomain of the Appstore server, i.e., uuid.appstore.domain/app/path.... This is the default setting.
  • BASE_URL - each app instance is exposed on a sub-path of the appstore server, i.e., appstore.domain/instance/uuid/app/path.... This setting requires that the app itself supports serving behind a base URL/path. All apps using Wave SDK 0.20 and later should support this out of the box. The /app/path... portion of the URL path is forwarded to the app container via the H2O_WAVE_BASE_URL environment variable in case it is needed by your application for some reason (in most cases, however, the Wave SDK should handle this for you).

In both cases the app's webserver gets the full request URI as received by the Appstore server.

Redirects: The Appstore server's app router component implements a redirect (via 307 TemporaryRedirect HTTP status) in case an instance is accessed via the wrong URL, i.e. it redirects from sub-path to subdomain for instances with DOMAIN RoutingMode and vice versa for BASE_URL.

App route

While it is not a strict requirement, since the platform deploys each app with its own Wave server, we advise that apps use / as their main route:

@app('/')
async def serve(q: Q):
pass

Give an app instance a pretty URL

You can leverage App aliases to give your instances pretty URLs, so that instead of 8184-810243981-23.cloud.h2o.ai your users can access the instance via something like my-pretty-name .cloud.h2o.ai.

Prerequisite: You must be an admin of the environment to run these commands.

To create a single alias for an app for which you want to have a pretty URL, run:

$ h2o admin alias create my-pretty-name <instance id> True

This instance then can be accessed via my-pretty-name.cloud.h2o.ai, accesses to <instance id>.cloud.h2o.ai will result in an HTTP 302 redirect to the alias.

When you’ve created a new app instance, usually because there’s a new version of the app, you may want to change which instance the alias points to. To do this, run:

h2o admin alias assign my-pretty-name <new instance id>`
h2o admin alias promote my-pretty-name

Note that there can be a slight delay before the change gets propagated.

See the CLI documentation for details on these commands.

note

Please note that if the environment requires base URL app routing for all apps, you will need to add this alias to the App Store TLS certificate.

The App Store allows importing apps that do not bundle executable code (and thus cannot have instances) but only represent a link to an external website. This kind of app is referred to as a "Link App". The goal is to inject an external link into the App Store UI/CLI in a way that is consistent in terms of UX with regular apps (app icon, listing on App Store page, categorization, app details with long description and screenshots, etc.).

You can create a link app by setting LINK as the value of App.InstanceLifecycle in app.toml. In such a case, you also need to set the Link.Address value to a URL of your choice. The UI and CLI will then direct users to this URL instead of directing them to an instance of the app.

A link app bundle still contains app.toml and static/, but should not contain any source code or requirements.txt.

A link app can leverage all the parameters in the App section, however the Runtime and File sections must be empty. For example:

[App]
Name = "ai.h2o.examples.link"
Version = "0.0.1"
Title = "Example: Link"
Description = "Showcasing the link functionality."
InstanceLifecycle = "LINK"

[Link]
Address = "https://h2o.ai"

Container Apps

The App Store allows importing containers in addition to Wave apps by admin users only.

You can create a container app by setting container as the value of Runtime.AppMode in app.toml. In such a case, you can also specify Runtime.Port value to the port where the web application is running on. You can also specify Runtime.AppArgs and Runtime.AppEntryPoint to specify the entrypoint and arguments to start the container otherwise the default entrypoint or the container will be used. Runtime.CustomImage can be set to specify where the container is.

A container app bundle still contains app.toml and static/, but should not contain any source code or requirements.txt.

In order for containerized apps to be imported the administrator must configure the server config option config.allowedCustomImageRegexes with the allowed registries containers can be imported from.

  allowedCustomImageRegexes:
- "^123456\\.dkr\\.ecr\\.us-east-1\\.amazonaws.com\\/.*"
[App]
Name = "ai.h2o.examples.container"
Version = "0.0.1"
Title = "Example: Container"
Description = "Showcasing the container functionality."

[Runtime]
AppMode = "container"
CustomImage = "customImage:latest"
AppArgs = ["--mode", "production"]
AppEntryPoint = ["./server"]
Port = 1337

To import this container app it must be bundled h2o app bundle and then imported using h2o admin app import with the optional --set-image flag where the container image address can be overridden.

In the case a container app is imported without an image it will be not runnable. The container image can be set using h2o admin app set-image <appID> <image> and then you must run h2o admin app preconditions refresh <appID> or wait until the appstore refreshes all the apps periodically to check if they are runnable.

Configure an app via the user interface

While tags and visibility for an App can be configured via the CLI, these attributes can also be set using the user interface, as described below:

note

Since the user interface is being continually improved, it is possible that the images below will not match exactly what you see in your version of H2O AI Cloud.

On the "My Apps" page, click on the pencil icon for the app you wish to edit:

My Apps - edit icon

This will bring up a panel on the right side of the user interface which will allow you to edit the values for:

  • Visibility (See Visibility for more information)
  • Categories
  • Badges
  • Authorization Tags
App configuration panel

Tags will show up in the "Categories", "Badges", or "Authorization Tags" select menus based on the following criteria:

  • If the tag has isCategory set to true it will be treated as a "Category", which allows it to be filtered on the App Store's main page.

  • If the tag has one or more visitorRoles set, it will be treated as an "Authorization Tag".

  • Otherwise, the tag will serve as a "Badge" in the App Store UI:

    Example badge

    The badge tags let the developer or the system administrator share more information about the app with end users. For instance, the administrator can configure an open-source badge if your environment has many open-source applications.

    Then the developers can tag their open source apps with this badge if they want to indicate to the user that the code is available in GitHub. This additional information shows on the App details page, the My apps page, and the Admin apps page.

    badge tag

See App Tag to learn more about tags.


Feedback