Using Nautilus
Nautilus is a verifiable off-chain compute layer on Sui. It enables builders to delegate sensitive or resource-intensive tasks to a self-managed trusted execution environment (TEE) or a TEE marketplace like Marlin Oyster while preserving trust on-chain through smart contract-based verification.
The Nautilus repo provides scaffolding such as reproducible builds, signature formatting, and HTTPS traffic forwarding. This allows you to focus on implementing the off-chain computation logic inside the enclave.
An on-chain template includes the minimal smart contract code required to register a Nautilus instance and its public key.
Nautilus is NOT just about running code in privacy-preserving manner in a TEE. Part of the overall value proposition is on-chain verification of computation integrity:
- PCRs (enclave measurements) must be registered and verified on-chain
- Every computation result could optionally be verified on-chain
Developer workflowβ
To use Nautilus in your app:
-
Implement the enclave in Rust with the desired computation logic.
-
Deploy a Move smart contract that stores the expected platform configuration registers (PCRs) and allows updates by the contract deployer.
-
Deploy the enclave instance on AWS and register it on-chain using its attestation document.
-
Upload signed responses from the registered enclave, verify them on-chain, and consume the results in your smart contract.
This guide walks you through the following steps:
-
Write and deploy a basic Nautilus off-chain instance using AWS Nitro Enclaves. The example instance runs a server that fetches weather data for a specific location.
-
Write a Move smart contract that registers the enclave by verifying its attestation and public key, then verifies the Nautilus response (signature and payload) on-chain and mints an NFT with the location and temperature data.
Frontend code is not included in this guide.
- Prerequisites
-
Install AWS CLI v2.
-
Install Make.
-
Download or git clone the Nautilus repo.
Step 1: Create an AWS developer accountβ
Set up an AWS developer account and install the AWS CLI. For detailed instructions, see the AWS Nitro Enclaves getting started guide.
Step 2: Configure AWS SSOβ
Confirm AWS CLI version:
aws --version
This should print a version that begins with 2:
aws-cli/2.x.x
Configure the single sign on for the AWS CLI tool using the command:
aws configure sso
When prompted, fill in the following fields:
-
SSO start URL:
<your-sso-start-url> -
SSO region:
us-east-1
If you see expired credentials, reset them with:
rm ~/.aws/credentials
rm ~/.aws/config
Step 3: Log in to AWS SSOβ
After configuring AWS SSO, you will be redirected to login through a browser window. Choose an account and the role AdministratorAccess. AWS should create a profile such as AdministratorAccess-094557288217.
Step 4: List existing EC2 SSH key pairwiseβ
Run the following command to list all key pairs created in your AWS account. These are used only for SSH into EC2.
aws ec2 describe-key-pairs \
--region us-east-1 \
--profile AdministratorAccess-094557288217
Step 4.1: Create a new EC2 SSH key pair (optional)β
If you don't already have one you want to use, create a new EC2 SSH key pair with the command:
aws ec2 create-key-pair \
--key-name <your-alias> \
--query 'KeyMaterial' \
--output text \
--region us-east-1 > ~/.ssh/<your-alias>.pem
Configure the SSH key's permissions:
chmod 400 ~/.ssh/<your-alias>.pem
Step 5: Set environment variablesβ
Set the KEY_PAIR variable with the name of your EC2 key pair:
export KEY_PAIR=<your-key-pair-name>
Then, you must set the AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and AWS_SESSION_TOKEN environment variables. These are required for the Nautilus provisioning script.
Export the values for these variables using the command:
aws configure export-credentials \
--profile AdministratorAccess-094557288217 \
--format env > ~/aws-temp-creds.sh
Then, load these credentials:
source ~/aws-temp-creds.sh
Confirm that they have been set properly:
env | grep AWS_
You can also verify your AWS identity with the command:
aws sts get-caller-identity
Expected output:
{
"UserId": "AROARMBANW4MXIS7SZ7S6:george.melas@mystenlabs.com",
"Account": "094557288217",
"Arn": "arn:aws:sts::094557288217:assumed-role/AWSReservedSSO_AdministratorAccess_bb69586677f0373c/george.melas@mystenlabs.com"
}
If this works, then your AWS environment is ready to use with Nautilus.
Step 6: View the Nautilus repository structureβ
View the contents of the Nautilus repository that you have downloaded or cloned.
/move
/enclave # Utility functions for enclave config and public key registration
/weather-example # Example on-chain logic using enclave functions
/twitter-example # Alternative example on-chain logic
/src
/aws # AWS boilerplate; does not require modification.
/init # AWS boilerplate; does not require modification.
/system # AWS boilerplate; does not require modification.
/nautilus-server # Nautilus server that runs inside the enclave.
/src
/apps
/weather-example # Example directory. Replace with your own application logic as needed.
mod.rs # Defines the process_data endpoint and related logic. Replace with your off-chain computation logic.
allowed_endpoints.yaml # Lists all endpoints the enclave can access. By default, the enclave has no internet access. During configuration, this file generates code for traffic forwarding.
/twitter-example # Another example directory with a similar structure.
run.sh # Runs the Rust server inside the enclave. Do not modify.
common.rs # Common code for retrieving attestation. Do not modify.
Key implementation files:
-
allowed_endpoints.yaml: Defines external API access permissions. -
mod.rs: Contains application-specific computation logic. -
run.sh: Handles server startup and configuration. -
common.rs: Manages retrieving attestation.
To create your own Nautilus app:
-
Add a directory under
move/my_appfor your Move modules. -
Add a directory under
src/nautilus-server/src/apps/my_appfor your Rust server logic. -
Use existing app directories as references.
-
Build frontend logic to interact with the deployed Move contract and enclave-hosted Rust server.
Most of the template can remain unmodified, which streamlines development while giving you full control over app-specific logic.
Step 7: Run the Nautilus provisioning scriptβ
Run from the root of the Nautilus directory:
cd /nautilus
sh configure_enclave.sh weather-example
The setup script:
-
Launches a preconfigured EC2 instance and allocates a Nitro Enclave.
-
Builds the Rust-based template application into an enclave image format (EIF) binary and runs it inside the enclave.
-
Configures required HTTP domains so the enclave can access external APIs through the parent EC2 instance (as the enclave itself has no internet access).
-
Exposes 3 endpoints to allow client-side communication with the enclave.
When the enclave starts, it generates a fresh key pair and exposes the following endpoints:
-
health_check: Probes allowed domains inside the enclave. This logic is built into the template and does not require modification. -
get_attestation: Returns a signed attestation document over the enclave public key. Use this during on-chain registration. This logic is built into the template and does not require modification. -
process_data: Fetches weather data from an external API, signs it with the enclave key, and returns the result. You must implement this customizable logic.
Run sh configure_enclave.sh -h to view additional instructions. If your AWS account is not in us-east-1, you might need to configure REGION and AMI_ID values specific to your region. Refer to this guide to find a suitable Amazon Linux image ID.
$ export REGION=<your-region>
$ export AMI_ID=<find-an-amazon-linux-ami-for-your-region>
You might need to create a VPC with a public subnet. Refer to this AWS guide for instructions.
Step 8: Run the exampleβ
To run the weather example as is, you do not need to modify allowed_endpoints.yaml because it already includes api.weatherapi.com. Follow the prompts to enter the required values. This step demonstrates how to store a secret (an API key) using AWS Secrets Manager, so you can avoid including the secret in the public application code.
Enter EC2 instance base name: weather # anything you like
Do you want to use a secret? (y/n): y
Do you want to create a new secret or use an existing secret ARN? (new/existing): new
Enter secret name: weather-api-key # anything you like
Enter secret value: 045a27812dbe456392913223221306 # this is an example api key, you can get your own at weatherapi.com
The output will include:
-
Instance ID
-
Public IP
-
Security group
-
IAM role created
Save the public IP, such as:
Instance launched with ID: i-0728fc72a13834bc8
Public IP: 18.208.226.122
Wait 2 to 3 minutes for EC2 initialisation.
Step 9: Copy the Nautilus repo to EC2β
When the script completes successfully, changes are generated in /src/nautilus-server/run.sh and expose_enclave.sh. You must copy these changes to EC2, as they are required when building the enclave image.
rsync -avz -e "ssh -i ~/.ssh/<your-alias>.pem" \
/Users/<you>/suistack/nautilus/ \
ec2-user@<public-ip>:~/nautilus/
# example: rsync -avz -e "ssh -i ~/.ssh/melas-se-3.pem" \
# /Users/geomel/suistack/nautilus/ \
# ec2-user@18.208.226.122:~/nautilus/
To allow the enclave to access additional external domains, add them to allowed_endpoints.yaml. If you update this file, you must rerun configure_enclave.sh to generate a new instance, as the endpoint list is compiled into the enclave build.
You can optionally create a secret to store any sensitive value you do not want included in the codebase. The secret is passed to the enclave as an environment variable. You can verify newly created secrets or find existing ARNs in the AWS Secrets Manager console.
Step 10: SSH into EC2β
ssh -i ~/.ssh/<your-alias>.pem ec2-user@<public-ip>
# example: ssh -i ~/.ssh/melas-se-3.pem ec2-user@18.208.226.122
You are now inside the directory containing the server code, including the committed file changes from the previous step. Next, build the enclave image, run it, and expose the HTTP endpoint on port 3000.
$ cd nautilus/
$ make ENCLAVE_APP=<APP> && make run # this builds the enclave and runs it, ex. `make ENCLAVE_APP=weather-example`
$ sh expose_enclave.sh # this exposes port 3000 to the Internet for traffic
Use make run-debug instead of make run to run the enclave in debug mode. This prints all logs, where the production build does not. In debug mode, the PCR values are all zeros and are not valid for production use.
You can now interact with the enclave from the outside world. You can find the PUBLIC_IP in the AWS console.
# Run a health check:
$ curl -H 'Content-Type: application/json' -X GET http://<PUBLIC_IP>:3000/health_check
# View the attestation document:
$ curl -H 'Content-Type: application/json' -X GET http://<PUBLIC_IP>:3000/get_attestation
# Get weather data for a location:
$ curl -H 'Content-Type: application/json' -d '{"payload": { "location": "San Francisco"}}' -X POST http://<PUBLIC_IP>:3000/process_data
Optionally, you can set up an application load balancer (ALB) for the EC2 instance with an SSL/TLS certificate from AWS Certificate Manager (ACM), and configure Amazon Route 53 for DNS routing. For more information, see the ACM User Guide and the ALB Guide.