TEE relay client is a connector between smart contracts on Flare's C-chain and TEE clients.
It listens to TeeInstructionsSent events emitted by the FlareTeeManager smart contract, processes them, and forwards them to the TEE nodes.
Build and run the relay client:
go build -o tee-relay ./cmd/main
./tee-relayThe binary expects config.toml to be present in the working directory.
TODO
The configuration is read from the config.toml file.
Copy the example to get started:
cp config.toml.example config.tomlTEE relay client can be run in provider or cosigner mode, controlled by the is_cosigner field.
For Flare entities included in the current signing policy. The relay client processes all instructions.
is_cosigner = false # defaultFor cosigners defined by TEE protocols that are not in the signing policy. The relay client processes only instructions that include the address corresponding to the configured private key among the cosigners.
is_cosigner = trueAddress of the FlareTeeManager smart contract to listen to:
flare_tee_manager = "0xdE25c06982Ab8e4b6B4F910896E3f93Ac77FB44d"Credentials for the C-chain indexer database:
[db]
host = "localhost"
port = 3306
database = "flare_ftso_indexer"
username = "root"
password = "root"
log_queries = falseThe database should be operated by C-chain indexer connected to desired chain.
The indexer should index TeeInstructionsSent events emitted by the FlareTeeManager diamond contract.
The relay client requires access to a private key — the signing policy key for providers, or the designated cosigning key for cosigners. The key is used to:
- sign instructions
- identify and decrypt packages for key recovery
- (cosigner mode only) identify relevant instructions
Two modes are supported.
The private key is held by the relay client itself, read from an environment variable at startup.
[signer]
local = true
private_key_variable = "PRIVATE_KEY" # name of the env var; defaults to "PRIVATE_KEY"Set the environment variable to a 0x-prefixed 32-byte hex string:
export PRIVATE_KEY=0x<64 hex chars>The private key is held by an external signer service (typically the FSP client).
[signer]
local = false
url = "https://signer-host:port"
key_name = "X-API-KEY"
key = "<api-key>"The external signer URL is operator-controlled and may point to a local address.
Use https for any non-loopback host: the API key and the /decrypt plaintext
(key-split secret material) would otherwise transit in cleartext. http is
acceptable only for a loopback address.
The signer service must expose three endpoints:
POST /sign
Request:
{ "hashes": ["<0x-prefixed 32-byte hex>", ...] }Response:
{ "signatures": ["<0x-prefixed 65-byte hex>", ...] }The j-th signature is the ECDSA personal signature (ERC-191 version 0x45) of the j-th hash.
POST /decrypt
Request:
{ "cipher": "<0x-prefixed hex ciphertext>" }Response:
{ "plain": "<0x-prefixed hex plaintext>" }plain is the ECIES decryption of cipher.
GET /id
Response:
{ "x": "<0x-prefixed 32-byte hex>", "y": "<0x-prefixed 32-byte hex>" }Returns the secp256k1 public key coordinates of the key used for signing and decryption.
A reference implementation is provided in go-flare-common/pkg/tee/signer.
One of the protocols operated on Flare TEEs is FDC2 (Flare Data Connector). FDC2 instructions require additional processing — the relay client must query designated verifier servers to obtain attestation responses.
A verifier must be configured for each supported (attestation type, source) pair. To avoid overloading servers, each verifier is backed by a queue. Multiple verifiers can share a queue when they point to the same server.
[fdc.queues.exampleQueue]
max_dequeues_per_second = 100 # zero for unlimited
max_workers = 50 # zero for unlimited
max_attempts = 3
time_off = "2s"[fdc.verifiers.example]
type = "AttestationTypeExampleName"
source = "ExampleSource"
queue = "exampleQueue"
server.url = "https://verifier-host/path/to/endpoint"
server.key_name = "X-API-KEY"
server.key = "exampleKey"Verifier server URLs are operator-controlled and may point to local addresses.
Use https for any non-loopback host so the API key and request/response bodies
are not sent in cleartext; http is acceptable only for a loopback address.
[logger]
level = "INFO" # DEBUG, INFO, WARN, ERROR
console = true # write logs to stdout
file = "" # path to log file; empty disables file logging
max_file_size = 10 # max log file size in MB before rotation| Variable | Required | Description |
|---|---|---|
PRIVATE_KEY |
When signer.local = true |
Private key for local signing. Name is configurable via signer.private_key_variable. Must be a 0x-prefixed 32-byte hex string. |
ALLOW_UNSAFE_URLS |
No | Set to true to disable SSRF protection on backup and TEE sender URLs. Intended for local end-to-end testing only. A warning is logged at startup when enabled. |