Key Gotchas & Architectural Rules
A curated list of critical rules, boundaries, and gotchas to keep in mind when working with Caatinga.
Critical Rules
--sourcemust be a CLI identity alias: Always use an alias (e.g.alice). Never pass a public key (G...), secret key (S...), or seed phrase.- Deploy auto-generates bindings: Pass
--no-generateif you wish to skip binding generation during CI deploys. - Full graph deploy auto-runs
wire+sync-env: Pass--no-wireor--no-sync-envto disable post-deploy hooks and environment synchronization. As of 3.9.2,sync-envno longer wipes the target env file — only the keys mapped infrontend.envare rewritten; unrelated assignments, comments, and blank lines are left untouched, and new keys are appended. (Before 3.9.2 it rebuilt the whole file, silently destroying other variables like API keys.) - Browser invoke is single-invoker only until v1.0: Multi-signer transactions (
signAuthEntry) require custom application code and throwCAATINGA_MULTI_AUTH_REQUIRED. - ZK on mainnet is blocked by default: Dev ceremony artifacts require
--allow-dev-ceremonyto deploy on mainnet. - Fresh machine setup: Install Node 22+, Rust, and Stellar CLI manually, then verify with
npx ctg doctor. - Errors are public API: Machine-readable
CAATINGA_*error codes must be parsed by scripts instead of parsing raw stdout/stderr text messages. read()vsinvoke():read()is a read-only simulation (no key signing, no network transaction);invoke()signs and submits an on-chain transaction.- Wallet adapters must reject on dismissal: Wallet prompts should reject on user cancellation, never leaving promises pending indefinitely.
caatinga.artifacts.jsonis git-versioned: Commit your artifacts file to version control alongside your application code.- Binding freshness is tracked automatically: Managed via
.caatinga-bindings.jsonmarkers (fresh,stale,missing,unknown). ctg doctordeploy coverage is advisory: Un-deployed contracts produce warnings but do not block CI exit code even with--strict.- Stellar CLI compatibility: Hard floor is 23.0.0; last tested is 27.0.0. Newer versions output advisory warnings.
buildRoot: When specified, runs a singlestellar contract buildfrom the Cargo workspace root rather than per-contract crate builds.buildFeatures: Cargo features passed directly tostellar contract build. Combine with--no-default-featuresto override defaults.postDeploysource override: Per-hooksourceis validated viaassertSafeSourceAccount(rejectsS...,G..., seed phrases).postDeployexpect: Verified using the Expect DSL matchers (equals,contains,matches,isArray,isNull).frontend.envsuffixes: Keys support.contractId(default),.wasmHash,.deployedAt, and.wasmPathsuffixes.doctor --strict: Fails on environment drift (--strict-env) and stale bindings (--strict-bindings).ctg regression: Local execution recipe mirroring CI:test → build → deploy --if-changed → generate → smoke.- Alias resolution: Method arguments accept
${source.address}or CLI identity aliases (length ≥ 3). identity export/import: Exports your whole Stellar config directory as a base64 tarball — not encrypted. Treat the output as key material forCAATINGA_CI_STELLAR_CONFIG_B64. Prior to 3.9.2, the archive was also left behind, world-readable, inos.tmpdir(); ifidentity export/importever ran on a machine before upgrading, check for and delete/tmp/caatinga-stellar-*.tar.gzand rotate any exposed keys. See CLI Identity Reference.- Config graph validation:
${contracts.*.contractId}placeholders indeployArgsmust be declared independsOn; validated at config load time. ctg upgradevsdeploy --upgrade: In-place preservescontractIdand invokesupgrade(); redeploy (deploy --upgrade) creates a new contract instance.read --summary/--quiet: Truncates large array payloads for clean terminal inspection on shared networks.ctg generaterequiresfrontend.bindingsOutput: Every--minimalscaffold starts without it.doctor/statusonly recommendgenerateonce the field is configured; the error prints the exact snippet to paste.- Concurrent
deploy/upgradeare safe as of 3.9.2: Artifact writes tocaatinga.artifacts.jsonare serialized with a lockfile and re-read inside the lock, so two processes running at once can no longer drop each other’scontractId. parseContractIdprefers labeled output: As of 3.9.2 it favors the last explicitly labeled contract-ID line, then the last standalone ID line, then the last bare match — and only matches the base32 alphabet (A-Z2-7) real strkeys use — so a diagnostic line printed before the real result can no longer be mistaken for the deployed contract ID.deployretries un-indexed WASM: If the RPC hasn’t yet indexed a just-uploaded WASM hash,deployretries with backoff instead of failing immediately withCAATINGA_DEPLOY_FAILED.