Earlier, I described the entire workflow of how to ROFLize an app, from setting up prerequisites to testing it. In a 2-part addendum, I will take you through the various features of ROFL that enhance the apps using it, and also how to troubleshoot issues. The first part covers the first three features.
ROFL Marketplace
While outlining the deployment process of the workflow, I first mentioned the ROFL marketplace. So, what is it? The ROFL marketplace is an on-chain protocol designed for app developers and ROFL node providers.
As the diagram shows, the ROFL marketplace has three entities.
- App developers: They build an app, register it on-chain, and rent a machine from the ROFL provider to deploy their apps.
- ROFL providers: They are the on-chain entity that bills and assigns machines to the developers, with a hosting plan called the offer.
- ROFL nodes: These are servers running on the Oasis node that instantiate machines for hosting an app.
If you are an app developer, refer to the 2-part workflow series, and the deployment process in Part 2 will be relevant for you. Or, if you approach this as an ROFL provider, start by learning about the ROFL node and setting it up to execute runtime off-chain logic inside a TEE.
Secrets
Sometimes we encounter situations where containers need to access data, but exposing it would be disastrous. API keys for accessing certain services fall into this category, and blockchain transparency cannot prevent them from becoming public. Containers running in ROFL can safely pass this type of data using the secrets feature.
Secrets are arbitrary key-value pairs powered by end-to-end encryption, which can only be decrypted inside an app with the right attestation. You can manage this feature using the Oasis CLI, for example:
echo -n "my very secret value" | oasis rofl secret set mysecret -
For a detailed understanding of secret management commands, including importing from .env files, removing secrets, and other advanced features, check out the CLI documentation.
In the above example, the mysecret we created is only encrypted and updated in the local app manifest file, and not yet reflected in the full app version. This enables you to configure multiple secrets without constantly updating the on-chain app configuration. Once you have created all the secrets you need, you can update them all at once with the usual command.
oasis rofl update
Note: The local app manifest doesn't need to be private while storing the secrets. The secret values inside the manifest are end-to-end encrypted. In effect, they are completely inaccessible, even to the administrator who set them up. The reason why only the app can decrypt the secret is that every time a new secret is created, it is encrypted with a corresponding new ephemeral key. As soon as encryption is done, the ephemeral key is discarded and irretrievable.
Now, the secrets pass inside the ROFL containers in two ways.
- Environment Variables
Each secret is automatically exposed in the Compose environment and can be trivially used in the Compose file. When this happens, the secret name convention changes, using capitalization and underscores. For example, the earlier known mysecret will now become MYSECRET. However, if you use any space in the name, e.g., my secret, it will become MY_SECRET.
services:
test:
image: docker.io/library/alpine:3.21.2@sha256:f3240395711384fc3c07daa46cbc8d73aa5ba25ad1deb97424992760f8cb2b94
command: echo "Hello $MYSECRET!"
environment:
- MYSECRET=${MYSECRET}
- Container Secrets
Each secret is also defined and can be passed as a container secret as per Docker documentation. You will also need to define this as an external secret since it is created by the app during boot.
services:
test:
image: docker.io/library/alpine:3.21.2@sha256:f3240395711384fc3c07daa46cbc8d73aa5ba25ad1deb97424992760f8cb2b94
command: echo "Hello $(cat /run/secrets/mysecret)!"
secrets:
- mysecret
secrets:
mysecret:
external: true
Persistent Storage
Sapphire's confidential EVM runtime enables ROFL developers to use smart contracts for secure and consistent storage. But this is not workable for apps with intensive read/write functions. That's why ROFL has built-in support for local persistent storage. This feature comes with these characteristics:
- Local per-machine storage, not synchronized across other ROFL replicas
- Fully encrypted on the host machine
- Preserved during ROFL upgrades and node restarts
One of the key benefits of this feature is its utility for caching. Whenever the compose.yaml file defines Docker images, persistent storage becomes the default storage destination. They are fetched only the first time an app is deployed, and afterwards, a cached version is used.
Another aspect of the feature is that all non-external container volumes will automatically reside in persistent storage. Let me illustrate this with an example. Here, I have defined a new volume, as referenced in Docker documentation. I have named it my-volume and made .ollama in the home folder persistent. As a result, every time a machine hosting the app restarts, I will not need to download ollama models.
compose.yaml
services:
ollama:
image: "docker.io/ollama/ollama"
ports:
- "11434:11434"
volumes:
- my-volume:/root/.ollama
entrypoint: ["/usr/bin/bash", "-c", "/bin/ollama serve & sleep 5; ollama pull deepseek-r1:1.5b; wait"]
volumes:
my-volume:
In the concluding part of this addendum guide, I will discuss the remaining features and troubleshooting process.
For technical specs, APIs, architecture, and integration guides, the Oasis documentation is your starting point.
For direct support on specific issues, the Oasis engineering team is available in the dev-central channel on the official Discord.

Top comments (0)