Architecture
Sunscreen is organized in four layers. Each one has a single responsibility and a clean interface to the next.
flowchart TB
User([User]) --> CLI[CLI surface<br/>clap commands]
CLI --> Runtime[Runtime layer<br/>watcher · supervisor · pipeline]
CLI --> Templates[Templates engine<br/>rust-embed · minijinja]
CLI --> Plugins[Plugin runtime<br/>stdio JSON-RPC · sandbox]
Runtime --> Tools[(External tools<br/>anchor · cargo-build-sbf<br/>codama · surfpool)]
Templates --> Workspace[(Workspace files<br/>programs/ · app/<br/>sunscreen.yml)]
Plugins --> Workspace
Runtime --> Workspace
Layer 1 — CLI surface
src/cli/. One module per top-level command (chain.rs, scaffold.rs, generate.rs, app.rs, onboarding/). Argument parsing via clap, exit codes from a single error.rs.
The CLI layer is thin: it parses, validates, then hands off to the runtime or templates layers.
Layer 2 — Runtime
src/runtime/. Owns:
- Subprocess management (
subprocess.rs) —CommandSpec,ProcessRunner,SubprocessRunner. Every shell-out goes through this for testability. - Build pipeline (
pipeline.rs) — anchor build → IDL export → Codama → frontend notify. - Watcher (
watcher.rs) —notifyevents, debounced, dedup'd, filtered. - Validator adapters (
surfpool.rs,testvalidator.rs) — abstracted behind aLocalValidatortrait. - Supervisor (
supervisor.rs,serve.rs) — long-running orchestrator forchain serve.
The runtime never reads CLI args directly. It receives configured *Spec structs.
Layer 3 — Templates
src/templates/ + templates/ (embedded via rust-embed).
Two responsibilities:
- Scaffold workspaces and primitives with deterministic
minijinjarendering. Golden-tested. - Patch existing files through
src/rustpatch/— the marker engine. Line-based, AST-free, resilient to malformed input.
Layer 4 — Plugin runtime
src/plugin/. Discovers plugins from sunscreen.yml, validates manifests, opens a stdio JSON-RPC session, enforces the sandbox, and routes scaffold <noun> commands when a plugin claims a noun.
The gRPC contract in proto/plugin.proto is wire-defined but not yet end-to-end live.
Cross-cutting
| Concern | Where |
|---|---|
Config (sunscreen.yml) | src/config/ — schema, loader, migrations |
| Toolchain detection | src/toolchain/ — uniform ToolReport for every external tool |
| Errors | src/error.rs — single Error enum, code + next_step |
| TUI (chain serve) | src/tui/serve_model.rs |
Design principles
- Determinism. Same inputs → same outputs. Golden tests cover scaffolds; round-trip tests cover marker patches.
- Boundary isolation. Every subprocess call goes through the runner; every file write is in the templates layer or the marker engine. Easy to mock for tests.
- Two-layer plugin model. JSON-RPC over stdio is the floor; gRPC is the ceiling. Both share the same logical contract.
- CLI is the API.
--jsonoutput is part of stability. Editor integrations don't need an SDK.
See also
- Build pipeline — what runs in what order.
- Incremental scaffolding — markers in depth.
- Plugin runtime — sandbox and trust.