A deployment is one process per source: set SOURCE_ID and the process serves
every attestation type that source offers. The following are common variables
needed for all sources:
PORT=<port_number>
API_KEYS=<comma_separated_strings>
DESTINATION_CHAIN_URL_SLUG=<lowercase_slug> # e.g. flare, sgb, coston, coston2 — third URL segment of every routeNOTE: The
<port_number>value must be consistent with thePORTenvironment variable throughout the configuration.
Environment variables:
SOURCE_ID=TEE
RELAY_CONTRACT_ADDRESS=0x...
FLARE_RPC_URL=https://<flare>
CHAIN_ID=<evm_chain_id>
# Relay cutover (optional, set BOTH or neither) — deploy ahead of a known
# Relay redeployment. Signing-policy ids at or above the starting reward epoch
# route to the next Relay; lower ids stay on RELAY_CONTRACT_ADDRESS, which
# keeps serving history. Each lookup routes independently (one TEE response may
# span the boundary), so the switch needs no redeploy or restart. A reward
# epoch and a signing-policy id are the same identifier: this value must equal
# the [relay_cutover] starting_reward_epoch configured in the other Flare
# clients (tee-relay-client, FDC, FSP) — a mismatch fails every proof on one
# side of the boundary.
RELAY_CUTOVER_CONTRACT_ADDRESS=0x...
RELAY_CUTOVER_STARTING_REWARD_EPOCH=<first reward epoch the next Relay serves>
# Test/E2E-only flags (optional, default to false):
ALLOW_TEE_DEBUG=false
DISABLE_ATTESTATION_CHECK_E2E=false
ALLOW_PRIVATE_NETWORKS=falseNOTE:
ALLOW_TEE_DEBUG,DISABLE_ATTESTATION_CHECK_E2E, andALLOW_PRIVATE_NETWORKSare test/E2E-only flags. In production, you should leave them unset (they default to false).ALLOW_TEE_DEBUG=trueadditionally accepts debug-mode TEEs alongside production TEEs (every debug admission logs a WARN); debug TEEs have the debugger attached and secrets are extractable, so this must never be set on production deployments.ALLOW_PRIVATE_NETWORKSpermits private/loopback IPs (e.g. Docker bridge172.17.0.1) while still blocking dangerous IPs (link-local/metadata, multicast, Teredo, 6to4, IPv4-compatible IPv6) and preserving DNS pinning.
WARNING: MagicPass bypass — TEE nodes running in non-production mode (
settings.Mode != 0) return"magic_pass"instead of a real attestation token. The verifier unconditionally accepts this token and skips ALL attestation validation (PKI, claims, CRL). This exists to support hackathon and development environments where real Google Confidential Space attestation is unavailable. Do NOT rely on this in production — any TEE returning this string will be trusted without verification.
NOTE (Relay cutover): the next Relay must be deployed and initialized before the starting reward epoch arrives — the verifier deliberately does not probe it at startup, so data providers can deploy this configuration early. Lookups fail closed on their own Relay (no fallback between the contracts). Keep the old Relay configured until no valid TEE references an initial signing policy stored only there (re-attestation refreshes
LastSigningPolicyID, not necessarilyInitialSigningPolicyID) or the next Relay carries the complete historical mapping; only then collapse to a singleRELAY_CONTRACT_ADDRESS. Note the semantics differ from tee-relay-client's[relay_cutover]: there, an absent block means the switch already happened, while here an absent pair means no cutover — do not assume symmetry.
The TeeAvailabilityCheck attestation type also uses Google Confidential Space Root Certificate, which is stored locally in the folder internal/config/assets. Read more about it here.
PMWPaymentStatus and PMWFeeProof require the following indexers:
Environment variables (the union of what the source's types need):
SOURCE_ID=XRP # or testXRP
SOURCE_RPC_URL=https://<xrpl> # PMWMultisigAccountConfigured
FLARE_RPC_URL=https://<flare-c-chain>/ext/C/rpc # PMWPaymentStatus / PMWFeeProof (getInitialNonce)
CCHAIN_DATABASE_URL=user:pass@tcp(host:port)/db?parseTime=true
SOURCE_DATABASE_URL=postgres://user:pass@host:port/db
FLARE_TEE_MANAGER_CONTRACT_ADDRESS=0x...
TEE_PAYMENTS_CONTRACT_ADDRESS=0x...NOTE: Two RPCs are required because the source's types talk to different chains:
SOURCE_RPC_URLis the source-chain (XRPL) endpoint used byPMWMultisigAccountConfigured, whileFLARE_RPC_URLis the Flare C-chain (EVM) RPC used byPMWPaymentStatus/PMWFeeProoffor the read-onlygetInitialNoncecall.
NOTE:
FLARE_TEE_MANAGER_CONTRACT_ADDRESSis the on-chain contract that emitsTeeInstructionsSentevents. The verifier rejects indexed logs emitted by any other address.
NOTE:
TEE_PAYMENTS_CONTRACT_ADDRESSis the source'sTeePaymentscontract (deployed per source) — the verifier callsgetInitialNonceon it for the deterministic paymentId→sequence binding. It is not the same asFLARE_TEE_MANAGER_CONTRACT_ADDRESS(the diamond that emits the events).
-
Fill in the
.envfile (for local development) or set environment variables directly (for production). To load the.envfile at startup setLOAD_DOTENV=truein your shell before running the binary —.envloading is opt-in so production deployments are not sensitive to filesystem contents. -
Install dependencies:
go mod tidy
-
Run the project:
go run ./cmd/main.go
For local development with a
.envfile, setLOAD_DOTENV=trueso the binary loads it at startup:LOAD_DOTENV=true go run ./cmd/main.go
In production, leave
LOAD_DOTENVunset and inject environment variables via the container runtime. -
Access Swagger UI:
http://localhost:<port_number>/verifier/<source>/<destinationChain>/api-doc/Replace
<port_number>with the value set in yourPORTenvironment variable.
Base path for all verifier endpoints:
/verifier/<sourceName>/<destinationChain>/<attestationType>/
<sourceName>must be lowercase.<destinationChain>is the deployment'sDESTINATION_CHAIN_URL_SLUG(conventionallyflare,sgb,coston, orcoston2).<attestationType>is the type of attestation (e.g., TeeAvailabilityCheck, PMWPaymentStatus, PMWMultisigAccountConfigured).
See API reference for endpoint definitions and examples.
An earlier version of TeeAvailabilityCheck ran a background poller that pinged active TEEs and maintained in-memory liveness samples. It was removed in favor of live-only verification. The last commit containing the full poller implementation is 70d8c33; check that commit out to inspect or restore the code.
The process of submitting an attestation requests is as follows:
Attestation requests are triggered via TEE smart contracts. The TEE relay client, which acts as a connector between contracts on Flare's C-chain and TEE clients, listens to TeeInstructionsSent events with an instructionId that correspond to an attestation request (FDC2_OP_TYPE ("F_FDC2") and PROVE ("PROVE")). Each attestation request is then placed into a queue and gradually promoted to the designated verifier server. It is advised that each TEE relay client runs its own verifier server.
The blockchain itself limits how many attestation requests can be emitted per block, while the queue system enforces a controlled consumption rate for verifier servers. It is also expected that the person deploying the verifier server implements additional rate limiting at other levels.
For internal-only APIs, we use a minimal set of headers:
- FrameDeny – prevent clickjacking
- ContentTypeNosniff – prevent MIME sniffing
Other headers (CORS, SSL redirect, STS, cross-origin policies) are not needed because these services are only accessed internally by trusted services, not browsers or public clients.
Minimal headers keep internal communication safe without unnecessary overhead.
- Running all tests with coverage
sh gencover.shThe script is located in gencover.sh.
- Docker services defined in internal/tests/docker/docker-compose.yaml will automatically start.
- All tests (unit + integration) will run.
- Docker services will automatically shut down after all tests complete. This is the simplest way to run everything without worrying about Docker manually.
- Running specific tests manually
- The majority of tests are self-contained:
- Do not require Docker and can be run directly:
go test -v <path_to_test>
- Do not require Docker and can be run directly:
- A few tests (PMWPaymentStatus / PMWFeeProof) access the indexer databases and are Docker-dependent. They are gated behind the
integrationbuild tag, so a barego test ./...skips them and stays green without Docker.- Start Docker manually:
docker compose -f internal/tests/docker/docker-compose.yaml up -d
- Run the integration tests (note
-tags integration):go test -tags integration -v <path_to_test>
- Stop Docker after finishing:
docker compose -f internal/tests/docker/docker-compose.yaml down
- Start Docker manually:
-
Running fuzz tests
Fuzz tests run their seed corpus as regular tests during
go testandgencover.sh. To run actual fuzzing with random inputs:go test ./internal/attestation/teeavailabilitycheck/verifier/ -fuzz FuzzResolveExternalURL -fuzztime 60sAvailable fuzz targets:
FuzzResolveExternalURL,FuzzGetOrFetchCRL,FuzzFetchCRLsForToken,FuzzFetchTEEChallengeResult. -
Running benchmarks
Benchmark tests measure PMWFeeProof performance scaling with real Postgres + MySQL. They require Docker and are gated behind the
docker_benchbuild tag:docker compose -f internal/tests/docker/docker-compose.yaml up -d # Sequential benchmark (single client, varying batch ranges): go test -tags docker_bench -run TestBenchmarkFeeProofPostgres -v ./internal/attestation/pmwfeeproof/xrp/ # Concurrent benchmark (multiple clients, varying batch ranges): go test -tags docker_bench -run TestBenchmarkFeeProofConcurrent -v ./internal/attestation/pmwfeeproof/xrp/ docker compose -f internal/tests/docker/docker-compose.yaml down
-
Running load tests
Load tests are gated behind the
loadbuild tag and don't run during normalgo testorgencover.sh:go test -tags load -run TestLoad -v ./internal/attestation/teeavailabilitycheck/verifier/ ./internal/attestation/pmwmultisigconfigured/xrp/ ./internal/attestation/pmwpaymentstatus/db/ ./internal/attestation/pmwpaymentstatus/xrp/ ./internal/attestation/pmwfeeproof/db/ ./internal/attestation/pmwfeeproof/xrp/ -
Running stress tests
Stress tests are gated behind the
stressbuild tag. They take longer (~70s) and push beyond normal load:go test -tags stress -run TestStress -v ./internal/attestation/teeavailabilitycheck/verifier/For detailed results, findings, and test parameters, see docs/load-and-stress-tests.md.
- Other
TODOs inside the code and README. - TEEAvailabilityCheck currently supports only "google". When support for other platforms is added, TeeInfo.Platform needs to be added in order to know, how to decode the data.
- PMWFeeProof: Confirm with FAsset team that the
estimatedFeeformula (pay_maxFee + sum(max(0, reissue_maxFee - pay_maxFee))) is suitable for their fee reconciliation use case. go.modpinsgithub.com/jackc/pgx/v5 v5.9.1as an explicit indirect override becausegorm.io/driver/postgres v1.6.0pulls the unpatched v5.6.0 (CVE-2026-33815, CVE-2026-33816). Drop the explicit pgx require once a newergorm.io/driver/postgresships that pulls pgx >= v5.9.0.
See docs/SPEC.md for the full technical specification covering architecture, verification flows, error model, and configuration.