gaiko2 is a lightweight Go service for replaying raiko2 Shasta execution packets
with taiko-geth and producing a TEE proof envelope.
GET /healthzreturns a minimal liveness response./prove/shastaaccepts the soundness-oriented requestschema: "raiko2-shasta-request-v1"withpayload.guest_input./prove/shastarejects replay-only v1 packets withoutpayload.guest_input. The legacy replay-packet wire shape is not a soundness-equivalent server API.gaiko2can decoderaiko2-adapted execution packets and replay them with nativetaiko-gethstateless execution.- For proposal requests,
gaiko2validatesGuestInputcarry data, raw blob hashes, Shasta source manifests, canonical transaction lists, and block metadata before replaying the witness blocks. gaiko2validates replay continuity againstproof_carry_data.- proof output now supports two signer modes behind one envelope:
native: sign the final input hash with a fixed, published native mock key. Intended for local/dev only; the/prove/shasta-aggregateendpoint is disabled in native mode unlessGAIKO2_DEV_MODE=1is set.tee: sign with an enclave-managed key; the bootstrap step emits theegoquote used by external registration flows.
- the checked-in shared fixture under
testdata/is derived from a realraiko2GuestInput fixture and replays successfully.
Run the current native test suite with:
cd gaiko2
go test ./...The main replay regression uses:
- testdata/shasta_request_taiko_mainnet_proposal_2222_l2_5412225_5412416.json
- internal/prover/replay_fixture_test.go
The GuestInput soundness checks are covered by:
- internal/prover/guestinput_carry_test.go
- internal/prover/blob_validate_test.go
- internal/prover/manifest_validate_test.go
- internal/prover/validate_test.go
Start the service with:
cd gaiko2
GAIKO2_PROVING_MODE=native go run ./cmd/gaiko2 serverProbe liveness with:
curl http://127.0.0.1:8080/healthzBoth /prove/shasta and /prove/shasta-aggregate accept exactly one JSON
request value with a maximum body size of 512 MiB. Larger bodies return HTTP
413 with the standard REQUEST_TOO_LARGE error envelope.
Proving configuration:
GAIKO2_PROVING_MODE=native|tee(required)GAIKO2_TEE_TYPE=egoGAIKO2_CONFIG_DIR=/path/to/configGAIKO2_SECRET_DIR=/path/to/secretsGAIKO2_INSTANCE_ID=0xDEADC0DEGAIKO2_FORK=shastaGAIKO2_PORT=8080GAIKO2_DEV_MODE=1(native mode only; see below)
GAIKO2_PROVING_MODE must be set explicitly. native mode preserves the
deterministic local-development proof flow, but its signing key is public. Never
register the native signer in a verifier protecting real value. Production
deployments must use tee mode with an enclave-managed key.
Native mode signs with a published mock key, so it is intended for local
development only. Because /prove/shasta-aggregate produces the final on-chain
signature without executing any blocks, it is disabled in native mode and
returns HTTP 403 with an AGGREGATE_DISABLED error envelope; set
GAIKO2_DEV_MODE=1 to re-enable it for local testing. The mock instance must
never be registered in a verifier guarding real value.
TEE mode expects the enclave key to be bootstrapped ahead of proving and also
requires either GAIKO2_INSTANCE_ID or a GAIKO2_FORK entry in
registered.gaiko2.json. gaiko2 server validates both requirements at
startup and caches the loaded key for later signing.
On SIGINT or SIGTERM, the server stops accepting new connections and gives
in-flight requests up to 30 seconds to finish. The checked-in Compose services
allow 35 seconds before forced termination so the application grace period can
complete.
Bootstrap the local tee state with:
cd gaiko2
GAIKO2_PROVING_MODE=tee GAIKO2_TEE_TYPE=ego \
GAIKO2_CONFIG_DIR=/tmp/gaiko2-config \
GAIKO2_SECRET_DIR=/tmp/gaiko2-secrets \
go run ./cmd/gaiko2 bootstrapThe bootstrap command writes:
bootstrap.gaiko2.jsonunderGAIKO2_CONFIG_DIRattestation.gaiko2.jsonunderGAIKO2_CONFIG_DIRwhenGAIKO2_ATTESTATION_PATHis available in the imagepriv.gaiko2.keyunderGAIKO2_SECRET_DIR
Re-running bootstrap without --force never rotates an existing tee key. If
the bootstrap JSON is missing, malformed, or identifies a different key, the
command rebuilds it from the existing sealed key and a fresh quote. Matching
metadata is left unchanged. If the sealed key cannot be loaded, for example
because it belongs to a different enclave identity, the error retains the
underlying cause and explains that --force can replace it.
To intentionally generate and install a replacement key, run:
GAIKO2_PROVING_MODE=tee GAIKO2_TEE_TYPE=ego \
GAIKO2_CONFIG_DIR=/tmp/gaiko2-config \
GAIKO2_SECRET_DIR=/tmp/gaiko2-secrets \
go run ./cmd/gaiko2 bootstrap --forceBefore replacement starts, the command writes a warning to stderr. Replacing the key makes the old key and any on-chain registration bound to it unusable.
For tee Docker deployments, the container entrypoint copies the embedded
attestation.json into GAIKO2_CONFIG_DIR/attestation.gaiko2.json before the
enclave bootstrap runs.
If an external registration script writes registered.gaiko2.json under
GAIKO2_CONFIG_DIR, setting GAIKO2_FORK=shasta lets gaiko2 resolve the tee
instance id from that file instead of requiring GAIKO2_INSTANCE_ID directly.
Inspect the embedded tee image metadata with:
GAIKO2_ATTESTATION_PATH=/opt/gaiko2/etc/attestation.json \
go run ./cmd/gaiko2 metadataBuild an image with:
cd gaiko2
./scripts/build-image.sh native latest
./scripts/build-image.sh tee latestBy default, tee image builds generate a disposable local enclave signing key
inside the Docker build and delete it after ego sign. That is useful for local
compile and smoke testing, but the resulting signer_id is not stable.
For release builds, fetch the MRSIGNER key from GCP Secret Manager and pass it to Docker through a BuildKit secret:
GCP_ENCLAVE_KEY_SECRET=gaiko2-enclave-key \
GCP_ENCLAVE_KEY_VERSION=latest \
GCP_ENCLAVE_KEY_PROJECT=<gcp-project> \
ENCLAVE_KEY_PUBLIC_SHA256=<expected-public-key-sha256> \
./scripts/build-image.sh tee v1.0.0ENCLAVE_KEY_PUBLIC_SHA256 is optional, but should be set for release builds so
the Docker signing step rejects an unexpected MRSIGNER key. Do not place a PEM
file under docker/; local key files are ignored by the Docker build context.
Or directly:
docker buildx build . -f docker/Dockerfile.native --load --platform linux/amd64 -t gaiko2-native:latest
DOCKER_BUILDKIT=1 docker buildx build . -f docker/Dockerfile.tee --load --platform linux/amd64 -t gaiko2-tee:latestOr use Compose profiles:
docker compose up --build
docker compose --profile tee-init run --rm gaiko2-tee-init
docker compose --profile tee up --build gaiko2-teeFor release-based SGX deployment, the operator entry point is:
./scripts/deploy-tee.sh --fork shasta --release v1.0.0 init
./scripts/deploy-tee.sh --fork shasta --release v1.0.0 upFor the full runbook, including bootstrap, external registration, rollback, and log-based troubleshooting, see:
The compose file defaults PCCS_HOST to host.docker.internal:8081 and adds the
host gateway mapping automatically, which works well when your local pccs
container already publishes 8081 on the host. If you run gaiko2 on the same
Docker network as a pccs service, override PCCS_HOST=pccs:8081 before
starting the tee profile.
Run the native server container on the default port:
docker run --rm -p 8080:8080 gaiko2-native:latestThe tee image also starts as a server and defaults to port 8080, but it still
requires the usual SGX runtime devices and host configuration to run correctly.
Bootstrap the tee image explicitly before starting the server.
If you run the container on the same Docker network as a pccs service, the
default PCCS_HOST=pccs:8081 works out of the box:
docker run --rm \
--network docker_default \
--device /dev/sgx_enclave \
--device /dev/sgx_provision \
-e GAIKO2_PROVING_MODE=tee \
-v /path/to/config:/var/lib/gaiko2/config \
-v /path/to/secrets:/var/lib/gaiko2/secrets \
gaiko2-tee:latest --init
docker run --rm \
--network docker_default \
--device /dev/sgx_enclave \
--device /dev/sgx_provision \
-p 8080:8080 \
-e GAIKO2_PROVING_MODE=tee \
-v /path/to/config:/var/lib/gaiko2/config \
-v /path/to/secrets:/var/lib/gaiko2/secrets \
gaiko2-tee:latestIf you run gaiko2-tee outside that compose network, set PCCS_HOST
explicitly. For example, with host gateway routing:
docker run --rm \
--add-host host.docker.internal:host-gateway \
--device /dev/sgx_enclave \
--device /dev/sgx_provision \
-e GAIKO2_PROVING_MODE=tee \
-e PCCS_HOST=host.docker.internal:8081 \
-v /path/to/config:/var/lib/gaiko2/config \
-v /path/to/secrets:/var/lib/gaiko2/secrets \
gaiko2-tee:latest --init