How to Use Script Customization for Chain Initialization in Starship
TLDR: Starship enables complete control over blockchain bootstrapping by allowing users to define custom shell scripts or inline commands in the ChainScripts configuration, which are mounted into Kubernetes init-containers via ConfigMaps to replace or extend default genesis creation, configuration updates, and token transfer logic.
Starship, maintained by hyperweb-io/starship, is a Kubernetes-native development environment for Cosmos SDK blockchains. Through its script customization for chain initialization feature, developers can override every step of the chain bootstrapping process—from genesis file creation to validator configuration—without modifying the core codebase.
Understanding the Script Customization Architecture
The script system relies on four coordinated components that transform user configuration into executable init-container commands.
The ChainScripts Interface and Script Types
According to the source code in packages/types/src/config.ts, the ChainScripts interface (lines 87-95) declares optional slots for each initialization phase, including createGenesis, updateConfig, and transferTokens. Each slot accepts a Script type (lines 151-157) that supports two input methods:
file: A relative path to a shell script on the local filesystemdata: Raw script content embedded directly in the YAML configuration
Script Resolution with ScriptManager
Located in generator/src/scripts.ts, the ScriptManager class resolves script definitions into executable content. When processing a file reference, it first checks the configuration directory (where the .starship.yaml resides), then falls back to the generator package root at packages/generator/scripts/default/. This resolution logic ensures test-specific scripts take precedence over bundled defaults.
Kubernetes ConfigMap Generation and Execution
The SetupScriptsConfigMapGenerator in generator/src/builders/chains/cosmos/configmap.ts (lines 24-38) transforms resolved script content into a Kubernetes ConfigMap named setup-scripts-<chain-hostname>. The CosmosGenesisStatefulSetGenerator (generator/src/builders/chains/cosmos/genesis.ts, lines 89-113) mounts this ConfigMap at /scripts inside the chain's init-containers.
The helper methods getGenesisScript and getValidatorStartScript (lines 595-603) provide fallback logic: if no custom script is defined, they return default definitions such as { name: 'create-genesis.sh', data: '/scripts/create-genesis.sh' }, ensuring the init-container always has a valid script to execute.
Configuring Custom Scripts in Starship
Users define scripts under the scripts key in their chain configuration. The system supports both external file references and inline definitions, with ScriptManager handling the resolution logic.
Referencing External Script Files
To override a specific initialization step, provide a file path relative to your configuration directory:
chains:
- name: osmosis
id: osmosis-1
image: ghcr.io/cosmology-tech/starship/osmosis:latest
scripts:
createGenesis:
file: my-scripts/custom-genesis.sh
As implemented in ScriptManager, this path resolves first against the configDir (the directory containing your Starship configuration), then against the package root. This allows you to keep test-specific scripts alongside your configuration files while falling back to reusable defaults shipped in the generator package.
Embedding Inline Scripts
For short automation tasks that do not warrant separate files, embed scripts directly using the data field:
chains:
- name: juno
id: juno-1
image: ghcr.io/cosmology-tech/starship/juno:latest
scripts:
transferTokens:
name: transfer-tokens.sh
data: |
#!/bin/bash
echo "Transferring initial funds..."
$CHAIN_BIN tx bank send $(cat /configs/faucet.mnemonic) $TARGET_ADDR 1000ujuno --keyring-backend test -y
The name field determines the filename within the generated ConfigMap. If omitted, the key name (e.g., transferTokens) is used with a .sh extension.
Script Execution Flow in Init Containers
When CosmosGenesisStatefulSetGenerator creates the StatefulSet definition, it generates init-container commands that execute scripts from the mounted ConfigMap. The default genesis initialization runs:
bash -e /scripts/create-genesis.sh
bash -e /scripts/update-genesis.sh
When custom scripts are provided via the configuration, SetupScriptsConfigMapGenerator stores the custom content at these specific paths within the ConfigMap. Consequently, the init-containers execute user-defined logic instead of the default scripts, despite using identical command invocations.
Complete Working Example
The following configuration demonstrates a custom genesis creation script that modifies governance parameters before the chain starts:
# .starship.yaml
name: my-test
chains:
- name: custom
id: custom-1
image: ghcr.io/cosmology-tech/starship/cosmos:latest
numValidators: 1
scripts:
createGenesis:
file: my-scripts/my-genesis.sh
# my-scripts/my-genesis.sh
#!/usr/bin/env bash
set -euo pipefail
echo "=== CUSTOM GENESIS CREATION ==="
jq '.app_state.gov.params.voting_period = "120s"' $CHAIN_DIR/config/genesis.json > /tmp/genesis.json
mv /tmp/genesis.json $CHAIN_DIR/config/genesis.json
During starship generate, the ScriptManager loads my-scripts/my-genesis.sh from the configuration directory. The SetupScriptsConfigMapGenerator stores this content in a ConfigMap mounted at /scripts/create-genesis.sh. When the init-container executes bash -e /scripts/create-genesis.sh, it runs the custom JSON modification instead of the default genesis creation logic.
Summary
- Script customization for chain initialization in Starship is controlled via the
ChainScriptsinterface defined inpackages/types/src/config.ts(lines 87-95). - The
ScriptManagerclass resolves bothfilereferences and inlinedata, checking the configuration directory before falling back to default package scripts inpackages/generator/scripts/default/. - Custom scripts are packaged into Kubernetes ConfigMaps by
SetupScriptsConfigMapGeneratorand mounted at/scriptsin init-containers. - Init-containers execute scripts using
bash -e /scripts/<script-name>.sh, automatically picking up custom content when provided via the configuration. - Both external files and embedded
datastrings are supported, allowing flexible workflow integration without core code modification.
Frequently Asked Questions
Can I override only specific initialization steps while keeping Starship defaults for others?
Yes. The ChainScripts interface defines optional slots for each phase. If you provide a custom script for createGenesis but omit updateConfig, Starship uses your custom genesis script while executing the default configuration update script located in packages/generator/scripts/default/.
How does Starship locate script files referenced in the configuration?
According to the ScriptManager implementation in generator/src/scripts.ts, Starship first attempts to resolve the file path relative to the directory containing your .starship.yaml configuration file. If the file does not exist there, it searches the generator package root, enabling both test-local overrides and reusable shared scripts.
Are inline scripts suitable for complex multi-line initialization logic?
Yes. The Script type supports multi-line strings via the data field, as defined in packages/types/src/config.ts (lines 151-157). However, for complex logic exceeding a few lines, external files referenced via file provide better maintainability, version control, and syntax highlighting in your IDE.
What happens if my custom script fails during chain initialization?
Init-containers execute with bash -e, which exits immediately on non-zero return codes, causing the pod initialization to fail and surface the error in Kubernetes logs. This behavior ensures faulty custom scripts halt deployment rather than proceeding with an invalid chain state, as implemented in CosmosGenesisStatefulSetGenerator (generator/src/builders/chains/cosmos/genesis.ts).
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →