Reference

Capsule API

HTTP API endpoints available to your application at 127.0.0.1:18000 inside the enclave.

The Capsule API consists of two services: the Primary API (full /v1/* surface) and the Aux API (restricted attestation proxy). Both bind to 127.0.0.1 only, with a 10 MiB request body limit.


Endpoint Overview

Ethereum
GET /v1/eth/address Enclave Ethereum address and secp256k1 public key
POST /v1/eth/sign Sign a UTF-8 string with personal-sign prefix
POST /v1/eth/sign-tx Sign an EIP-1559 transaction (structured or raw RLP)
Random & Attestation
GET /v1/random 32 NSM-backed cryptographically secure random bytes
POST /v1/attestation NSM attestation document (CBOR), optional custom user_data
Encryption (P-384 ECDH + AES-256-GCM)
GET /v1/encryption/public_key Enclave P-384 public key (DER and PEM formats)
POST /v1/encryption/decrypt Decrypt client data (rejects nonce reuse)
POST /v1/encryption/encrypt Encrypt response data back to the client
S3 Storage (requires storage.s3.enabled: true)
POST /v1/s3/get Retrieve an object from the configured S3 bucket
POST /v1/s3/put Store an object in the configured S3 bucket
POST /v1/s3/delete Delete an object from the configured S3 bucket
POST /v1/s3/list List objects with pagination support
Nova KMS (requires kms_integration.enabled: true)
POST /v1/kms/derive Derive key material (app/session/alpha paths)
POST /v1/kms/kv/get Get value from KMS key-value storage
POST /v1/kms/kv/put Put value into KMS key-value storage
POST /v1/kms/kv/delete Delete value from KMS key-value storage
App Wallet (requires kms_integration.use_app_wallet: true)
GET /v1/app-wallet/address Wallet address and app ID
POST /v1/app-wallet/sign Sign with the app wallet key
POST /v1/app-wallet/sign-tx Sign a transaction with the app wallet key

Ethereum APIs

GET /v1/eth/address

Returns the enclave's Ethereum address and secp256k1 public key. The key is deterministic per enclave instance lifetime.

Request
GET http://127.0.0.1:18000/v1/eth/address
Response
{
  "address": "0x1234...abcd",
  "public_key": "0x04..."
}

POST /v1/eth/sign

Signs a UTF-8 string using the Ethereum personal-sign prefix.

Request
POST http://127.0.0.1:18000/v1/eth/sign
Content-Type: application/json

{
  "message": "Hello, World!"
}
Response
{
  "signature": "0x..."
}

POST /v1/eth/sign-tx

Signs an EIP-1559 transaction. Accepts structured fields or raw RLP.

Request — structured fields
POST http://127.0.0.1:18000/v1/eth/sign-tx
Content-Type: application/json

{
  "chain_id": 84532,
  "nonce": 0,
  "to": "0xRecipientAddress",
  "value": "0x0",
  "data": "0x",
  "max_fee_per_gas": "0x59682F00",
  "max_priority_fee_per_gas": "0x3B9ACA00",
  "gas_limit": 21000
}

Encryption APIs

End-to-end encryption between client and enclave using P-384 ECDH + HKDF-SHA256 + AES-256-GCM.

Cryptographic Protocol

Key agreement: P-384 ECDH  |  KDF: HKDF-SHA256  |  Cipher: AES-256-GCM  |  Nonce: 12 bytes

HKDF info string: "capsule-ecdh-aes256gcm-v1"

Salt derivation: sort lexicographically(client_pub_sec1, enclave_pub_sec1) || nonce

GET /v1/encryption/public_key

Response
{
  "public_key_der": "0x3076...",
  "public_key_pem": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----"
}

POST /v1/encryption/decrypt

Request
{
  "nonce": "0x...",
  "client_public_key": "0x...",
  "encrypted_data": "0x..."
}
Response
{
  "plaintext": "decrypted string"
}
Hex values may include or omit the 0x prefix. client_public_key is DER/SPKI format. Plaintext must be valid UTF-8. Duplicate (client_public_key, nonce) pairs are rejected.

POST /v1/encryption/encrypt

Request
{
  "plaintext": "string to encrypt",
  "client_public_key": "0x..."
}
Response
{
  "encrypted_data": "a1b2c3...",
  "enclave_public_key": "3076...",
  "nonce": "d4e5f6..."
}
Response values use raw hex without the 0x prefix.

Client Integration Flow

01

Get enclave public key

GET /v1/encryption/public_key or extract from attestation document.

02

Generate client P-384 keypair

Create an ephemeral P-384 key pair on the client side.

03

Derive shared secret

ECDH(client_private, enclave_public) → HKDF-SHA256 with sorted public keys and nonce as salt.

04

Encrypt and send

Generate a fresh 12-byte nonce per message. Encrypt with AES-256-GCM. Send ciphertext, nonce, and client DER public key to /v1/encryption/decrypt.


S3 Storage APIs

Requires storage.s3.enabled: true in capsule.yaml. Egress access to IMDS (169.254.169.254) and S3 endpoints is required.

POST /v1/s3/put

{
  "key": "data/record.json",
  "body": "{\"id\": 1, \"name\": \"test\"}"
}

POST /v1/s3/get

{
  "key": "data/record.json"
}

POST /v1/s3/delete

{
  "key": "data/record.json"
}

POST /v1/s3/list

{
  "prefix": "data/",
  "max_keys": 100,
  "continuation_token": null
}

Key Path Security

All keys are prefixed with the configured storage.s3.prefix. Keys containing .. or absolute paths are rejected. When encryption.mode: kms is used, objects are automatically encrypted with KMS-derived keys.


Auxiliary API

A restricted proxy on port 18001 (default, or api_port + 1). Required for external attestation flows.

Purpose

The Aux API provides a safe attestation endpoint for external clients. It forwards POST /v1/attestation to the Primary API but sanitizes the response by removing the public_key field.

When to Expose

Add the Aux API port to your ingress list when external clients need to fetch attestation documents. Never expose the Primary API port externally.

Configuration

Set aux_api.listen_port in capsule.yaml. Defaults to the primary API port + 1 if omitted.


Mock Service for Development

Develop locally without running a real enclave.

External Mock Endpoint

Available at http://capsule-runtime.sparsity.cloud:18000/ — hosted by the Nova Platform team. Not version-locked to your Capsule Runtime. Use for lightweight development only.

Usage pattern
# In your application:
# Set CAPSULE_API_MOCK_URL environment variable to point to the mock
# When IN_ENCLAVE is not set, use CAPSULE_API_MOCK_URL
# When IN_ENCLAVE=true, use http://127.0.0.1:18000

# Quick test:
curl http://capsule-runtime.sparsity.cloud:18000/v1/eth/address
curl http://capsule-runtime.sparsity.cloud:18000/v1/random

Mock Limitations

Not all endpoints may be available. Response formats may differ from the real API. Error codes and edge-case behavior may not match. Always verify against a real enclave before production.