Getting Started

In this tutorial we will:

  1. Write our own custom filter for Envoy
  2. build a WASM module from our filter and store it as an OCI image/
  3. push the module to the WebAssembly Hub
  4. deploy the image to a running instance of Envoy.
  5. curl the instance to see our filter act on a request.

For in-depth guides, please refer to:

Prepare environment

To get started, let’s deploy a sample service that we can call through Envoy. We’ll deploy the sample petstore API:

$  kubectl apply -f \

You should now have the petstore running:

$  kubectl get po 

NAME                        READY   STATUS    RESTARTS   AGE
petstore-5dcf5d6b66-n8tjt   1/1     Running   0          2m20s

Deploying Envoy

In this tutorial, we’ll use Gloo, an API Gateway based on Envoy that has built-in wasm support but these steps should also work for base Envoy.

First, install Gloo using one of the following installation options:

helm repo add gloo
helm repo update
kubectl create ns gloo-system
helm install --namespace gloo-system --set global.wasm.enabled=true gloo gloo/gloo

glooctl install gateway -n gloo-system --values <(echo '{"namespace":{"create":true},"crds":{"create":true},"global":{"wasm":{"enabled":true}}}')

You can install glooctl via

curl -sL | sh
export PATH=$HOME/.gloo/bin:$PATH

Gloo will be installed to the gloo-system namespace.

You can deploy your own gloo (version 1.2.10 and above), by enabling the experimental WASM support when installing. When installing you need to set the “global.wasm.enabled” flag to true. If installing with glooctl, you can use the following command:

glooctl install gateway -n gloo-system --values <(echo '{"namespace":{"create":true},"crds":{"create":true},"global":{"wasm":{"enabled":true}}}')

You can add the --dry-run flag to glooctl to generate a yaml for you instead of installing directly.

Verify set up

Lastly, we’ll set up our routing rules to be able to call our petstore service. Let’s add a route to the routing table:

Download and apply the virtual service manifest

$  kubectl apply -f default-virtualservice.yaml

To get Gloo’s external IP, run the following:

$  URL=$(kubectl get svc -n gloo-system gateway-proxy \
 -o jsonpath='{.status.loadBalancer.ingress[*].ip}')

Now let’s curl that URL:

$  curl -v $URL/api/pets

*   Trying
* Connected to ( port 80 (#0)
> GET /api/pets HTTP/1.1
> Host:
> User-Agent: curl/7.54.0
> Accept: */*
< HTTP/1.1 200 OK
< content-type: application/xml
< date: Tue, 10 Dec 2019 16:02:00 GMT
< content-length: 86
< x-envoy-upstream-service-time: 2
< server: envoy

If you’re able to get to this point, we have a working Envoy proxy and we’re able to call it externally.

Creating a new WASM module

Refer to the installation guide for installing wasme, the WebAssembly Hub CLI.

Let’s create a new project called new-filter:

$  wasme init ./new-filter

You’ll be asked with an interactive prompt which language platform you are building for.

Select cpp and gloo-1.3.x:

? What language do you wish to use for the filter:
  ▸ cpp
? With which platform do you wish to use the filter?:
  ▸ gloo 1.3.x

We should now have a new folder with our new project:

$  cd new-filter
$  ls -l 
-rw-r--r--  1 ceposta  staff    572 Dec 10 09:06 BUILD
-rw-r--r--  1 ceposta  staff    505 Dec 10 09:06
-rw-r--r--  1 ceposta  staff   2782 Dec 10 09:06 WORKSPACE
drwxr-xr-x  3 ceposta  staff     96 Dec 10 09:06 bazel
drwxr-xr-x  3 ceposta  staff     34 Dec 10 09:06 filter-config.json
-rw-r--r--  1 ceposta  staff   2797 Dec 10 09:06
-rw-r--r--  1 ceposta  staff     60 Dec 10 09:06 filter.proto
drwxr-xr-x  7 ceposta  staff    224 Dec 10 09:06 toolchain

Open this project in your favorite IDE. The source code is C++ and we’ll make some changes to create a new filter.

Making changes to the sample filter

Feel free to explore the project that was created. This is a Bazel project that sets up the correct emscripten toolchain to build C++ into WASM. Let’s open ./new-filter/ in our favorite IDE.

Navigate to the FilterHeadersStatus AddHeaderContext::onResponseHeaders member method. Let’s add a new header that we can use to verify our module was executed correctly (later down in the tutorial). Let’s add a new response header named doc-header:

addResponseHeader("doc-header", "it-worked");

Your method should look like this:

FilterHeadersStatus AddHeaderContext::onResponseHeaders(uint32_t) {
  LOG_DEBUG(std::string("onResponseHeaders ") + std::to_string(id()));
  addResponseHeader("newheader", root_->header_value_);
  addResponseHeader("doc-header", "it-worked");
  replaceResponseHeader("location", "envoy-wasm");
  return FilterHeadersStatus::Continue;

Building the filter

Now, let’s build a WASM image from our filter with wasme. The filter will be tagged and stored in a local registry, similar to how Docker stores images.

In this example we’ll include the registry address as well as our GitHub username which will be used to authenticate to the registry.

Build and tag our image like so:

wasme build . -t ilackarms/add-header:gloo-1.3

The module will take up to a few minutes to build. In the background, wasme has launched a Docker container to run the necessary build steps.

When the build has finished, you’ll be able to see the image with wasme list:

wasme list
NAME                                    SHA      UPDATED             SIZE   TAGS  bbfdf674 26 Jan 20 10:45 EST 1.0 MB gloo-1.3

Now that we’ve built the WASM module, let’s publish it into a registry so we can deploy it to our Envoy proxy running in Kubernetes.

Pushing WASM module to registry

To do that, let’s login to the using GitHub as the OAuth provider. From the CLI:

$  wasme login

Using port: 60632
Opening browser for login. If the browser did not open for you, please go to:

You should see a GitHub OAuth screen:

Click the “Authorize” button at the bottom and continue.

After successful auth, you should see this in the terminal:

success ! you are now authenticated

Now let’s push to the registry.

$  wasme push
INFO[0000] Pushing image
INFO[0001] Pushed
INFO[0001] Digest: sha256:22f2d81f9b61ebbf1aaeb00aa7bde20a90b90ec8bb5f492cc18a140de205dc32

The tag name to use is<your-git-username>/<some-name>:<some-version>

When you’ve pushed, you should be able to see your new module in the registry:

$  wasme list --published  

NAME                        SHA      UPDATED             SIZE   TAGS
ilackarms/add-header        6aef37f3 13 Jan 10 12:54 MST 1.0 MB gloo-1.3

Deploy our new module

To deploy the module to Envoy via Gloo:

wasme deploy gloo --id=add-header \
  --config '{"name":"hello","value":"World!"}'

It will take a few moments for the image to be pulled by the server-side cache.

Verify behavior

To verify the behavior, let’s use the curl command from above:

curl -v $URL/api/pets

We expect to see our new headers in the response:

*   Trying
* Connected to ( port 80 (#0)
> GET /api/pets HTTP/1.1
> Host:
> User-Agent: curl/7.54.0
> Accept: */*
< HTTP/1.1 200 OK
< content-type: application/xml
< date: Fri, 20 Dec 2019 19:17:13 GMT
< content-length: 86
< x-envoy-upstream-service-time: 0
< hello: World!
< location: envoy-wasm
< server: envoy

You can see our new header hello: World! in our response!

Add this new module to the Web Assembly Hub catalog

Just like when we pushed the module to the registry, to add the module to the catalog on WebAssembly Hub, we need to be logged in. If you’re already logged in, skip this command:

$  wasme login

Adding the the catalog is a git-ops workflow that involves a Pull Request. To initiate the process, run the wasme catalog add command:

$  wasme catalog add ilackarms/add-header:gloo-1.3

Note, the tag we are adding matches what we built in previous steps.

This will give us an interactive prompt to fill in the details of our WebAssembly Hub extension:

? Please provide name of the extension test
? Please provide short description of the extension (required) short des
? Please provide long description of the extension long desc
? Please provide the url to the source code
? Please provide the url to the documentation
? Please provide the name of the extension creator christian-posta
? Please provide a url for the extension creator
? Please provide a url for the extension logo

Lastly, you’ll be prompted one last time whether to proceed. Type Y and hit enter:

creatorName: christian-posta
extensionRef: ilackarms/add-header:gloo-1.3
longDescription: long desc
name: test
shortDescription: short des

In these steps we will:
         Create a feature branch named test-gloo-1.3
         Add your spec to this branch in this location: catalog/test/gloo-1.3/spec.yaml
         And open a Pull Request against
Making sure your fork is available
Making sure a feature branch is available
Adding your catalog item to the feature branch
Makeing sure a PR is open
Created PR:

If you open the URL, you should see a PR was created in the wasme GitHub repo:

Pull Request

The Pull Request from here will be reviewed by the WebAssembly Hub team and included in the catalog if it’s accepted.