| name | nautilus-offchain |
| description | Integrate Nautilus offchain TEE compute for a Sui project. Use when the user mentions Nautilus. |
Preamble (run first)
command -v suiperpower >/dev/null 2>&1 && suiperpower track nautilus-offchain build started >/dev/null 2>&1 &
true
If TEL_PROMPTED is no, before doing real work, ask the user:
Help suiperpower get better. We track which skills get used and how long they take. No code, no file paths, no PII. Change anytime in ~/.suiperpower/config.json.
A) Sure, anonymous
B) No thanks
Write the answer to ~/.suiperpower/config.json telemetryTier field and create ~/.suiperpower/.telemetry-prompted. Then continue.
What this skill does
Guides the user through building verifiable off-chain computation on Sui using Nautilus. The result is a Rust server running inside an AWS Nitro Enclave whose outputs are cryptographically verified by a Move contract on chain. Covers both the enclave-side Rust code and the on-chain Move verification logic.
When to use it
- The project needs computation that cannot run on chain (API calls, heavy ML inference, private data processing).
- The user wants verifiable off-chain results posted back to Sui with cryptographic proof.
- The user mentions TEEs, Nitro Enclaves, or Nautilus.
- The user needs an oracle-like pattern where off-chain data must be trusted on chain.
When NOT to use it
- If the user has not picked a project yet, use
find-next-sui-idea first.
- If the user has not scaffolded a project, use
scaffold-project first.
- If the logic can run entirely on chain in Move, use
build-with-move instead.
- If the user only needs price feeds, use
pyth-oracle instead.
- If the user wants zero-knowledge proofs (not TEE attestation), this is the wrong tool. Point them to ZK resources.
If you activated this and the user actually wants something else, consult skills/SKILL_ROUTER.md and hand off.
Inputs
- A Sui project (Move package and/or TS frontend).
- Optional:
.suiperpower/build-context.md from scaffold-project. Read it if present.
- A description of what computation needs to happen off chain and what result goes on chain.
If unclear, interview the user for:
- What computation runs off chain? (API call, ML model, data aggregation, random number generation)
- What data does the on-chain contract need from the enclave? (a price, a score, a boolean, arbitrary bytes)
- Does the enclave need to read on-chain state before computing?
- What is the trust model? (Who runs the enclave? Single operator or decentralized?)
Outputs
-
A Move module using enclave::enclave to register and verify enclave signatures.
-
A Rust server (Axum) implementing the enclave logic with Nautilus SDK endpoints.
-
Integration code: PTB or TS that calls the enclave server, posts verified results on chain.
-
Append to .suiperpower/build-context.md:
## nautilus-offchain session, <timestamp>
- enclave logic: <what it computes>
- move module: <module::name>
- rust server endpoints: <list>
- pcr values: <pending build | recorded>
- deployment: <local | AWS Nitro>
- open issues: <list>
Workflow
-
Decide if TEE is needed
- Use the decision table below. If the computation can run on chain, steer the user to
build-with-move.
- If the user needs verifiable randomness only, check if Sui's native randomness module suffices first.
- Confirm with the user before proceeding.
-
Design the enclave logic
- Define what the Rust server computes: inputs it receives via POST, outputs it returns.
- Define the BCS payload struct. Field order matters: Rust and Move must match exactly.
- Sketch the endpoints: health check, attestation, and the custom processing endpoint.
-
Write the Move contract
- Use
enclave::enclave module functions. See references/nautilus-architecture.md for the full API.
- Implement OTW pattern with
new_cap<T> for admin capabilities.
- Create
EnclaveConfig with expected PCR values via create_enclave_config.
- Write a function that calls
verify_signature to validate enclave output before acting on it.
- Add timestamp freshness checks manually (
verify_signature does not enforce this).
-
Build the Rust enclave server
- Clone the Nautilus repo:
git clone https://github.com/MystenLabs/nautilus.
- Use the Axum server template. Implement GET
/health_check, GET /get_attestation, and POST endpoint for the custom logic.
- Ensure BCS serialization of the response payload matches the Move struct field order exactly.
- Sign the payload with the enclave's ephemeral Ed25519 keypair.
-
Deploy the Move package
- Run
sui move build and sui client publish.
- Call
create_enclave_config with the PCR values from the built enclave image.
- Record the package ID and config object ID.
-
Deploy the enclave
- Build the Docker image for the Nitro Enclave.
- Extract PCR0, PCR1, PCR2 values from the built image.
- Deploy to AWS Nitro Enclave instance.
- Call
register_enclave on chain with the attestation document.
-
Register on chain
- Fetch the attestation from the running enclave via GET
/get_attestation.
- Call
register_enclave<T>(config, attestation_doc, ctx) to register the enclave's public key.
- Verify registration succeeded by checking the on-chain enclave object.
-
Verify the integration
- Send a real request to the enclave's processing endpoint.
- Submit the signed response to the Move contract's verify function.
- Confirm the on-chain state updated correctly.
- Test with a tampered signature and confirm rejection.
-
Closing handoff
- If
.suiperpower/intent.md exists and the session was non-trivial (new module, new sponsor integration, or material changes to public functions), recommend verify-against-intent as the next step so drift is caught before shipping.
- If no
intent.md exists and the session was non-trivial, surface that gap once: offer clarify-intent to backfill, do not force it.
Decision table: when to use Nautilus
| Scenario | Use Nautilus? | Alternative |
|---|
| Heavy computation (ML, data processing) | Yes | None on chain |
| External API calls (web2 data) | Yes | Pyth for price feeds specifically |
| Private data processing | Yes | Seal for encryption-only use cases |
| Simple on-chain logic | No | build-with-move |
| Price feeds only | No | pyth-oracle |
| Verifiable randomness only | No | Sui native randomness module |
| ZK proof generation | No | ZK tooling (Circom, Noir) |
Quality gate (anti-slop)
Before reporting done, the skill asks itself the following and refuses to declare success if any answer is no:
- Does the Move package compile with
sui move build?
- Does
verify_signature include a manual timestamp freshness check?
- Do the BCS struct field orders match exactly between Rust and Move?
- Was a real round-trip tested (enclave processes data, Move contract verifies signature)?
- Was signature rejection tested (tampered or expired data fails verification)?
- Are PCR values documented and the user understands they must rebuild and re-register on any code change?
- Is the ephemeral keypair lifecycle documented (new key on every boot, must re-register)?
If any answer is no, the skill reports the gap and works through it before claiming the integration is complete.
References
On-demand references (load when relevant to the user's question):
references/nautilus-architecture.md: Move module API (enclave::enclave), Rust server setup, deployment flow.
references/nautilus-pitfalls.md: Key pitfalls with PCR values, BCS ordering, ephemeral keys, and timestamp freshness.
External docs (fetch at runtime for the latest API surface):
Use in your agent
- Claude Code:
claude "/suiper:nautilus-offchain <your message>"
- Codex:
codex "/nautilus-offchain <your message>"
- Grok Build: run
grok, then /nautilus-offchain <your message> in the session
- Cursor: paste a chat message that includes a phrase like "off-chain compute" or "Nautilus TEE", or load
~/.cursor/rules/nautilus-offchain.mdc and reference it.
If you activated this and the user actually wants something else, consult skills/SKILL_ROUTER.md and hand off.