Developer Guide

App Bundle Structure

Each app has to be bundled as a zip archive (commonly used with suffix .wave) 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
  • app source code

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

app.toml

Each app archive has to contain a 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"
Category = "Other"
Keywords = ["awesome"]
[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
[[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
    • Name (string) - a machine-oriented unique identifier of the app (links different app versions together)
    • Version (string) - a https://semver.org version of the app
  • Runtime
    • Module (string) - the name of the main Python module of the app, i.e., the app should be started via python3 -m $module_name
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 (struct)
    • Title (string) - a human-oriented name of the app for presentation in UI/CLI
    • Description (string) - a single-line description of the app for presentation in UI/CLI
    • LongDescription (string) - a 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 ([]())
    • Category (string) - category for organizing apps into groups (UI recognizes All, Data Science, Financial Services, Healthcare, Insurance, Manufacturing, Marketing, Retail, Sales, Telecommunications, and Other)
    • Keywords (list of strings) - keywords for search in the UI/CLI; currently not supported
  • Runtime (struct)
    • RuntimeVersion (string) the name of the runtime version the app will run on top of (similar to a docker base image, see Runtime Environment); empty/unspecified means the platform's default, valid values differ based on platform deployment and configuration.
    • VolumeMount (string) and VolumeSize (string) - request for a volume to persist app instance data across restarts, VolumeMount has to be an absolute path, VolumeSize needs to conform to the k8s resource model.
    • ResourceVolumeSize (string) - request for a volume to persist internal app resources (such as Python venv) across restarts, only recommended for production-quality apps with sizeable resources due to cluster node limits, needs to conform to the k8s resource model.
    • MemoryLimit (string) and MemoryReservation (string) - memory requirements for an instance of the app (defaults to service-wide settings managed by Admins); be conservative with these limits; MemoryLimit is a hard limit on the maximum amount of memory an instance can use before it is OOM-killed; MemoryReservation is how much memory is required to schedule an instance of the app; both need to conform to the k8s resource model.
    • CPULimit (string) and CPUReservation (string) - cpu requirements for an instance of the app (defaults to service-wide settings managed by Admins); be conservative with these limits; CPULimit Maximum CPU usage that an instance of the app can use; CPUReservation How many CPU units is required to schedule an instance of the app; both need to conform to the k8s resource model.
    • GPUCount (int) and GPUType (string) - GPU requirements of the app; empty/unspecified GPUType means the platform's default, valid values differ based on platform deployment and configuration.
    • EnableOIDC (bool) - Enable wave to be setup with OIDC authorization giving access to the user's authentication and authorization information from your app (see Wave Security for details).
    • EnableSHM (bool) - Enable extended docker shared memory for the app; some Libraries like pyTorch use Shared Memory for Multiprocessing. See this issue for more details on this topic.
  • Env (struct) - request for a literal value/secret to be injected into an instance at startup-time as an Env variable; repeated; see Utilizing Secrets.
    • Name (string) - name of the Env variable to the injected into the Python process; names prefixed with H2O_CLOUD are disallowed, names prefixed with H2O_WAVE are also disallowed, except H2O_WAVE_APP_MODE and names allowed by the administrator.
    • Secret (string) - name of the shared secret to use; each secret can contain multiple key-value pairs; cannot be used together with Value.
    • SecretKey (string) - name of the key within the secret to use; cannot be used together with Value.
    • Value (string) - the literal value of the Env variable; cannot be used together with Secret/SecretKey.
  • File (struct) - request for a literal value/secret to be injected into an instance at startup-time as a file; repeated; see Utilizing Secrets.
    • Path (string) - path to inject the file to; relative path means relative to the directory with the app code as determined by the platform; path dir cannot be / or . (only subdirs are allowed); path dir has to be unique across all other File configurations; path dir /resources is disallowed.
    • Secret (string) - name of the shared secret to use; each secret can contain multiple key-value pairs; cannot be used together with Value.
    • SecretKey (string) - name of the key within the secret to use; cannot be used together with Value.
    • Value (string) - the literal value of the file; cannot be used together with Secret/SecretKey.

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
deb10_py37_wlatest Default
gpu_py37_wlatest
...
note

At this moment, the platform does not 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 Appstore 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 Appstore 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

Updating 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, H2O AI Hybrid Cloud Appstore 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.

Utilizing 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 allows developers to link their apps with external dependencies (e.g., S3, Driverless AI) securely, while allowing easy overrides for local development or deployments outside the platform.

The h2o secret list command will let you see a list of secrets available to be used by your apps. The optional -v flag will filter based on PRIVATE and ALL_USERS

$ h2o secret list
NAME VISIBILITY KEYS
private-secret PRIVATE password, user, host
all-users-secret ALL_USERS accessKey, secretKey
...

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