DEV Community

Cover image for Take Capact for a spin!
Mateusz Szostok for

Posted on • Originally published at

Take Capact for a spin!

Our first blog post focused strongly on why—why we created Capact, what's the general concept behind it. Now it's time for showing how—how you can use it.

Although, there are many ways to get started with Capact, today we will focus on your perspective as a Capact User.

The recommended way to try out Capact quickly is to set up a local environment.


Detailed instructions are already nicely described in our local installation tutorial. Once you have Capact up and running, we can start!

What you will learn

NOTE: For this blog post, we will use Capact CLI. However, the described scenario can also be executed with our brand-new UI! 😎

In 5 minutes, by running examples from this blog post, you will learn about the following Capact concepts:

  • Interface—provides an option to abstract Implementations.
  • Implementation—defines the actual workflow that is executed.
  • Attribute—provides an option to tag manifests. Today we will use it to select a specific Implementation.
  • Type and TypeInstance—provide a combined feature, where Type holds the schema and TypeInstance the instance for the Type.
  • Action—triggers Engine to render and run a given Interface.
  • Policy—impacts the render process to select desired Implementations.

Creating new workflows is not the concern of this article, and it will be covered in future blog posts.

Here we only briefly describe each part, but I encourage you to take a deeper look at our Terminology section later.


📜 Scenario

The key concepts were described in the Introducing Capact blog post. There, we used Mattermost as an example. It was a real life scenario but at the same time more resource and time-consuming.

For the purpose of this blog post, we created an extremely simple Hello World example.


There are two Implementations for the cap.interface.capactio.examples.say Interface. Depending on the Policy, a different Implementation is selected. By default, Engine selects the Implementation which is first on the list (alphabetical order) and doesn't have any requirements regarding executions, such as requiring AWS credentials.

🎬 Camera, Lights, Action!

Action is the entry point for executing any Interface. It allows you to define which Interface should be executed and with which parameters. This is later consumed by Capact Engine and the appropriate workflow is rendered. Later, you can review it and approve it for the execution.



  1. Make sure that you are logged into local cluster.

  2. List available Interfaces:

    capact hub interfaces get

    We are interested in this part:

                               PATH          LATEST REVISION                  IMPLEMENTATIONS
    # ... trimmed ...
    cap.interface.capactio.examples.greet        0.1.0             cap.implementation.capactio.examples.greet
    cap.interface.capactio.examples.say          0.1.0             cap.implementation.capactio.examples.hello
  3. Create a new Action:

    💡 A Policy is not needed as we use the default behavior here.

    capact act create --name hello cap.interface.capactio.examples.greet
  4. Wait for the Action to have the READY_TO_RUN status:

     capact act get hello
  5. When the status is READY_TO_RUN, run the Action:

     capact act run hello
  6. Watch the progress:

     capact act watch hello
  7. (Optional) Once the Action is finished, copy the name of the Pod for the print workflow step (column PODNAME). To read its logs, run:

    kubectl logs {PODNAME} main

    Example output:

    < message: Hello from Capact >
                        ##        .
                  ## ## ##       ==
               ## ## ## ##      ===
           /""""""""""""""""___/ ===
      ~~~ {~~ ~~~~ ~~~ ~~~~ ~~ ~ /  ===- ~~~
           \______ o          __/
            \    \        __/
  8. Get the Action output (TypeInstance). Copy the id field value:

     capact act get hello -ojson | jq '.Actions[0].output.typeInstances'

    Example output:

       "id": "08fcaa07-7846-47af-b6a7-7c3818c69656",
       "typeRef": {
         "path": "cap.type.capactio.examples.message",
         "revision": "0.1.0"
  9. Get the TypeInstance value.message field value:

     capact ti get {ID} -ojson | jq -r '.[0].latestResourceVersion.spec.value.message'
  10. Delete the Action:

     capact act delete hello

What if you wanted to output a different message, that reminds you about a masterpiece song you can listen to endlessly? With Capact that's really easy ✨

To choose a different Implementation for the say Interface, we need to prepare a dedicated Policy. The power of it is that you don't have to change the main workflow. You treat the step as a building block and just swap it for another existing one.

Here we go again GIF

To test it, create the Action again, and pass the Action Policy.

First, save the Policy to the /tmp/policy.yaml file:

cat <<EOF > /tmp/policy.yaml
  - interface:
      path: cap.interface.capactio.examples.say
    - implementationConstraints:
          - path:
            revision: 0.1.0
Enter fullscreen mode Exit fullscreen mode

Next, create the Action with the Policy:

capact act create --name hello cap.interface.capactio.examples.greet --action-policy-from-file /tmp/policy.yaml
Enter fullscreen mode Exit fullscreen mode

Now, you can repeat steps starting from 4th point—so, once again, wait for the Action to have the READY_TO_RUN status and run it!

In the optional step no. 7, if you read the Pod logs, the output will be:

< message: Never gonna give you up >
                    ##        .
              ## ## ##       ==
           ## ## ## ##      ===
       /""""""""""""""""___/ ===
  ~~~ {~~ ~~~~ ~~~ ~~~~ ~~ ~ /  ===- ~~~
       \______ o          __/
        \    \        __/
Enter fullscreen mode Exit fullscreen mode

Note also that the produced TypeInstance from no. 9 is valid against the same JSON Schema but the message was changed.

What is so special about it?

Our example was simplified to a minimum, but you could see that you can:

  • chain different steps together,
  • have a unified way of execution,
  • built-in Type validation based on JSON Schema,
  • and more importantly, write once and swap implementation details with ease.

Capact is also great if you want to chain different tools together and keep the same entry point (UX) but that requires more complex examples. For now, we recommend you to take a look at our other examples described in the How can I get involved and learn more? section.

Behind the CLI

As you saw, you can use CLI to browse Hub manifests, create a specific Action for a given Interface and retrieve the output. CLI communicates with our Gateway via GraphQL calls, same as the UI (Capact Dashboard). You can read more in the E2E Architecture document.

However, a more important question is, how did it happen?

1  spec:
2    # ... trimmed ...
3    imports:
4      - interfaceGroupPath: cap.interface.capactio.examples
5        alias: examples
6        methods:
7          - name: say
9    # ... trimmed ...
10   steps:
11     - - name: get-message
12         capact-action: examples.say # dynamic step, rendered by Engine based on the Policy.
13     - - name: print
14         template: print
15         arguments:
16           artifacts:
17             - name: message
18             from: "{{steps.get-message.outputs.artifacts.message}}"
19     # ... trimmed ...
20     - name: print
21       inputs:
22         artifacts:
23           - name: message
24           path: /tmp/message.yaml
25       container:
26         image: docker/whalesay:latest
27         command: [ sh, -c ]
28         args: [ "cowsay < /tmp/message.yaml" ]
Enter fullscreen mode Exit fullscreen mode

The snippet above shows the most important parts of the Implementation manifest. In lines 4-7, we declare the import, which is later used in steps. In line 12, we use capact-action to make a dynamic step with a given Interface.

💡 Instead of providing the full Interface path, we use alias assigned to the imported Interface.

As a result, our Engine and, in particular, Argo Renderer has an option to put there an Implementation that fulfills the specified Interface. Thanks to that, this step is swappable based on the defined Policy.

How can I get involved and learn more?

The Getting Started goes into more detail about how to start with Capact and use its more-advanced features. There are also Rocket.Chat and Mattermost tutorials that we keep up-to-date for you to walk you through using Capact in real cases.

We appreciate any input you have about your experience with Capact!

There are plenty of options to contact us:

Thank you for taking the time to learn about Capact 🙌

Top comments (0)