Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
25c157d
refactor(spartan): unify deployment config sources
spypsy Apr 29, 2026
a4e4c4c
refactor(spartan): move deploy defaults into YAML
spypsy Apr 29, 2026
af3d808
fix(spartan): emit GCP secret mask commands on stderr
spypsy Apr 29, 2026
4191d76
refactor(spartan): single var.deploy/env/releases for deploy-aztec-infra
spypsy Apr 29, 2026
09563fb
quoting fixes
spypsy Apr 29, 2026
bb53178
fix(spartan): wrap loader release values for wrapper charts
spypsy Apr 30, 2026
ad21fc7
ci(deploy-network): only alert on next branch
spypsy Apr 30, 2026
8c5b2d3
fix(spartan): restore .Values.node.env loop in pod template
spypsy Apr 30, 2026
0199444
fix(spartan): route common env to subchart alias in wrapper charts
spypsy Apr 30, 2026
56f7572
fix(spartan): rename R2_ACCESS_KEY_ID -> AWS_ACCESS_KEY_ID throughout
spypsy Apr 30, 2026
8c54e9a
fix(spartan): remove AWS credential set blocks that override with emp…
spypsy Apr 30, 2026
97713f4
HA var fix
spypsy May 5, 2026
def8213
blob_sink 1 replica
spypsy May 5, 2026
223668d
fix P2P_ENABLEd
spypsy May 5, 2026
19ffccd
fix bot startCmd
spypsy May 6, 2026
ec149cc
fix bots, remove more camelCase vars
spypsy May 6, 2026
682f4c1
undo unecessary changes
spypsy May 6, 2026
96640f5
Merge conflicts + topology fixes
spypsy May 11, 2026
5bd39aa
Merge branch 'merge-train/spartan' into spy/deployment-config
spypsy May 12, 2026
f7127da
fix noir-project bootstrap
spypsy May 12, 2026
dae4079
Merge branch 'spy/deployment-config' of https://github.com/AztecProto…
spypsy May 12, 2026
0a87a03
remove --archiver from infra
spypsy May 12, 2026
7ca13dd
Merge branch 'merge-train/spartan' into spy/deployment-config
spypsy May 12, 2026
cdd6459
fix env / deploy shapes
spypsy May 12, 2026
ab7ed4b
migrate contract deployment
spypsy May 12, 2026
4bff2cc
better wrapping for bot & validator
spypsy May 12, 2026
9bb4fe3
fix r2 env values sourcing
spypsy May 12, 2026
3e8bb62
remove default duplicates
spypsy May 12, 2026
3cedb1a
chore(spartan): tidy loader, drop placeholder defaults
spypsy May 12, 2026
0f40316
python files instead of inline scripts
spypsy May 12, 2026
1dcd998
fix docker image for deploy contracts job
spypsy May 12, 2026
b10ff73
refactor(spartan): derive resource profiles and p2p flags in Terraform
spypsy May 12, 2026
129f859
fix(spartan): genesis flags must prefer var.env over var.deploy
spypsy May 12, 2026
8d2ee1d
fix(spartan): restore VALIDATOR/FISHERMAN_MNEMONIC_START_INDEX in mai…
spypsy May 12, 2026
76e399a
update with TS script
spypsy May 13, 2026
9ddf18e
refactor(spartan): replace Python config scripts with TS, env spread …
spypsy May 13, 2026
5f76045
fix(spartan): env spread must not clobber array values with string en…
spypsy May 13, 2026
c7cc534
fix(spartan): env spread must not clobber GCP secret placeholders
spypsy May 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions .github/workflows/deploy-network.yml
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,10 @@ jobs:

- name: Validate inputs
run: |
# Validate network
if [[ ! -f "spartan/environments/${{ inputs.network }}.env" ]]; then
echo "Error: Environment file not found for network '${{ inputs.network }}'"
if [[ ! -f "spartan/environments/networks/${{ inputs.network }}.yml" ]]; then
echo "Error: Network YAML not found: spartan/environments/networks/${{ inputs.network }}.yml"
echo "Available networks:"
ls -1 spartan/environments/ | grep -v '\.local\.env$' || echo "No environment files found"
ls -1 spartan/environments/networks/
exit 1
fi

Expand Down Expand Up @@ -206,10 +205,10 @@ jobs:

cd spartan
./scripts/install_deps.sh
./scripts/network_deploy.sh "${{ inputs.network }}"
./scripts/deploy_network_with_env.sh "${{ inputs.network }}"

# need to source this for CLUSTER
source "./environments/${{ inputs.network }}.env"
source "./scripts/source_env_basic.sh"
source_env_basic "${{ inputs.network }}"

if [ -n "$CLUSTER" ]; then
echo "cluster=$CLUSTER" >> $GITHUB_OUTPUT
Expand All @@ -234,7 +233,9 @@ jobs:
} >> "$GITHUB_STEP_SUMMARY"

- name: Notify Slack and dispatch ClaudeBox on failure
if: failure()
# Only alert on the canonical `next` branch -- avoids paging on
# ad-hoc deploys from feature branches or release tags.
if: failure() && steps.checkout-ref.outputs.ref == 'refs/heads/next'
env:
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
GH_TOKEN: ${{ secrets.AZTEC_BOT_GITHUB_TOKEN }}
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/ensure-funded-environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ on:

concurrency:
group: ensure-funded-environment-${{ inputs.environment }}
cancel-in-progress: false # Don't cancel funding operations
cancel-in-progress: false # Don't cancel funding operations

jobs:
ensure-funded:
Expand All @@ -71,10 +71,10 @@ jobs:
- name: Validate inputs
run: |
# Validate environment
if [[ ! -f "spartan/environments/${{ inputs.environment }}.env" ]]; then
echo "Error: Environment file not found for environment '${{ inputs.environment }}'"
if [[ ! -f "spartan/environments/networks/${{ inputs.environment }}.yml" ]]; then
echo "Error: Network YAML not found: spartan/environments/networks/${{ inputs.environment }}.yml"
echo "Available environments:"
ls -1 spartan/environments/ | grep -v '\.local\.env$' || echo "No environment files found"
ls -1 spartan/environments/networks/
exit 1
fi

Expand Down
3 changes: 2 additions & 1 deletion l1-contracts/scripts/load_network_defaults.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ fi

# explode(.) resolves YAML anchors (<<: *prodlike inheritance)
# Output as props, filter comments, normalize spacing
# Read from .networks.<name>.env (flat env baseline; was .networks.<name> pre-refactor).
while IFS='=' read -r key value; do
export "$key"="$value"
done < <(yq -o=props "explode(.) | .networks.$network | with_entries(select(.key | test(\"^AZTEC_|^ETHEREUM_\")))" "$network_defaults" \
done < <(yq -o=props "explode(.) | .networks.$network.env | with_entries(select(.key | test(\"^AZTEC_|^ETHEREUM_\")))" "$network_defaults" \
| grep -v '^#' \
| grep -v '^$' \
| sed 's/ = /=/')
3 changes: 2 additions & 1 deletion noir-projects/noir-contracts/bootstrap.sh
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,8 @@ function stamp_aztec_version {
semver check "$REF_NAME" 2>/dev/null && version="${REF_NAME#v}"
local tmp=$(mktemp)
jq --arg v "$version" '.aztec_version = $v' "$json_path" > "$tmp"
mv "$tmp" "$json_path"
cat "$tmp" > "$json_path"
rm "$tmp"
}
export -f stamp_aztec_version

Expand Down
27 changes: 2 additions & 25 deletions spartan/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,8 @@ tfplan
mnemonic.tmp
environments/*
!environments/network-defaults.yml
!environments/prove-n-tps-fake.env
!environments/prove-n-tps-real.env
!environments/ten-tps-short-epoch.env
!environments/ten-tps-long-epoch.env
!environments/five-tps-short-epoch.env
!environments/five-tps-long-epoch.env
!environments/devnet-next.env
!environments/devnet.env
!environments/block-capacity.env
!environments/next-net.env
!environments/next-net-clone.env
!environments/next-scenario.env
!environments/scenario.local.env
!environments/networks/
!environments/networks/*.yml
!environments/source-env.sh
!environments/staging-ignition.env
!environments/staging-public.env
!environments/staging.local.env
!environments/testnet-canary.env
!environments/testnet.env
!environments/mainnet.env
!environments/tps-scenario.env
!environments/kind-minimal.env
!environments/kind-provers.env
!environments/alpha-net.env
!environments/mbps-pipeline.env
!environments/bench-10tps.env
*.tfvars
!terraform/deploy-external-secrets/*.tfvars
110 changes: 84 additions & 26 deletions spartan/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ spartan/
├── aztec-keystore/ # Key derivation setup chart
├── aztec-postgres/ # Simple PostgreSQL chart for HA signing
├── aztec-snapshots/ # Snapshot management chart
├── environments/ # Environment-specific configurations (.env files)
├── environments/ # Network YAML configs + network-defaults.yml
└── terraform/
├── deploy-aztec-infra/ # Main deployment module
└── modules/ # Reusable Terraform modules
Expand Down Expand Up @@ -48,22 +48,26 @@ The main entry point is `terraform/deploy-aztec-infra/`:
### Helm Charts

**aztec-node** (base chart):

- Deployable as Deployment or StatefulSet
- Configurable via `node.env` for environment variables
- Pre-start scripts for dynamic configuration
- Services for P2P, RPC, and admin endpoints
- Pod template in `templates/_pod-template.yaml`

**aztec-validator** (extends aztec-node):

- Wrapper chart with `aztec-node` as dependency (aliased as `validator`)
- Adds validator-specific ConfigMap (`env.configmap.yaml`)
- Configures mnemonic, validators-per-node, publishers-per-replica

**aztec-prover-stack**:

- Multi-component: prover node, broker, and agent replicas
- Each component has its own sub-values (`node`, `broker`, `agent`)

**aztec-postgres**:

- Simple PostgreSQL StatefulSet using official `postgres:16-alpine` image
- Used for validator HA signing coordination
- No third-party chart dependencies (avoids Bitnami, etc.)
Expand Down Expand Up @@ -115,11 +119,13 @@ module "validator_ha_postgres" {
```

The module:

- Deploys the `aztec-postgres` Helm chart
- Runs database migrations via a Kubernetes Job (`aztec migrate-ha-db up`)
- Outputs `database_url` for validators to connect

Validators receive the database URL via environment variables:

- `VALIDATOR_HA_SIGNING_ENABLED=true`
- `VALIDATOR_HA_DATABASE_URL=postgresql://...`
- `VALIDATOR_HA_NODE_ID` (auto-set from pod name)
Expand All @@ -128,62 +134,99 @@ Validators receive the database URL via environment variables:

### Network Defaults (Code Generation)

`environments/network-defaults.yml` is a **code generation source**, not a runtime config file. It centralizes "baked-in" defaults for the yarn-project packages.
`environments/network-defaults.yml` is the **joint source of truth** for both code generation AND runtime deployment. It defines:

- `l1-contracts:` (anchor `&l1-contracts-defaults`) -- L1 smart contract parameters; consumed by codegen
- `slasher:` (anchor `&slasher`) -- Slasher node operational settings; consumed by codegen
- `_prodlike:` (anchor `&prodlike`) -- merges the above and adds runtime defaults
- `_release_defaults:` -- per-release Helm-shape baselines (replicaCount, env)
- `networks.<name>.env:` -- per-network env baseline; consumed by both codegen AND runtime
- `networks.<name>.<release>:` -- optional per-release overrides; runtime only

**What it defines:**
- `l1-contracts`: L1 smart contract parameters (timing, validator thresholds, slashing)
- `slasher`: Slasher node operational settings (penalties, offense tracking)
- `networks`: Preset configurations for `devnet`, `testnet`, and `mainnet`
**Codegen outputs (paths and shape unchanged):**

**Generated outputs:**
- `yarn-project/ethereum/src/generated/l1-contracts-defaults.ts`
- `yarn-project/slasher/src/generated/slasher-defaults.ts`
- `yarn-project/cli/src/config/generated/networks.ts`
- `l1-contracts/generated/default.json`
- `yarn-project/ethereum/src/generated/l1-contracts-defaults.ts` (reads `.l1-contracts`)
- `yarn-project/slasher/src/generated/slasher-defaults.ts` (reads `.slasher`)
- `yarn-project/cli/src/config/generated/networks.ts` (reads `.networks.<name>.env`)
- `l1-contracts/generated/default.json` (reads `.l1-contracts` + `.networks.<name>.env`)

**Regenerate after editing:**

```bash
cd yarn-project/ethereum && yarn generate
cd yarn-project/slasher && yarn generate
cd yarn-project/cli && yarn generate
cd l1-contracts && ./bootstrap.sh
```

### Deployment Environment Files
### Deployment Configuration (per-network YAML)

Environment files in `environments/*.env` provide deployment-specific values:
Each deployable network has a YAML override at `environments/networks/<name>.yml`:

```bash
# Example: devnet.env
NAMESPACE=devnet
RELEASE_PREFIX=devnet
L1_RPC_URLS=https://...
VALIDATOR_REPLICAS=4
PROVER_REPLICAS=1
VALIDATOR_HA_REPLICAS=1 # 0 = no HA, 1 = primary + 1 HA release
```yaml
# environments/networks/devnet.yml
network: devnet # selects networks.devnet.env from network-defaults.yml as the env baseline

deploy: # consumed by deploy script / Terraform; not pod env
CLUSTER: aztec-gke-private
NAMESPACE: devnet
RPC_INGRESS_ENABLED: true

env: # adds to / overrides the network env baseline (UPPER_SNAKE pod env vars)
ETHEREUM_RPC_URLS: REPLACE_WITH_GCP_SECRET
LABS_INFRA_MNEMONIC: REPLACE_WITH_GCP_SECRET

validator: # per-release Helm values (mirrors aztec-validator chart shape)
replicaCount: 1
env:
P2P_GOSSIPSUB_D: "8"
prover:
agent:
replicaCount: 4
```

These are loaded by deployment scripts and passed to Terraform.
**Loader** (`scripts/load_network_config.sh`):

- Deep-merges `_release_defaults` + `networks.<preset>.env` + per-network YAML.
- Expands `${VAR}` and `${VAR:-default}` placeholders from shell env.
- Computes derived values (e.g. devnet's `MNEMONIC_INDEX_OFFSET` from NAMESPACE regex).
- Resolves `REPLACE_WITH_GCP_SECRET` placeholders via gcloud (when on PATH).
- Emits in `--format=env` (shell exports), `--format=json` (merged tree), or `--format=tfvars` (terraform.tfvars.json shape).

The `source_env_basic.sh` and `source_network_env.sh` helpers wrap the YAML loader; existing callers (`bootstrap.sh`, `network_deploy.sh`, GitHub Actions) keep their CLI signatures unchanged.

## Common Patterns

### Passing Environment Variables to Pods

Via Terraform `custom_settings`:
Set under the matching release block in `environments/networks/<name>.yml`:

```yaml
validator:
env:
MY_VAR: "value"
```

The loader emits this as `releases.validator.env.MY_VAR` in `terraform.tfvars.json`, which Terraform forwards to Helm via `yamlencode`. The chart's pod template mounts the values via `envFrom` on a generated ConfigMap (`{release}-env-from-values`).

For deploy-time-computed values (e.g. internal service URLs, HA postgres URL, per-HA mnemonic indices) that can't live in static YAML, set them via Terraform `custom_settings`:

```hcl
"validator.node.env.MY_VAR" = var.MY_VALUE
"validator.env.MY_VAR" = local.computed_value
```

This maps to Helm values that populate the pod's env section.
These end up in the same ConfigMap.

### Conditional Deployments

Use ternary operators in the `helm_releases` map:

```hcl
prover = tonumber(var.PROVER_REPLICAS) > 0 ? { ... } : null
```

For dynamic multi-release generation (e.g., HA validators), use `for` expressions:

```hcl
validator_releases = tonumber(var.VALIDATOR_REPLICAS) > 0 ? {
for idx in range(1 + var.VALIDATOR_HA_REPLICAS) :
Expand All @@ -194,6 +237,7 @@ validator_releases = tonumber(var.VALIDATOR_REPLICAS) > 0 ? {
### Values Layering

Values are applied in order (later overrides earlier):

1. `common.yaml`
2. `{component}.yaml`
3. `{component}-resources-{profile}.yaml`
Expand All @@ -203,11 +247,13 @@ Values are applied in order (later overrides earlier):
### Service Discovery

Internal services use Kubernetes DNS:

```
http://{release-name}-{component}.{namespace}.svc.cluster.local:{port}
```

Example web3signer URL:

```
http://staging-signer-web3signer.staging.svc.cluster.local:9000/
```
Expand All @@ -227,6 +273,7 @@ When `VALIDATOR_HA_REPLICAS > 0`, validators are deployed as **multiple Helm rel
- `VALIDATOR_HA_REPLICAS=2` → 3 releases (primary + 2 HA)

Example with `VALIDATOR_HA_REPLICAS=1`:

```
validator-0 & validator-ha-1-0 share attesters 0-11
validator-1 & validator-ha-1-1 share attesters 12-23
Expand Down Expand Up @@ -272,6 +319,7 @@ Publishers are allocated **per replica (pod)**, not per attester key. Each relea
```

Example with 4 replicas, 4 publishers/replica, base index 5000:

- Primary (idx=0): `PUBLISHER_KEY_INDEX_START = 5000`
- HA-1 (idx=1): `PUBLISHER_KEY_INDEX_START = 5000 + (1 * 4 * 4) = 5016`

Expand All @@ -283,28 +331,37 @@ PUBLISHER_KEY_INDEX=$((POD_INDEX * VALIDATOR_PUBLISHERS_PER_REPLICA + PUBLISHER_
```

The keystore uses **schema v2** with a top-level `publisher` array shared by all validators on the pod:

```json
{"schemaVersion": 2, "publisher": ["0x1", "0x2", "0x3", "0x4"], "validators": [{"attester": "..."}]}
{
"schemaVersion": 2,
"publisher": ["0x1", "0x2", "0x3", "0x4"],
"validators": [{ "attester": "..." }]
}
```

This ensures each release uses non-overlapping publisher key ranges while decoupling publisher count from attester count.

**HA coordination:**

- Both releases connect to shared PostgreSQL via `VALIDATOR_HA_DATABASE_URL`
- Database prevents double-signing by the same attester
- If one pod dies, its HA partner continues signing

### Provers

- Generate validity proofs for epochs
- Broker distributes proving jobs to agents
- Agents can scale horizontally

### RPC Nodes

- Serve public API endpoints
- Optional ingress with GCP backend config
- Archive nodes for historical data

### Boot Nodes

- P2P bootstrap for network discovery
- Internal boot node optional (can use external)

Expand All @@ -315,6 +372,7 @@ This ensures each release uses non-overlapping publisher key ranges while decoup
3. **New Helm chart**: Add to `spartan/` root (follow aztec-keystore pattern)

For new modules, follow the web3signer pattern:

- `main.tf`: Helm release(s) and supporting resources
- `variables.tf`: Input variables
- `outputs.tf`: Service URLs and other outputs
Expand Down
Loading
Loading