Search docs

Find a documentation page

Validator install guide

Deploy the 2QUIC TEE into your own AWS account with validator-cli fetch-release and deploy.sh.

This page is the manual command reference for installing the TEE stack (Nitro Enclave + host-proxy) into your own AWS account from the open-source repo staked-quic-connection-provider. Everything runs under your AWS account and your credentials. The marketplace never touches your infrastructure.

The recommended path is the web onboarding funnel at /validator (Identify, Provision TEE, Register, Live). It prefills two command blocks: a clone + build + deploy install block (which uses the from-source build.sh path rather than the operator fetch-release path in Step 2) and the validator-cli register command with the one-time --onboard-token that attributes the listing to your web account. See Validator onboarding. Every other command on this page is manual only.

Prerequisites

One-time AWS account preparation is covered by deploy/PREREQUISITES.md in the staked-quic-connection-provider repo. Budget 30 to 45 minutes for it: MFA on the root account, a dedicated IAM deploy user with a minimal policy, and a region choice. The playbook defaults to us-east-1 (the cheapest). Nitro-capable instance families are available in every major commercial region.

Local tools, from the same document:

ToolMin versionNeeded by
AWS CLIv2everyone
Terraform1.6everyone
Rust1.92 (pinned by rust-toolchain.toml)everyone, builds validator-cli
openssl1.1+everyone
jq1.6+everyone
grpcurl1.9+everyone
Docker24+developers building from source (build.sh), and smoke-test.sh

deploy.sh additionally expects python3, curl, and xxd, which are preinstalled on most systems. You also need your validator identity keypair as a 64-byte solana-keygen JSON file on the machine you deploy from.

Step 1: build validator-cli

git clone https://github.com/nodexpert-labs/staked-quic-connection-provider.git
cd staked-quic-connection-provider
cargo install --path validator-cli --locked

Rust is required on the operator workstation solely for this build. The CLI carries the embedded K5 release trust root (the key that signs releases), so it must not be downloaded over the very channel it verifies. The deployed artefacts themselves are never built by operators.

The CLI has exactly five subcommands: fetch-release, register, set-payout-wallet, security, and emergency-stop.

Step 2: stage the release artefacts

deploy.sh never builds anything. It consumes pre-staged artefacts from deploy/artefacts/ and fails fast if they are missing. There are two ways to stage them.

Operators fetch the signed release:

validator-cli fetch-release --version 1.2.3

This downloads the release from the GitHub Releases base (default https://github.com/nodexpert-labs/staked-quic-connection-provider/releases, override with --releases-url), K5-verifies the signed EIF attestation, refuses a version mismatch, and sha384-verifies enclave.eif, host-proxy, and provision before placing them in deploy/artefacts/. It also writes .pcr-values.json (the PCR0/1/2 enclave measurements from the signed attestation) and .release-version.json (which enables the revocation kill-switch poller on the host).

Developers building from source run ./deploy/scripts/build.sh instead. It builds the binaries and the EIF inside an Amazon Linux 2023 Docker container (Linux x86_64 only, use WSL2 or a VM on macOS/Windows) and captures the PCRs from nitro-cli build-enclave. Locally built artefacts carry no .release-version.json, so the subsequent deploy needs the test-only --dev-no-pollers flag.

Step 3: export the marketplace JWT environment

deploy.sh validates these before touching Terraform and exits 1 if any of the three required variables is missing. They configure the host-proxy to verify the short-lived ES256 lease JWTs the marketplace mints for traders.

VariableRequiredWhat it is
MARKETPLACE_JWT_PUBKEY_PEMyesThe marketplace's ES256 (P-256) JWT verification public key, PEM format
MARKETPLACE_JWT_AUDyesThe aud claim the host-proxy accepts. Must equal the endpoint_url you register, byte for byte
MARKETPLACE_JWT_VAL_PKyesYour validator identity pubkey, base58. Matched against the JWT val_pk claim
MARKETPLACE_JWT_KIDnokid the marketplace puts in JWT headers. Default mkt-jwt-v1-dev
MARKETPLACE_API_URLnoSet to https://api.swqos.dev to enable usage telemetry. The API key file written by register next to your keypair is detected automatically
export MARKETPLACE_JWT_PUBKEY_PEM="$(cat marketplace-jwt-pubkey.pem)"
export MARKETPLACE_JWT_AUD="https://__PUBLIC_IP__:443"
export MARKETPLACE_JWT_VAL_PK="<your-identity-pubkey-base58>"

MARKETPLACE_JWT_AUD must equal the registered endpoint_url byte for byte: scheme, host case, port, trailing slash. A mismatch rejects every newly minted lease JWT with PERMISSION_DENIED "wrong audience". If you ever change the listing's endpoint_url, update this value on the host in lockstep. The literal __PUBLIC_IP__ placeholder is replaced with the allocated Elastic IP after terraform apply, which is useful when no DNS name fronts the endpoint.

The test-only --insecure-no-auth flag skips this block and deploys with an empty [jwt] config. Never use it for a listing that will take real leases.

Step 4: run deploy.sh

./deploy/scripts/deploy.sh \
  --identity-keypair /path/to/validator-keypair.json \
  --region us-east-1
FlagDefaultMeaning
--identity-keypair <path>required64-byte Solana keypair JSON
--region <region>us-east-1AWS region to deploy into
--allowed-cidr <CIDR>detected operator IP/32Ingress allowlist on the gRPC 443 security group rule
--planoffterraform plan only, then stop (no apply)
--insecure-no-authoffSkip the JWT config (TEST ONLY)
--dev-no-pollersoffDisable the revocation poller (TEST ONLY, required for locally built artefacts)

The script fails fast if a Terraform state already contains resources (run destroy.sh first). A --plan run is a complete pre-flight: tools, AWS credentials, staged artefacts, and JWT environment are all checked up front.

A full run takes roughly 10 to 15 minutes and does, in order:

  1. Validates the keypair and derives your identity pubkey.
  2. Fails fast on existing state or missing artefacts.
  3. Detects your public IP for the security-group allowlist.
  4. Picks the cheapest Nitro-capable instance type available in the region. Candidates in order: c6a.xlarge, c5.xlarge, c6i.xlarge, c7i.xlarge, m6a.xlarge, m6i.xlarge. There is no tier or instance flag, selection is automatic.
  5. Runs terraform apply.
  6. Waits for the SSM agent to come online.
  7. KMS-encrypts the validator seed and installs the secrets: the seed ciphertext travels in the SSM command, the rendered config.toml goes via the SSE S3 bucket (deleted afterwards), and the TLS keypair is generated on the host. Only the public cert is echoed back to deploy/tls/cert.pem.
  8. Starts the enclave and host-proxy, then runs provision, which KMS-decrypts the seed inside the enclave.
  9. Verifies gRPC Health: the enclave reports provisioned: true and the reported pubkey_sha256 matches the SHA-256 of your identity pubkey. A mismatch aborts.
  10. Prints the client config: endpoint, port, auth, CA cert path, TEE pubkey.

What Terraform actually provisions

  • A VPC with one public subnet, an internet gateway, and a route table.
  • A security group allowing gRPC 443 from --allowed-cidr only.
  • One Nitro-capable EC2 instance plus an Elastic IP.
  • A KMS key and alias whose decrypt policy is attestation-gated (PCR-bound to the staged enclave measurements).
  • An S3 artefacts bucket (SSE, versioned, public access blocked).
  • An IAM role, instance profile, and policies, plus the SSM attachment.

There is no NAT gateway, no load balancer, no VPC endpoints, and no CloudWatch log groups. Host and proxy logs live on the instance via systemd/journald, reachable over SSM.

Step 5: register the listing

The endpoint printed by deploy.sh becomes the endpoint_url of your listing, and it must equal the MARKETPLACE_JWT_AUD you exported (after __PUBLIC_IP__ substitution). Register with:

validator-cli register \
  --identity-keypair /path/to/validator-keypair.json \
  --endpoint-url https://<your-eip>:443 \
  --price 2.5 \
  --bio "Your public listing bio" \
  --region us-east-1

The full flag reference (payout wallet, shared offering, onboarding token, dry run) is on the onboarding page. Two things to know now: the minted API key is written once to a 0600 file named <validator_id>.api-key next to your keypair and never shown again, and the listing goes live immediately on successful registration.

Step 6: verify

deploy.sh already verified the enclave at step 9. To re-check manually at any time:

grpcurl -insecure \
  -import-path proto -proto signer.proto \
  <your-eip>:443 signer.v1.StakedQuicSigner/Health

Expect provisioned: true. The -insecure flag is needed because the TEE leaf certificate is self-signed with the Elastic IP in the SAN. The host-proxy does not expose gRPC reflection, so grpcurl must be given the proto file. Run the command from the root of the repo checkout from Step 1, which carries proto/signer.proto.

Run the end-to-end smoke test, which sends 50 memo transactions through the full TLS + TEE + QUIC pipeline in staked mode and reports landing rate plus p50/p95 confirm latency:

MARKETPLACE_LEASE_JWT=<a-marketplace-lease-jwt> ./deploy/scripts/smoke-test.sh

It needs Docker, solana, and solana-keygen locally, takes --rpc-url (default https://api.testnet.solana.com), --tx-count (default 50), and --trader-keypair (default deploy/trader-keypair.json), and requires a marketplace lease JWT in MARKETPLACE_LEASE_JWT.

Finally, once registered, the marketplace probe calls SignCertificateVerify on your TEE every 60 seconds. Watch probe success and TEE health on your dashboard at /validator.

Re-printing the client config

./deploy/scripts/display-config.sh

Prints the client configuration (endpoint, auth, CA cert path, TEE pubkey) of the current deployment. Idempotent, safe to run any time after deploy.sh.

Teardown

./deploy/scripts/destroy.sh        # interactive confirmation
./deploy/scripts/destroy.sh --yes  # skip the prompt

This runs terraform destroy (EC2, EIP, KMS key, S3 bucket, VPC) and wipes local secrets (the test seed, deploy/tls/, report files). It preserves trader-keypair.json, deploy/artefacts/, and terraform.tfvars, so the next deploy.sh run is fast. Since deploy.sh refuses to run over an existing state, destroy.sh is also the path to a from-scratch redeploy.

For an in-place version upgrade or rollback, do not destroy. Use fetch-release plus update.sh as described in the TEE upgrade runbook. After an EC2 reboot the enclave comes back unprovisioned by design, see the incident response runbook for reprovision.sh.

Next steps