Skip to content
Back to skills

Nova App Builder

CSecurity

Build and deploy Nova Platform apps (TEE apps on Sparsity Nova / sparsity.cloud). Use when a user wants to create a Nova app, write enclave application code, build it into a Docker image, and deploy it to the Nova Platform to get a live running URL. Handles the full lifecycle: scaffold, code, build, push, deploy, verify running. Triggers on requests like 'build me a Nova app', 'deploy to Nova Platform', 'create a TEE app on sparsity.cloud', 'I want to run an enclave app on Nova'.

  • 14 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 7, 2026
developmentpythonrustgobashreactfastapidockerawsgitapi

Works with

  • cli
  • api

Security analysis

C71/100
  • mediumUses curl or wget to download content
  • criticalSends environment variables or credentials to an external URL
  • criticalDownloads and executes remote scripts — classic supply chain attack

Pro scans all 12 files and shows the line behind each finding

Scanned September 7, 2026

npx -y skills add modbender/skill-library-mcp --skill nova-app-builder --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Nova App Builder?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Nova App Builder
[![Security: C — Skills Directory](https://www.skillsdirectory.com/api/skills/modbender-nova-app-builder/badge)](https://www.skillsdirectory.com/skills/modbender-nova-app-builder)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: nova-app-builder
description: "Build and deploy Nova Platform apps (TEE apps on Sparsity Nova / sparsity.cloud). Use when a user wants to create a Nova app, write enclave application code, build it into a Docker image, and deploy it to the Nova Platform to get a live running URL. Handles the full lifecycle: scaffold, code, build, push, deploy, verify running. Triggers on requests like 'build me a Nova app', 'deploy to Nova Platform', 'create a TEE app on sparsity.cloud', 'I want to run an enclave app on Nova'."
---

# Nova App Builder

End-to-end workflow: scaffold → code → push to Git → create app → build → deploy → (on-chain: create app on-chain → enroll version → generate ZK proof → register instance).

## Architecture Overview

Nova apps run inside AWS Nitro Enclaves, managed by **Enclaver** (Sparsity edition) and supervised by **Odyn** (PID 1 inside the enclave). Key concepts:

- **Enclaver**: packages your Docker image into an EIF (Enclave Image File) and manages the enclave lifecycle.
- **Odyn**: supervisor inside the enclave; provides Internal API for signing, attestation, encryption, KMS, S3, and manages networking.
- **Nova Platform**: cloud platform at [sparsity.cloud](https://sparsity.cloud) — builds EIFs from Git, runs enclaves, exposes app URLs.
- **Nova KMS**: distributed key management; enclave apps derive keys via `/v1/kms/derive`.

## Prerequisites (collect from user before starting)

> **Ensure skill is up to date before starting:**
> ```bash
> clawhub update nova-app-builder
> ```
> Older versions are missing `Dockerfile.txt` in the template, causing scaffold to fail.

- **App idea**: What does the app do?
- **Nova account + API key**: Sign up at [sparsity.cloud](https://sparsity.cloud) → Account → API Keys.
- **GitHub repo + GitHub PAT**: Used only to push your app code to GitHub. Nova Platform then builds from the repo URL. The PAT is not passed to Nova Platform.

  **GitHub PAT setup**:
  1. GitHub → Settings → Developer settings → Personal access tokens → Fine-grained tokens
  2. Required permissions: **Contents** (Read & Write), **Metadata** (Read)
  3. Push with token:
     ```bash
     git remote set-url origin https://oauth2:${GH_TOKEN}@github.com/<user>/<repo>.git
     git push origin main
     ```

> ⚠️ **Do NOT ask for Docker registry credentials or AWS S3 credentials.**
> Nova Platform handles the Docker build and image registry internally (Git-based build pipeline).
> S3 storage credentials are managed by the Enclaver/Odyn layer — the app only calls Internal API endpoints (`/v1/s3/*`). Users never touch AWS credentials.

## Full Workflow

### Step 1 — Scaffold the project

```bash
python3 scripts/scaffold.py \
  --name <app-name> \
  --desc "<one-line description>" \
  --port <port> \
  --out <output-dir>
```

> **Port choice**: Any port works. Set it via `advanced.app_listening_port` when creating the app. Must also match `EXPOSE` in your Dockerfile. No requirement to use 8080.

This generates `<output-dir>/<app-name>/` with the following structure:
```
<app-name>/
├── Dockerfile
└── enclave/
    ├── main.py
    ├── odyn.py
    └── requirements.txt
```

> **Note**: The template ships as `Dockerfile.txt` (clawhub cannot distribute extensionless files). `scaffold.py` automatically renames it to `Dockerfile` and substitutes the port. No manual action needed.

Alternatively, fork [nova-app-template](https://github.com/sparsity-xyz/nova-app-template) — a production-ready starter with KMS, S3, App Wallet, E2E encryption, and a built-in React frontend.

### Step 2 — Write the app logic

Edit `enclave/main.py`. Key patterns:

**Minimal FastAPI app:**
```python
import os, httpx
from fastapi import FastAPI

app = FastAPI()
IN_ENCLAVE = os.getenv("IN_ENCLAVE", "false").lower() == "true"
ODYN_BASE = "http://127.0.0.1:18000" if IN_ENCLAVE else "http://odyn.sparsity.cloud:18000"

@app.get("/api/hello")
def hello():
    r = httpx.get(f"{ODYN_BASE}/v1/eth/address", timeout=10)
    return {"message": "Hello from TEE!", "enclave": r.json()["address"]}
```

**With App Wallet + KMS (from nova-app-template):**
```python
from kms_client import NovaKmsClient

kms = NovaKmsClient(endpoint=ODYN_BASE)

@app.get("/api/wallet")
def wallet():
    return kms.app_wallet_address()     # {"address": "0x...", "app_id": 0}

@app.post("/api/sign")
def sign(body: dict):
    return kms.app_wallet_sign(body["message"])   # {"signature": "0x..."}
```

**Dual-chain topology** (as in the template):
- **Auth/Registry chain**: Base Sepolia (84532) — KMS authorization & app registry
- **Business chain**: Ethereum Mainnet (1) — your business logic

Helios light-client RPC runs locally at `http://127.0.0.1:18545` (Base Sepolia) and `http://127.0.0.1:18546` (Mainnet).

> ⚠️ **Helios RPC ports must be decided before creating the app** — they are set in `advanced.helios_chains[].local_rpc_port` and locked at creation time. Choose values carefully up front.

Rules for enclave code:
- All outbound HTTP must use `httpx` (proxy-aware). Never use `requests` or `urllib` — they may bypass the egress proxy.
- Persistent state → use `/v1/s3/*` endpoints; the enclave filesystem is ephemeral.
- Secrets → derive via KMS (`/v1/kms/derive`); never from env vars or hardcoded.
- Test locally: `IN_ENCLAVE=false uvicorn main:app --port <port>` (Odyn calls hit the public mock).

### Step 3 — Commit & push to Git

Your repo only needs `Dockerfile` + app code. No local Docker build needed — Nova Platform builds from your Git repo directly.

KMS integration is fully handled by the platform — just set `enable_decentralized_kms: true` (and optionally `enable_app_wallet: true`) in `advanced` when creating the app. No contract addresses, app IDs, or manual KMS config needed in your code.

```bash
git add .
git commit -m "Initial Nova app"
git push origin main
```

### Step 4 — Deploy to Nova Platform

The deployment is a **3-step** process: **Create App → Trigger Build → Create Deployment**.

#### Via Portal (recommended for first-time)

1. Go to [sparsity.cloud](https://sparsity.cloud) → **Apps** → **Create App**
2. Fill in **Name**, **Description**, **Git Repo URL**, configure Advanced settings → Submit → copy the **App sqid**
3. In the App page → **Builds** → **Trigger Build**:
   - Git Ref: `main` (or tag / commit SHA)
   - Version: e.g. `v1.0.0`
4. Wait for build status → `success` (Nova builds Docker image → EIF → generates PCRs)
5. **Deployments** → **Create Deployment** → select the successful build → **Deploy**
6. Poll until state → `running` → copy the **App URL** (hostname from app detail)

#### Via API (scripted)

The script will ask interactively whether to run on-chain registration after the app is live. Use `--onchain` to always run it, `--no-onchain` to always skip.

```bash
# Preview config without deploying
python3 scripts/nova_deploy.py \
  --repo https://github.com/you/my-app \
  --name "my-app" \
  --port 8080 \
  --api-key <your-nova-api-key> \
  --dry-run

# Deploy (will prompt for on-chain at the end)
python3 scripts/nova_deploy.py \
  --repo https://github.com/you/my-app \
  --name "my-app" \
  --port 8080 \
  --api-key <your-nova-api-key>

# Deploy + on-chain registration in one shot
python3 scripts/nova_deploy.py \
  --repo https://github.com/you/my-app \
  --name "my-app" \
  --port 8080 \
  --api-key <your-nova-api-key> \
  --onchain
```

Or via raw API:

```bash
BASE="https://sparsity.cloud/api"
TOKEN="<your-api-key>"
REPO="https://github.com/you/my-app"

# 1. Create app — 'advanced' is the only config field needed
SQID=$(curl -sX POST "$BASE/apps" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name":"my-app",
    "repo_url":"'"$REPO"'",
    "advanced":{
      "directory":"/",
      "app_listening_port":8080,
      "egress_allow":["**"],
      "enable_decentralized_kms":false,
      "enable_persistent_storage":false,
      "enable_s3_storage":false,
      "enable_s3_kms_encryption":false,
      "enable_ipfs_storage":false,
      "enable_walrus_storage":false,
      "enable_app_wallet":false,
      "enable_helios_rpc":false,
      "helios_chains":[
        {"chain_id":"1","kind":"ethereum","network":"mainnet","execution_rpc":"","local_rpc_port":18545}
      ]
    }
  }' \
  | python3 -c "import sys,json; print(json.load(sys.stdin)['sqid'])")
echo "App sqid: $SQID"

# 2. Trigger build
BUILD_ID=$(curl -sX POST "$BASE/apps/$SQID/builds" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"git_ref":"main","version":"1.0.0"}' \
  | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")
echo "Build ID: $BUILD_ID"

# 3. Poll build
while true; do
  STATUS=$(curl -s "$BASE/builds/$BUILD_ID/status" \
    -H "Authorization: Bearer $TOKEN" \
    | python3 -c "import sys,json; print(json.load(sys.stdin).get('status',''))")
  echo "Build: $STATUS"
  [ "$STATUS" = "success" ] && break
  [ "$STATUS" = "failed" ] && echo "Build failed!" && exit 1
  sleep 15
done

# 4. Create deployment
DEPLOY_ID=$(curl -sX POST "$BASE/apps/$SQID/deployments" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"build_id\":$BUILD_ID}" \
  | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")
echo "Deployment ID: $DEPLOY_ID"

# 5. Poll deployment
while true; do
  RESP=$(curl -s "$BASE/deployments/$DEPLOY_ID/status" -H "Authorization: Bearer $TOKEN")
  STATE=$(echo $RESP | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('deployment_state',''))")
  echo "Deployment state: $STATE"
  [ "$STATE" = "running" ] && break
  [ "$STATE" = "failed" ] && echo "Deploy failed!" && exit 1
  sleep 15
done

# 6. Get app URL (hostname from detail, or use unified status)
curl -s "$BASE/apps/$SQID/detail" -H "Authorization: Bearer $TOKEN" \
  | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['app'].get('hostname',''))"

# Tip: GET /api/apps/{sqid}/status gives the full lifecycle in one call:
# build_status, deployment_state, proof_status, onchain_instance_id, etc.
```

### Step 5 — Verify the live app

```bash
# Health check
curl https://<hostname>/

# Hello endpoint (returns enclave address + signature)
curl https://<hostname>/api/hello

# Attestation (proves it's a real Nitro Enclave) — POST, returns binary CBOR
curl -sX POST https://<hostname>/.well-known/attestation --output attestation.bin
```

> **Note**: `/api/app-wallet` is only available if you forked [nova-app-template](https://github.com/sparsity-xyz/nova-app-template). The scaffold template includes `/api/hello` instead.

### Step 6 — On-Chain Registration (optional but important for trust)

After the app is running, register it on-chain to establish verifiable trust.

**Using the script** (recommended):
```bash
python3 scripts/nova_deploy.py ... --onchain
```
The script will automatically execute steps 6a–6d and poll until complete.

**Manually via API** — 4-step sequence:

```bash
# 6a. Create app on-chain
curl -sX POST "$BASE/apps/$SQID/create-onchain" \
  -H "Authorization: Bearer $TOKEN"
# Poll: GET /api/apps/{sqid}/status → onchain_app_id is set when done

# 6b. Enroll build version on-chain (PCR measurements → trusted code fingerprint)
curl -sX POST "$BASE/apps/$SQID/builds/$BUILD_ID/enroll" \
  -H "Authorization: Bearer $TOKEN"

# Poll enrollment via build status endpoint
while true; do
  ENROLLED=$(curl -s "$BASE/builds/$BUILD_ID/status" \
    -H "Authorization: Bearer $TOKEN" \
    | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('is_enrolled',''))")
  echo "Enrolled: $ENROLLED"
  [ "$ENROLLED" = "True" ] && break
  sleep 10
done

# 6c. Generate ZK proof
curl -sX POST "$BASE/zkproof/generate" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"deployment_id\": $DEPLOY_ID}"

# Poll proof via deployment status endpoint (or /zkproof/status/{deployment_id})
while true; do
  PROOF=$(curl -s "$BASE/deployments/$DEPLOY_ID/status" \
    -H "Authorization: Bearer $TOKEN" \
    | python3 -c "import sys,json; print(json.load(sys.stdin).get('proof_status',''))")
  echo "Proof status: $PROOF"
  [ "$PROOF" = "proved" ] && break
  [ "$PROOF" = "failed" ] && echo "Proof failed!" && exit 1
  sleep 30
done

# 6d. Register instance on-chain
curl -sX POST "$BASE/zkproof/onchain/register" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"deployment_id\": $DEPLOY_ID}"

# Poll: GET /api/deployments/{id}/status → onchain_instance_id is set when done
# Or use unified: GET /api/apps/{sqid}/status → latest_onchain_instance_id
```

**What each step does:**
- **Create app on-chain**: Registers your app in the Nova App Registry contract (Base Sepolia)
- **Enroll version**: Records the EIF's PCR measurements on-chain — the trusted code fingerprint
- **Generate ZK proof**: Platform generates a zero-knowledge proof from the enclave's attestation
- **Register instance**: Verifies the ZK proof on-chain and links the live instance to its enrolled version

After registration, anyone can verify on-chain that this running instance matches the audited build.

## Key Notes

1. **`advanced` is REQUIRED at app creation.** Omitting it will cause the build to fail. Only `advanced` exists — the old `enclaver` field has been removed from the API.
2. **Your repo only needs `Dockerfile` + app code.** The platform handles everything else at build time.
3. **App ID format**: `sqid` (string like `abc123`) — use in all URL paths, not the integer `id`.
4. **Port**: Set via `advanced.app_listening_port`. Must also match `EXPOSE` in Dockerfile.
5. **Helios RPC**: Use the canonical port mapping in `references/nova-api.md` — ports are fixed and locked at app creation.
6. **KMS dependency chain**: `enable_app_wallet` or `enable_s3_kms_encryption` implies KMS → implies Helios (with base-sepolia chain) → implies `kms_app_id` + `nova_app_registry`.
7. **KMS is handled by Enclaver** — no contract addresses or config.py changes needed in your app code.
8. **No Docker push**: Platform builds from Git.
9. **On-chain steps** (create-onchain → enroll → ZK proof → register) are required for public verifiability, but optional for a functional running app.

## Common Issues

| Symptom | Fix |
|---|---|
| `Dockerfile` missing after scaffold | Run `clawhub update nova-app-builder` then re-scaffold. The template ships as `Dockerfile.txt` and scaffold renames it to `Dockerfile` automatically. |
| scaffold fails / `enclaver.yaml` or `config.py` expected | You have an old version. Run `clawhub update nova-app-builder`. These files are no longer needed — `advanced` field at app creation replaces all manual config. |
| API endpoint `console.sparsity.cloud` not resolving | Old version of `nova_deploy.py`. Run `clawhub update nova-app-builder`. Correct endpoint is `https://sparsity.cloud/api`. |
| Build stuck in `pending` | Check GitHub Actions in nova build repo; may be queued |
| Build `failed` | Check `error_message` in build response; usually Dockerfile issue |
| Deploy API returns 401 | Regenerate API key at sparsity.cloud |
| App stuck in `provisioning` >10 min | Check app logs via `GET /api/apps/{sqid}/detail` |
| `httpx` request fails inside enclave | Add domain to `advanced.egress_allow`. Note: `"**"` matches domains only — add `"0.0.0.0/0"` for direct IP connections |
| Direct IP connection blocked | `"**"` does NOT cover IPs. Add `"0.0.0.0/0"` (IPv4) and/or `"::/0"` (IPv6) to `egress_allow` |
| S3 fails | Ensure `169.254.169.254` and S3 endpoint are in egress allow list |
| `/v1/kms/*` returns 400 | Ensure `enable_decentralized_kms: true` and `enable_helios_rpc: true` in `advanced` at app creation |
| App Wallet unavailable | Ensure `enable_app_wallet: true` in `advanced` at app creation |
| Proxy not respected | Switch from `requests`/`urllib` to `httpx` |
| Health check returns 502 | App is starting; wait for enclave to fully boot |
| ZK proof stuck | Check `GET /api/zkproof/status/{deployment_id}` for details |

## Reference Files

- **`references/odyn-api.md`** — Full Odyn Internal API (signing, encryption, S3, KMS, App Wallet, attestation)
- **`references/nova-api.md`** — Nova Platform REST API (full endpoint reference)

## Key URLs

- Nova Platform: https://sparsity.cloud
- **Nova Platform API docs**: https://sparsity.cloud/api/docs
- **Create-App Full Parameter Guide**: https://sparsity.cloud/resources/nova-api/create-app-guide
- Nova Examples: https://github.com/sparsity-xyz/sparsity-nova-examples/
- Enclaver (Sparsity): https://github.com/sparsity-xyz/enclaver
- Nova App Template: https://github.com/sparsity-xyz/nova-app-template
- Enclaver Docs: [odyn.md](https://github.com/sparsity-xyz/enclaver/blob/sparsity/docs/odyn.md), [internal_api.md](https://github.com/sparsity-xyz/enclaver/blob/sparsity/docs/internal_api.md)

Files in this skill

  • SKILL.md17 KB
  • assets/app-template/Dockerfile.txt243 B
  • assets/app-template/enclave/main.py3.1 KB
  • assets/app-template/enclave/odyn.py4.9 KB
  • assets/app-template/enclave/requirements.txt283 B
  • references/app-patterns.md8.7 KB
  • references/nova-api.md12.9 KB
  • references/odyn-api.md7.6 KB
  • scripts/build_push.py3 KB
  • scripts/deploy.py14.1 KB
  • scripts/nova_deploy.py17.5 KB
  • scripts/scaffold.py2.5 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…