Architecture
This document is the canonical product and architecture stance for Caatinga. It complements package-level code and the CLI reference in docs/cli.md. Detailed rationale for selected decisions lives in Architecture Decision Records under docs/adr/.
One-sentence promise
Deployment Orchestration + Versioned Artifacts for Soroban: local, graph-aware deployment orchestration and portable, Git-versioned artifacts for TypeScript teams.
That does not mean hiding Stellar reality. Users keep a stable Caatinga surface (ctg build, ctg deploy, ctg generate, ctg invoke, @caatinga/client). Changes in flags, stdout, paths, transaction/XDR workflow, and subprocess composition are absorbed behind small adapters, not scattered across user scripts.
What Caatinga is (and is not)
| Caatinga is | Caatinga is not |
|---|---|
| Convention + orchestration + artifacts + frontend/client integration | A second Soroban/Stellar SDK |
A thin CLI over @caatinga/core | A place to store private keys or run silent signing |
| Template-driven project scaffolding | A hosted registry required for core workflows (future registries are optional) |
Primary competitor today: ad-hoc package.json scripts.
For a detailed breakdown of all features categorized into Core, Nice to Have, Experimental, and Out of Scope, refer to the Scope Policy.
Direct ecosystem overlap: Scaffold Stellar (stellar scaffold + stellar registry, official templates, environments.toml, Vite/React frontend).
Caatinga differentiation: npm-first TypeScript toolkit (@caatinga/cli, @caatinga/core, @caatinga/client), caatinga.config.ts + caatinga.artifacts.json as the per-network artifacts contract, CAATINGA_* error codes as a public API, explicit wallet adapters, and multi-contract orchestration via dependsOn — without an on-chain registry or Rust macro layer.
Caatinga vs Scaffold Stellar
| Dimension | Caatinga | Scaffold Stellar |
|---|---|---|
| Entry point | npm install -g @caatinga/cli | Stellar CLI plugins (stellar scaffold, stellar registry) |
| Config contract | caatinga.config.ts + caatinga.artifacts.json | environments.toml + registry naming |
| Deploy model | Stellar CLI subprocess + per-network artifacts file | On-chain registry publish/deploy workflow |
| Browser integration | @caatinga/client with pluggable wallet adapters | Generated TS clients + Vite/React template |
| Error surface | Stable CAATINGA_* codes for automation | Stellar CLI / plugin errors |
What Caatinga should do unusually well: (1) persist git-versioned per-network deployment artifacts, (2) orchestrate multi-contract deploy graphs with dependsOn, (3) ship wallet-ready browser client integration, (4) track binding freshness, and (5) lower friction for JS/TS teams — CLI orchestration is infrastructure supporting those outcomes, not the headline value.
Competitive moat
| Moat | Why it matters |
|---|---|
| Portable artifacts file | Exit Caatinga without losing deploy history — caatinga.artifacts.json stays in your repo |
CAATINGA_* error API | Stable automation surface for CI/CD |
| Parser fixtures + adapters | Absorb Stellar CLI stdout drift without user script churn |
| npm-first, no on-chain registry | Sovereignty for teams that reject mandatory registry workflows |
| Multi-contract DAG deploy | Topological deploy + ${contracts.*.contractId} placeholders |
Honest risk: SDF may integrate overlapping workflow pieces into Stellar CLI or Scaffold Stellar. Caatinga competes on TypeScript DX + git artifacts + multi-contract orchestration, not on reimplementing Soroban or replacing the official SDK.
Core Pillars
Architecturally, Caatinga is structured around four pillars that compartmentalize responsibilities and prevent cross-boundary pollution:
1. Deployment (Orchestration Engine)
This pillar encompasses the build and deployment pipeline. It manages contract compilation (via Stellar CLI shell orchestration), multi-contract dependency topological sorting (dependsOn), placeholder resolution (e.g. ${contracts.token.contractId}), and executing post-deploy hooks (postDeploy). Components responsible: @caatinga/core (specifically deploy-graph, load-config), @caatinga/cli.
2. Artifacts (State Contract)
This pillar represents the static state representation of all deployments. The per-network caatinga.artifacts.json file serves as the Git-versioned contract between compilation, deployment, and client integration, capturing contract IDs, compiler hashes, and metadata. Components responsible: @caatinga/core (schema validations, state read/write).
3. Runtime (Client Integration Layer)
This pillar exposes the APIs consumed by browser/Node applications. It includes the TypeScript clients, pluggable wallet adapters, React context providers/hooks (@caatinga/client/react), and transaction pipeline orchestration (simulate → sign → submit → watch). Components responsible: @caatinga/client.
4. Automation (Developer Diagnostics & Safety)
This pillar ensures local workspace reliability, environment diagnostics (ctg doctor), regression checking (ctg smoke / postDeployRead), and CI/CD-friendly error APIs using stable error codes. Components responsible: @caatinga/cli (commands doctor, smoke), @caatinga/core (stable errors module).
Validation roadmap (flows)
- Supported v1 flow:
init → build → deploy → generate → invokeplus@caatinga/clientfor browser-side binding/artifact/wallet interop. - Shipped: multi-contract deploy with dependencies (e.g. deploy token, then a dependent contract such as vault that injects the token's
contractId, then generate bindings for both, then invoke across that dependency). See ADR 0005. - Shipped: upgrade / redeploy with artifacts history —
ctg deploy --upgrade(new instance) andctg upgrade(in-place WASM replacement). See Contract upgrade.
Supported v1 flow diagram
Each box is either a file you commit, a CLI command you run, or a runtime component. The arrows show which inputs are required to start the next step — for example, deploy needs both the compiled WASM and the network configuration.
Package boundaries (monorepo)
@caatinga/cli(CLI Interface): argument parsing, terminal UX, and delegation to the core Orchestration Engine—no subprocess orchestration except through core APIs.@caatinga/core(Orchestration Engine): loadcaatinga.config.ts, validate schemas, resolve networks/contracts, read/writecaatinga.artifacts.json, run Stellar CLI and related tools via a single shell layer (run-command.ts). Allexecausage stays here.@caatinga/client(Integration SDK): browser contract client, Freighter adapter, SWK adapter, React context, and the transaction execution pipeline. No Node-only dependencies, no shell orchestration, no file-system access. It must remain bundling-safe (Vite, Webpack, Turbopack).@caatinga/zk(ZK Cryptographic Engine): ZK proof serialization, Circom Groth16 workflow helpers, and browser binding args for on-chain verification.packages/templates(Project Scaffolds): official template starter layouts consumed byctg init.
For detailed package dependency boundaries and compliance rules, see Package Boundaries & Isolation Rules.
Deferred unless explicitly rescoped: CLI XDR commands, ctg generate --interop, full plugin system, RWA-only templates, visual dashboard, custom test runner as required core dependencies.
Dependency Map
Notes encoded in the diagram:
- CLI Isolation: The CLI Interface depends on the Orchestration Engine (
core), never the other way around. - Node vs Browser Boundaries: The Orchestration Engine is the only package that orchestrates subprocesses via CLI Adapters executing Stellar CLI commands. The Integration SDK (
@caatinga/client) consumes only the browser-safe subpath@caatinga/core/browser, ensuring that Node-specific dependencies likeexecaorfsare never pulled into web applications. - State Registry: The Artifacts State (
caatinga.artifacts.json) acts as the shared database between the Orchestration Engine (which writes it on deploy) and the Transaction Pipeline / Integration SDK (which reads it at runtime).
Meta-framework boundary: orchestrate workflow, not mental model
May abstract: build/deploy/bindings flow, artifact lookup, network config from the project, template layout, command composition, stable CLI commands, wallet adapter handoff, and generated-binding transaction workflow.
Should not hide (users and docs should keep these visible): contractId, network passphrase, RPC choice, accounts, wallet signing, XDR, fees, simulation, Soroban data model as understood via official SDKs and generated bindings.
Red flags (avoid): Caatinga-owned contract models, hand-rolled Soroban serialization, replacing generated bindings as the primary API, custom signing runtimes parallel to the Stellar ecosystem, or “smart” behavior that obscures what actually hit the network.
Rule of thumb: if Stellar CLI, stellar-sdk, Soroban SDK, or generated bindings already own it, Caatinga composes, validates, or organizes—it does not reimplement.
Product boundary: Caatinga ends at the typed client + resolved contract IDs (caatinga.artifacts.json, generated bindings, synced env). HTTP APIs, databases, async jobs, transaction hash persistence, and caller-specific auth models are application concerns — a green Caatinga pipeline does not imply a green production app. See production readiness and the app-side checklist in docs.
Source of truth (MVP)
Local project state is authoritative:
contracts/- Generated bindings (path from
caatinga.config.ts) caatinga.config.tscaatinga.artifacts.json
Each generated binding package carries a .caatinga-bindings.json marker recording the source contractId, wasmHash, and network. ctg status, doctor, and generate compare the marker against caatinga.artifacts.json to flag stale bindings after a redeploy. The marker is a sidecar, not part of the artifacts schema: deleting a bindings directory simply resets its state to missing.
No central cache or remote artifact registry is assumed in the core MVP. Optional remote services may exist later but must not be hard dependencies of core.
ctg dev
MVP direction: opinionated proxy around Vite + Caatinga validation (not a plugin or template store). Official templates are Vite + React only (vite-react). Future adapters (next, astro, custom) are conceivable only after the core workflow and multi-contract story prove value. See CLI — Supported today vs not yet.
Extensibility
- Templates: start as opinionated snapshots (
react-vite-counter, etc.). Parameterized generators (--tailwind, wallet flavor, i18n) come later—they expand the test matrix quickly. - Template contract: every template includes a
caatinga.template.jsonmanifest (name, version,compatibleCore, paths) so templates and core semver are validated atinit—see ADR 0003. - Post-deploy hooks:
postDeploy,postDeployRead, andsmokeare first-class config surfaces for wiring, read verification, and CI smoke — see ADR 0006 and Config — postDeploy. Expect DSL matchers (reachable,isArray, etc.) apply to hooks,ctg read --expect, andctg smoke. - Verification layer:
ctg smoke,ctg regression, andctg ci runcompose deploy/generate with read checks.@caatinga/coreexportsverifyExpect,evaluateEnvDrift, andrunSmokeReadsfor custom tooling. - Plugins: still deferred for broader extension points (for example CI presets or indexer hooks). Keep hooks declarative and data-only until a concrete use case requires executable plugin code.
Ecosystem: official vs community templates
- Official: live in the Caatinga repo, reviewed, CI-tested, semver-matrices documented.
- Community: installable via Git URL or npm-style packages, never implicitly trusted—treat as untrusted code; warn users; avoid auto-running post-install scripts from external templates in MVP-class flows.
Distribution: MVP+1 favors Git + URL (e.g. ctg init my-app --template github:org/repo). A dedicated template registry is explicitly later—moderation, security, and availability cost.
Suggested naming: @caatinga/template-* for official; @scope/caatinga-template-* for community.
Networks vs environments
Today: artifacts keyed by network (same logical contract may differ per network—correct).
Future (MVP+1): environment (e.g. staging vs production) can share a network but differ in deployed contractIds. Expect either a new artifacts shape version or an explicit environments model—design TBD with migration (ctg migrate) when introduced.
Multi-contract
Deploy order is supported in core for declared dependencies (DAG / topological sort), with artifact-safe constructor arg injection through ${contracts.<name>.contractId} — see ADR 0005. The next layer is runtime wiring: ${source.address}, postDeploy, ctg wire, frontend env sync, and workspace builds are covered by ADR 0006.
CI and secrets
Caatinga does not manage long-lived private keys. CI provides identities (--source ci-deployer), secrets via the platform, and a configured Stellar CLI on the runner. Caatinga validates config, runs deploy/generate/invoke, updates artifacts, and fails with clear, stable error codes (see below).
Client and frontend SDK
@caatinga/client provides the browser/client-side interop layer: generated binding registration, artifact-based contractId lookup, wallet adapters, invoke(), read(), simulate(), buildXdr(), and explicit XDR/raw debug output. The @caatinga/client/react subpath ships WalletProvider + useWallet hooks so React apps stop hand-rolling wallet context — React stays an optional peer. Avoid a parallel generic Soroban client that bypasses generated types.
DX beyond CLI
Prefer ctg doctor (bins, config/artifact sanity, network/source checks, optional staleness hints later) before investing in VS Code/LSP.
Errors as public API
Stable CAATINGA_* codes are part of the contract for CI, support, and docs. New public errors must be added through the central CaatingaErrorCode object and documented in errors.md—see ADR 0004.
Testing strategy vs Stellar CLI drift
Layered approach:
- Unit tests in
@caatinga/core. - Fixtures of Stellar CLI stdout/stderr per supported CLI generation (parsing is the fragile boundary).
- Contract tests with pinned Stellar CLI versions in CI.
- Optional scheduled smoke against testnet.
The runtime compatibility check (evaluateStellarCliCompatibility) intentionally decouples the hard floor (22.x invoke-signing bug) from the last-tested version. Drift above the last-tested version is a non-fatal advisory, not a CI failure, so we can keep one pinned fixture version in CI while still running against whatever Stellar CLI the developer has installed. See the Stellar CLI version contract for the operational rules.
Versioning and migrations
Semver applies to monorepo packages and to serialized formats (caatinga.artifacts.json already has a version field). Breaking format or command behavior should eventually ship with ctg migrate—not required on day one, but fields and ADRs should assume migrations will exist.
Performance responsibilities
- Rust / Stellar toolchain: real compile and WASM output.
- Caatinga: detect stale WASM vs sources when feasible, avoid redundant
generate, compare WASM hashes, emit clear “run build first” guidance. MVP: basic checks; later: stronger staleness and caching policies.
Business stance for the core
Core stays open-source and neutral (CLI, core, baseline templates, artifacts, config workflow). Revenue-bearing or hosted offerings, if any, stay outside the neutrality of the core dependency graph.
Architecture Decision Records
| ADR | Status | Topic |
|---|---|---|
| 0001 | Accepted | Stable Caatinga workflow while encapsulating Stellar CLI churn |
| 0002 | Accepted | Local artifacts and config as source of truth; no central registry in MVP |
| 0003 | Accepted | Template manifest and core compatibility |
| 0004 | Accepted | Stable CAATINGA_* error codes and migration |
| 0005 | Accepted | Multi-contract dependsOn and contractId injection |
0001–0005 are ratified; multi-contract deploy sequencing and placeholder resolution are implemented in @caatinga/core and documented in ADR 0005.