An example TEE extension that stores a private key and signs messages with it. Use this as a hackathon starter template: clone it, modify the code to create your own extension, then deploy/register/test it on Coston2.
Warning: This repo is for demonstration purposes only. Storing encrypted secrets on-chain is not advisable in production — on-chain data is public and encryption can be broken over time. A production extension should use off-chain channels for secret delivery.
Pick the language you're most comfortable with and work inside its directory.
You should modify the files in app/ and the shared
contract/InstructionSender.sol. The files in base/ are framework
infrastructure -- you should not need to modify them.
| Language | Directory | Test command |
|---|---|---|
| Go | go/ |
cd go && go test ./... |
| Python | python/ |
cd python && python3 -m unittest discover -s tests -p 'test_*.py' |
| TypeScript | typescript/ |
cd typescript && npm ci && npm test |
See each directory's README.md for details on the handler signature, what's
provided by base/, and what files to change.
Install the agent skills for guided extension development:
npx skills add .contract/InstructionSender.sol is shared across all implementations. Update it
to match your extension's OPType/OPCommand constants.
Run the sign extension locally, expose it to the internet via a tunnel (cloudflared, ngrok, etc.), and register + test it on the Coston2 testnet (chain ID 114).
All deployment, registration, and testing tools are in go/tools/ and work
for all extension languages. The scripts interact with smart contracts and
the TEE proxy — they don't depend on the extension's implementation language.
Set LANGUAGE in .env to choose which Docker image to build.
See go/README.md for tool details.
- Docker
- cloudflared to expose a local port to the internet (no account required; ngrok also works but needs sign-up)
- Foundry (
forge,cast) for contract compilation and verification - A funded Coston2 wallet (needs C2FLR for gas + TEE registration fees)
- Go >= 1.23 (for the deployment/registration tools in
go/tools/)
cp .env.example .env
# Edit .env and fill in PRIVATE_KEY, INITIAL_OWNER, etc.
# Set LANGUAGE=go, LANGUAGE=python, or LANGUAGE=typescript
# to choose which implementation the Docker stack runs.
cp config/proxy/extension_proxy.toml.example config/proxy/extension_proxy.toml
# Edit config/proxy/extension_proxy.toml and fill in the DB credentials
# for the Coston2 C-chain indexer (username and password in the [db] section).If you prefer to run your own local indexer, see config/indexer/ and swap the
[db] section in the proxy config (a commented example is in the file).
cd go/tools
go run ./cmd/deploy-contractThe deploy tool automatically verifies the contract source on the
Coston2 block explorer.
Pass --no-verify to skip.
Save the printed address in .env:
# Add to .env
INSTRUCTION_SENDER="<deployed-address>"cd go/tools
go run ./cmd/register-extensionSave the printed extension ID in .env:
# Add to .env
EXTENSION_ID="0x<64-hex-chars>"Build and start the stack (rebuild when switching LANGUAGE in .env):
docker compose build
docker compose up -dWait for the proxy to become healthy:
until curl -sf http://localhost:6676/info >/dev/null 2>&1; do sleep 2; done
echo "Extension proxy is ready"In a separate terminal, expose the extension proxy port (6676) to the internet:
# Using cloudflared (no account required):
cloudflared tunnel --url http://localhost:6676
# Or using ngrok:
ngrok http 6676Note the public HTTPS URL and add it to .env:
# Add to .env
TUNNEL_URL="https://<your-tunnel-url>"Note: The tunnel must stay running for the entire session. If your computer sleeps or restarts, restart the tunnel and update
TUNNEL_URLin.envwith the new URL.
cd go/tools
go run ./cmd/allow-tee-version -p http://localhost:6676Make sure TUNNEL_URL is set correctly in .env.
cd go/tools
go run ./cmd/register-tee -p http://localhost:6676 -lThe -l flag enables local/test mode (required when the TEE returns a test
attestation token instead of a real GCP JWT).
The -p flag specifies an existing production TEE on extension 0 that
performs the FTDC availability check on your TEE. It defaults to
https://tee-proxy-coston2-1.flare.rocks (the Coston2 public TEE proxy).
Make sure INSTRUCTION_SENDER and TUNNEL_URL are set correctly in .env.
cd go/tools
go run ./cmd/run-test -p http://localhost:6676The test will:
- Call
setExtensionId()on the InstructionSender - Fetch the TEE's public key from the proxy
- ECIES-encrypt a test private key and send
updateKeyon-chain - Wait for the TEE to process the instruction
- Send a
signinstruction on-chain - Verify the returned signature matches the test private key
| Service | Container port | Host port |
|---|---|---|
| ext-proxy internal | 6663 | 6675 |
| ext-proxy external | 6664 | 6676 |
| redis | 6379 | 6383 |
The tunnel exposes host port 6676 (ext-proxy external) to the internet.
The proxy needs a synced C-chain indexer DB. Check the proxy logs and verify
the DB credentials in config/proxy/extension_proxy.toml:
docker compose logs ext-proxyEnsure your wallet has enough C2FLR for gas + fees. The TEE fee calculator determines the required fee for each operation.
Try restarting the proxy — it may have missed a signing policy round:
docker compose restart ext-proxyIf that doesn't help, the FDC attestation flow requires active relay providers on Coston2. If no relay infrastructure is running, the availability check won't complete.
If your tunnel restarts and the URL changes, update TUNNEL_URL in .env
and restart the Docker stack (docker compose down && docker compose up -d),
then re-run steps 5-6 (allow-tee-version + register-tee) to register a new
TEE machine with the new URL.
To shut down all local services and prepare for a fresh start:
docker compose downThis stops and removes all containers (redis, ext-proxy, extension-tee).
If you want to completely reset and follow the README from the beginning:
# Remove built images (forces rebuild)
docker compose down --rmi local
# Clear environment state
rm -f .env config/proxy/extension_proxy.tomlAfter a full reset, start again from Step 0.
Note: On-chain state (deployed contracts, registered extensions, registered TEEs) cannot be reset. Each fresh start will deploy a new InstructionSender contract and register a new extension. This is fine for testing — Coston2 is a testnet.