From 90c185c43c91aa6d09c643d8b94483c15054df41 Mon Sep 17 00:00:00 2001 From: Pantani Date: Tue, 2 Jun 2026 15:56:07 -0300 Subject: [PATCH 1/2] docs: scaffold Phase 8 documentation site + docs-team harness MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the mdBook-based documentation site (41 pages across Learn / Guides / Reference / Concepts / Contributing) with the Eclipse theme, GitHub Pages workflow, and a dedicated 5-agent docs harness so future doc work can be orchestrated independently of the implementation team. Why now: Phase 8 of ROADMAP.md requires a public docs site before v1.0. The dual-audience requirement (newcomers + Solana professionals) is solved by splitting tracks rather than mixing tone within pages — Learn assumes zero background and defines every term; Reference is scannable for pros. Highlights: - mdBook config with admonish + mermaid + linkcheck preprocessors - Eclipse palette theme (dark-first, WCAG AA contrast) - GitHub Actions workflow publishing to gh-pages on push to main - Architecture / build-pipeline / scaffolding / plugin diagrams (mermaid) - 5 new agents (docs-architect, docs-tutorial-writer, docs-reference-writer, docs-designer, docs-reviewer) + sunscreen-docs-orchestrator skill Reference pages were synthesized from CLAUDE.md and ROADMAP.md — before public announcement, they should be cross-checked against src/cli/*.rs, src/error.rs, and src/plugin/. See _workspace/docs-review.md (gitignored) for the P1 list. Co-Authored-By: Claude Opus 4.7 --- .claude/agents/docs-architect.md | 41 +++ .claude/agents/docs-designer.md | 48 +++ .claude/agents/docs-reference-writer.md | 52 ++++ .claude/agents/docs-reviewer.md | 53 ++++ .claude/agents/docs-tutorial-writer.md | 66 +++++ .../sunscreen-docs-orchestrator/SKILL.md | 93 ++++++ .github/workflows/docs.yml | 78 +++++ CLAUDE.md | 1 + docs/site/README.md | 38 +++ docs/site/book.toml | 61 ++++ docs/site/src/SUMMARY.md | 61 ++++ docs/site/src/assets/logo.svg | 8 + docs/site/src/concepts/architecture.md | 70 +++++ docs/site/src/concepts/build-pipeline.md | 92 ++++++ .../concepts/framework-pinocchio-vs-anchor.md | 52 ++++ .../src/concepts/incremental-scaffolding.md | 59 ++++ docs/site/src/concepts/plugin-runtime.md | 96 ++++++ docs/site/src/concepts/workspace-model.md | 92 ++++++ docs/site/src/contributing/adrs.md | 34 +++ docs/site/src/contributing/dev-setup.md | 71 +++++ docs/site/src/contributing/docs-style.md | 86 ++++++ docs/site/src/contributing/roadmap.md | 47 +++ docs/site/src/guides/deploying-to-devnet.md | 124 ++++++++ docs/site/src/guides/deploying-to-mainnet.md | 104 +++++++ docs/site/src/guides/dev-loop.md | 96 ++++++ docs/site/src/guides/plugins.md | 109 +++++++ docs/site/src/guides/scaffolding-crud.md | 99 +++++++ docs/site/src/guides/troubleshooting.md | 112 +++++++ docs/site/src/index.md | 58 ++++ docs/site/src/learn/first-workspace.md | 106 +++++++ docs/site/src/learn/glossary.md | 79 +++++ docs/site/src/learn/installing.md | 78 +++++ docs/site/src/learn/rust-primer.md | 127 ++++++++ docs/site/src/learn/solana-primer.md | 89 ++++++ docs/site/src/learn/what-is-sunscreen.md | 48 +++ docs/site/src/learn/your-first-nft.md | 104 +++++++ docs/site/src/reference/cli/app.md | 116 ++++++++ docs/site/src/reference/cli/chain.md | 102 +++++++ docs/site/src/reference/cli/doctor.md | 62 ++++ docs/site/src/reference/cli/generate.md | 70 +++++ docs/site/src/reference/cli/index.md | 69 +++++ docs/site/src/reference/cli/onboarding.md | 101 +++++++ docs/site/src/reference/cli/scaffold.md | 100 +++++++ docs/site/src/reference/config/schema.md | 105 +++++++ docs/site/src/reference/errors.md | 94 ++++++ docs/site/src/reference/events.md | 72 +++++ docs/site/src/reference/markers.md | 76 +++++ .../src/reference/plugin-protocol/index.md | 118 ++++++++ docs/site/src/reference/recipes/crud.md | 93 ++++++ docs/site/src/reference/recipes/index.md | 27 ++ .../src/reference/recipes/metaplex-nft.md | 84 ++++++ docs/site/src/reference/recipes/spl-token.md | 66 +++++ docs/site/theme/css/admonish.css | 35 +++ docs/site/theme/css/general.css | 276 ++++++++++++++++++ docs/site/theme/css/variables.css | 104 +++++++ docs/site/theme/favicon.svg | 7 + docs/site/theme/js/mermaid-init.js | 26 ++ 57 files changed, 4435 insertions(+) create mode 100644 .claude/agents/docs-architect.md create mode 100644 .claude/agents/docs-designer.md create mode 100644 .claude/agents/docs-reference-writer.md create mode 100644 .claude/agents/docs-reviewer.md create mode 100644 .claude/agents/docs-tutorial-writer.md create mode 100644 .claude/skills/sunscreen-docs-orchestrator/SKILL.md create mode 100644 .github/workflows/docs.yml create mode 100644 docs/site/README.md create mode 100644 docs/site/book.toml create mode 100644 docs/site/src/SUMMARY.md create mode 100644 docs/site/src/assets/logo.svg create mode 100644 docs/site/src/concepts/architecture.md create mode 100644 docs/site/src/concepts/build-pipeline.md create mode 100644 docs/site/src/concepts/framework-pinocchio-vs-anchor.md create mode 100644 docs/site/src/concepts/incremental-scaffolding.md create mode 100644 docs/site/src/concepts/plugin-runtime.md create mode 100644 docs/site/src/concepts/workspace-model.md create mode 100644 docs/site/src/contributing/adrs.md create mode 100644 docs/site/src/contributing/dev-setup.md create mode 100644 docs/site/src/contributing/docs-style.md create mode 100644 docs/site/src/contributing/roadmap.md create mode 100644 docs/site/src/guides/deploying-to-devnet.md create mode 100644 docs/site/src/guides/deploying-to-mainnet.md create mode 100644 docs/site/src/guides/dev-loop.md create mode 100644 docs/site/src/guides/plugins.md create mode 100644 docs/site/src/guides/scaffolding-crud.md create mode 100644 docs/site/src/guides/troubleshooting.md create mode 100644 docs/site/src/index.md create mode 100644 docs/site/src/learn/first-workspace.md create mode 100644 docs/site/src/learn/glossary.md create mode 100644 docs/site/src/learn/installing.md create mode 100644 docs/site/src/learn/rust-primer.md create mode 100644 docs/site/src/learn/solana-primer.md create mode 100644 docs/site/src/learn/what-is-sunscreen.md create mode 100644 docs/site/src/learn/your-first-nft.md create mode 100644 docs/site/src/reference/cli/app.md create mode 100644 docs/site/src/reference/cli/chain.md create mode 100644 docs/site/src/reference/cli/doctor.md create mode 100644 docs/site/src/reference/cli/generate.md create mode 100644 docs/site/src/reference/cli/index.md create mode 100644 docs/site/src/reference/cli/onboarding.md create mode 100644 docs/site/src/reference/cli/scaffold.md create mode 100644 docs/site/src/reference/config/schema.md create mode 100644 docs/site/src/reference/errors.md create mode 100644 docs/site/src/reference/events.md create mode 100644 docs/site/src/reference/markers.md create mode 100644 docs/site/src/reference/plugin-protocol/index.md create mode 100644 docs/site/src/reference/recipes/crud.md create mode 100644 docs/site/src/reference/recipes/index.md create mode 100644 docs/site/src/reference/recipes/metaplex-nft.md create mode 100644 docs/site/src/reference/recipes/spl-token.md create mode 100644 docs/site/theme/css/admonish.css create mode 100644 docs/site/theme/css/general.css create mode 100644 docs/site/theme/css/variables.css create mode 100644 docs/site/theme/favicon.svg create mode 100644 docs/site/theme/js/mermaid-init.js diff --git a/.claude/agents/docs-architect.md b/.claude/agents/docs-architect.md new file mode 100644 index 0000000..c6e5e44 --- /dev/null +++ b/.claude/agents/docs-architect.md @@ -0,0 +1,41 @@ +--- +name: docs-architect +description: Arquiteto de informação do site de documentação do sunscreen. Dono da estrutura de navegação, escolha de stack (mdBook + tema custom), config do GitHub Pages, CI de docs, sumário (SUMMARY.md) e taxonomia das trilhas Learn/Reference/Guides. Não escreve conteúdo de páginas — define onde cada coisa mora. +model: opus +--- + +# Docs Architect + +## Core Role +Define a arquitetura do site de documentação em `docs/site/` (mdBook). Decide rotas, navegação, theming-hooks, deploy. + +## Decisões fixadas +- **Stack**: mdBook 0.4+ com tema `mdbook-admonish` + `mdbook-mermaid` + `mdbook-linkcheck`. Justificativa: Rust-native, build determinístico, deploy Pages trivial, profissionais Solana já conhecem (Anchor Book usa mdBook). +- **Estrutura de trilhas**: + - `learn/` — iniciantes (zero-to-NFT, primers Rust/Solana, glossário) + - `guides/` — tutoriais task-oriented (criar workspace, scaffold CRUD, deploy devnet) + - `reference/` — comandos, schema `sunscreen.yml`, recipes, plugin protocol, markers + - `concepts/` — modelo mental (workspace, marcadores, plugin runtime, IDL flow) + - `contributing/` — ADRs (link para `docs/adr/`), roadmap, dev setup +- **Deploy**: workflow GitHub Actions em `.github/workflows/docs.yml` publicando em `gh-pages` via `peaceiris/actions-gh-pages@v4`. +- **URL**: `https://.github.io/sunscreen/` (confirmar org com usuário no relatório). + +## Entregáveis +- `docs/site/book.toml` com preprocessors configurados, `output.html.git-repository-url`, edit-button, theme custom. +- `docs/site/src/SUMMARY.md` com hierarquia completa (cada autor preenche conteúdo depois). +- `docs/site/theme/` — overrides CSS variables (paleta, fonte) — coordenar com `docs-designer`. +- `.github/workflows/docs.yml` — build mdBook, linkcheck, deploy condicional em `main`. +- `docs/site/README.md` — como rodar local (`mdbook serve`), como adicionar página. + +## Princípios +- Cada página tem um único propósito (Learn ensina, Reference cataloga, Guide resolve tarefa). +- Profundidade progressiva: trilha Learn nunca assume conhecimento de Solana; trilha Reference nunca explica o básico de novo — apenas linka para Learn. +- Não duplique conteúdo entre trilhas. Quando tentado a duplicar, extraia para `concepts/` e linke. + +## I/O Protocol +- Lê: `ROADMAP.md`, `README.md`, `docs/adr/*.md`, `docs/reference/*.md` existentes. +- Escreve: arquivos acima. +- Sinaliza conclusão: `_workspace/done_docs-architect.md` listando rotas criadas e gaps de conteúdo (cada gap vira tarefa para tutorial-writer ou reference-writer). + +## Re-run +Se já existe `docs/site/`, audite drift entre `SUMMARY.md` e arquivos reais. Adicione/remova entradas sem reescrever páginas existentes. diff --git a/.claude/agents/docs-designer.md b/.claude/agents/docs-designer.md new file mode 100644 index 0000000..878e586 --- /dev/null +++ b/.claude/agents/docs-designer.md @@ -0,0 +1,48 @@ +--- +name: docs-designer +description: Identidade visual e polish do site de documentação sunscreen. Cuida do tema mdBook (paleta, tipografia, espaçamento), landing page, diagramas mermaid, code-block highlighting, dark mode, hero, badges, favicon e o feel "TMDCP-like" — premium, calmo, editorial, com hierarquia tipográfica forte. +model: opus +--- + +# Docs Designer + +## Core Role +Dono de `docs/site/theme/`, `docs/site/src/index.md` (landing) e identidade visual do site. + +## Inspiração (TMDCP-grade) +- Tipografia editorial, escala forte (h1 ~3rem, line-height generoso 1.6+). +- Paleta restrita: 1 cor de marca, 1 acento, neutros frios. Sem gradientes berrantes. +- Espaçamento amplo (max-width ~720px para prose, sidebar fixa). +- Code blocks com contraste alto, mas tema próprio (não o default mdBook). +- Hero da landing: nome + tagline + 1 frase + 2 CTAs ("Comece em 10 min" / "Ver referência"). +- Dark mode primeiro, light disponível. +- Micro-detalhes: favicon próprio, logo SVG inline, badges (crates.io, CI, license) no topo do README do repo e da landing. + +## Decisões propostas (negociáveis com `docs-architect`) +- **Fonte sans**: Inter (já carregada por mdBook variants) ou Geist (mais editorial). Default: Inter. +- **Fonte mono**: JetBrains Mono ou Geist Mono. Default: JetBrains Mono. +- **Cor primária**: ainda a definir — propor 3 paletas no `_workspace/palettes.md` e deixar usuário/orquestrador escolher antes de aplicar. +- **Logo**: gerar SVG simples (wordmark "sunscreen" com glifo abstrato — sol estilizado / escudo). + +## Entregáveis +- `docs/site/theme/css/variables.css` — override de CSS vars mdBook (`--bg`, `--fg`, `--sidebar-bg`, `--links`, etc). +- `docs/site/theme/css/general.css` — espaçamento, tipografia, hero. +- `docs/site/theme/index.hbs` — opcional, só se precisar customizar layout além de CSS. +- `docs/site/src/index.md` — landing com hero + 3 cards (Learn / Guides / Reference) + "Por que sunscreen" + footer. +- `docs/site/theme/favicon.svg` + `favicon.png`. +- `docs/site/src/assets/logo.svg`. +- Diagramas mermaid embutidos em concepts/ (coordenar com `docs-reference-writer`): arquitetura, build pipeline, plugin runtime. + +## Princípios +- **Calma > densidade**. Whitespace generoso, sem walls of text na landing. +- **Premium ≠ chamativo**. Sem animações, sem decorações sem função. +- **Acessibilidade**: contraste AA mínimo, foco visível, navegação por teclado funcional. +- **Performance**: nada de JS extra além do que mdBook traz. Sem web fonts pesadas (preload + font-display: swap). + +## I/O Protocol +- Lê: `docs-architect` (estrutura), branding existente no README. +- Escreve: arquivos acima. +- Antes de pintar tudo, escreva `_workspace/palettes.md` com 3 opções de paleta + amostra. Aguarde sinal do orquestrador antes de aplicar. + +## Re-run +Não regerar paleta sem motivo. Se tema já existe, ajustar apenas o que o usuário pediu. diff --git a/.claude/agents/docs-reference-writer.md b/.claude/agents/docs-reference-writer.md new file mode 100644 index 0000000..8ff97fe --- /dev/null +++ b/.claude/agents/docs-reference-writer.md @@ -0,0 +1,52 @@ +--- +name: docs-reference-writer +description: Escreve a trilha Reference e Concepts do site sunscreen — comandos completos, schema sunscreen.yml, recipes, plugin protocol, markers, exit codes, environment variables, NDJSON events. Audiência alvo: dev profissional Rust/Solana que quer mergulhar fundo, comparar com Anchor CLI/Solana CLI, integrar em pipelines. +model: opus +--- + +# Docs Reference Writer + +## Core Role +Dono de `docs/site/src/reference/` e `docs/site/src/concepts/`. + +## Audiência +Profissionais. Assume conhecimento de Rust idiomático, Anchor, Solana CLI. Otimize para **busca e scanning**, não para leitura linear. + +## Princípios +- **Catalogação exaustiva**: todo flag, todo exit code, todo evento NDJSON, todo campo de schema, todo erro com `code` documentado. +- **Mesma estrutura por comando**: synopsis, description, flags table, examples, exit codes, related commands. Padronização permite scanning rápido. +- **Fonte da verdade**: gere conteúdo lendo `src/cli/*.rs`, `src/config/schema.rs`, `src/error.rs`. Não invente; se algo está fora do código, marque ``. +- **Concepts explica o "porquê"**, Reference o "o quê". Concepts pode ter prose; Reference é principalmente tabelas e listas. + +## Entregáveis mínimos (Phase 8) + +### `reference/` +- `cli/index.md` — overview, exit codes globais (0=ok, 2=toolchain, 3=invalid_config, 4=user_input, 5=missing_workspace, 9=plugin_runtime), env vars `SUNSCREEN_*`, `--json` contract. +- `cli/chain.md` — `chain {new,build,serve,doctor}` com flags completos. +- `cli/scaffold.md` — primitives + recipes, flags, idempotência. +- `cli/generate.md` — `generate {clients,idl,frontend-hooks}`. +- `cli/onboarding.md` — `init`, `examples`, `quickstart`, `wallet`, `deploy`, `learn`. +- `cli/app.md` — plugin lifecycle (`install`, `commands`, `run`, `hook`, `marketplace`). +- `cli/doctor.md` — output table + `--json` schema. +- `config/schema.md` — schema completo do `sunscreen.yml`, defaults, validações, env overrides. +- `recipes/index.md` + `recipes/{crud,spl-token,metaplex-nft}.md` — composição, arquivos gerados, parâmetros. +- `plugin-protocol/index.md` — stdio JSON-RPC, manifest, gRPC contract, sandbox. +- `events.md` — eventos NDJSON emitidos por `chain build`/`chain serve`/pipeline. +- `errors.md` — tabela de erros com `code`, exit, `next_step`. +- `markers.md` — re-host ou link para `docs/reference/markers.md`. + +### `concepts/` +- `architecture.md` — diagrama da camada CLI → runtime → templates → plugins (mermaid). +- `workspace-model.md` — workspace = Cargo + Anchor.toml + `sunscreen.yml`, layout, multi-program. +- `incremental-scaffolding.md` — marcadores, idempotência, drift detection, `doctor --fix-markers`. +- `build-pipeline.md` — anchor build → IDL → Codama → frontend notify. +- `plugin-runtime.md` — quando usar plugin, sandbox, modelo de confiança. +- `framework-pinocchio-vs-anchor.md` — quando escolher cada um. + +## I/O Protocol +- Lê: `src/cli/**`, `src/config/**`, `src/error.rs`, `src/codegen/**`, `src/runtime/**`, `proto/plugin.proto`, e os docs internos existentes em `docs/reference/`. +- Escreve: arquivos `.md` na estrutura acima. +- Cada flag/erro/evento documentado cita o arquivo+símbolo de origem em comentário HTML: `` — facilita auditoria pelo `docs-reviewer`. + +## Re-run +Quando código muda, faça diff entre o catalogado e o real. Reporte deltas no `_workspace/done_docs-reference-writer.md` antes de atualizar. diff --git a/.claude/agents/docs-reviewer.md b/.claude/agents/docs-reviewer.md new file mode 100644 index 0000000..a51342b --- /dev/null +++ b/.claude/agents/docs-reviewer.md @@ -0,0 +1,53 @@ +--- +name: docs-reviewer +description: QA do site de documentação sunscreen. Verifica links quebrados, exemplos de código que não compilam, comandos que divergem do CLI real, inconsistências cross-doc, jargão sem definição na trilha Learn, e nível de leitura. Não escreve conteúdo — só reporta defeitos com root cause e arquivo/linha. +model: opus +--- + +# Docs Reviewer + +## Core Role +Auditar o site (`docs/site/`) antes do deploy. Não edita conteúdo — relata. + +## Eixos de auditoria + +### 1. Correção técnica +- Cada bloco de comando executado contra o repo: `cargo run -- --help` confere com o documentado. +- Cada exit code citado existe em `src/error.rs`. +- Cada flag documentado existe em `src/cli/**`. +- Cada arquivo gerado citado (templates, scaffold output) confere com `templates/**` e testes. + +### 2. Links +- `mdbook-linkcheck` passa sem warnings. +- Links externos (crates.io, docs.rs, solana.com) respondem 200 (amostrar, não todos). +- Âncoras internas (`#section`) existem. + +### 3. Consistência cross-doc +- Termos do glossário usados de forma consistente. +- Mesmo comando documentado em Learn e Reference não conflita. +- Exit codes / erros: mesma tabela em `reference/errors.md` e em referências locais. + +### 4. Acessibilidade da trilha Learn +- Cada novo termo aparece definido ou linkado para `glossary.md` na primeira ocorrência. +- Nível de leitura: frases curtas, voz ativa, sem dependências culturais. +- Cada tutorial cumpre o template (⏱, pré-requisitos, passos, recap, próximos passos). + +### 5. Build do site +- `mdbook build` sem warnings (exceto whitelist explícita). +- Tema renderiza em dark e light sem regressão visual óbvia (revisar 3 páginas amostra). +- Workflow `.github/workflows/docs.yml` é válido (`act` ou inspeção manual). + +## I/O Protocol +- Lê: tudo em `docs/site/`, código fonte, workflows. +- Escreve: `_workspace/docs-review.md` com: + - **Bloqueadores** (impedem deploy) — listar com arquivo:linha + root cause. + - **Defeitos não bloqueadores** — listar com prioridade P1/P2/P3. + - **Sugestões** — separadas de defeitos, sem ação obrigatória. +- Nunca silencia warnings sem aprovação. Se algo deve ser ignorado, propõe entrada em allowlist documentada. + +## Princípios +- Reporte root cause, não sintoma. "Link quebrado" → "tutorial X linka `learn/foo.md` que não existe; ou criar página, ou mover link para guides/foo.md". +- Não recomende sem dados. Se sugerir tema mudar, mostre prova (screenshot, diff de contraste WCAG). + +## Re-run +Em re-runs, compare com `_workspace/docs-review.md` anterior. Marque defeitos resolvidos, persistentes, e novos. diff --git a/.claude/agents/docs-tutorial-writer.md b/.claude/agents/docs-tutorial-writer.md new file mode 100644 index 0000000..4462dcb --- /dev/null +++ b/.claude/agents/docs-tutorial-writer.md @@ -0,0 +1,66 @@ +--- +name: docs-tutorial-writer +description: Escreve a trilha Learn e Guides do site sunscreen — quickstart, "zero-to-NFT em 10 minutos", primers de Rust e Solana, glossário, tutoriais task-oriented. Audiência alvo: desenvolvedor que nunca tocou Solana e nunca usou Rust em produção. Linguagem clara, sem jargão sem definição, com mãos-no-código a cada passo. +model: opus +--- + +# Docs Tutorial Writer + +## Core Role +Dono de `docs/site/src/learn/` e `docs/site/src/guides/`. + +## Audiência +- **Learn**: zero-base. Pode ser dev web/Python que ouviu falar em Solana. Não assuma Rust/Anchor/SPL. +- **Guides**: dev que já passou pelo Learn ou já conhece Solana, mas é novo no sunscreen. Pode pular intros. + +## Princípios +- **Definição antes de uso**: ao introduzir um termo (PDA, IDL, mint, anchor program), escreva a definição inline em 1 frase + link para `concepts/`. Nunca jargão cru. +- **Copy-paste real**: todo bloco de código deve ser copiável e funcionar. Mostre o comando, o output esperado (truncado se >20 linhas), o estado de arquivos. +- **Falhas esperadas**: documente os erros mais comuns ("se vir `toolchain_missing: anchor`, rode X"). O CLI já emite `next_step` — referencie-o. +- **Tempo declarado**: cada tutorial declara "⏱ ~10 min" no topo. +- **Um caminho feliz por tutorial**: sem ramificações. Variações vão para Guides separados. + +## Estrutura padrão de tutorial +``` +# Título orientado a resultado ("Criar seu primeiro NFT em 10 minutos") + +⏱ 10 min · 🎯 você terá: + +## Pré-requisitos +- (lista mínima, com link para instalação) + +## Passo 1: +<1 parágrafo do porquê> + + + +## Passo 2: ... + +## O que aconteceu + + +## Próximos passos +- (link para guide relacionado) +- (link para reference relacionado) +``` + +## Entregáveis mínimos (Phase 8) +- `learn/SUMMARY-intro.md` — o que é sunscreen, quando usar, comparação honesta com Anchor CLI puro. +- `learn/installing.md` — instalação cross-OS (curl installer, cargo-binstall, cargo install). +- `learn/first-workspace.md` — `chain new`, anatomia gerada, primeiro `chain build`. +- `learn/your-first-nft.md` — quickstart NFT em devnet (composição: init → scaffold metaplex-nft → deploy → mint). +- `learn/rust-primer.md` — Rust mínimo para ler programas Anchor (ownership só o suficiente, macros `#[account]`, `#[derive(Accounts)]`). +- `learn/solana-primer.md` — accounts, programs, PDAs, transactions, fee payer, devnet vs mainnet — em 5 min de leitura. +- `learn/glossary.md` — termos do ecossistema com 1-2 frases cada. +- `guides/scaffolding-crud.md` — usar `scaffold crud` para um recurso (`Post`). +- `guides/dev-loop.md` — `chain serve` end-to-end com frontend hot reload. +- `guides/deploying-to-devnet.md` / `mainnet.md` — wallet setup, airdrop, deploy, verificar on-chain. +- `guides/troubleshooting.md` — top 10 erros com fix. + +## I/O Protocol +- Lê: agentes `docs-architect` (SUMMARY.md), código real para validar comandos, `docs/reference/onboarding.md`. +- Escreve: arquivos `.md` em `docs/site/src/learn/` e `docs/site/src/guides/`. +- Antes de declarar pronto, execute mentalmente cada comando passo-a-passo contra o repo atual. Marque divergências entre tutorial e CLI real no `_workspace/done_docs-tutorial-writer.md`. + +## Re-run +Releia arquivo existente, preserve a estrutura, atualize apenas trechos divergentes. Adicione changelog no rodapé: `_Atualizado em : _`. diff --git a/.claude/skills/sunscreen-docs-orchestrator/SKILL.md b/.claude/skills/sunscreen-docs-orchestrator/SKILL.md new file mode 100644 index 0000000..172aea9 --- /dev/null +++ b/.claude/skills/sunscreen-docs-orchestrator/SKILL.md @@ -0,0 +1,93 @@ +--- +name: sunscreen-docs-orchestrator +description: Orquestra o time de documentação do CLI sunscreen — site mdBook, GitHub Pages, trilhas Learn/Guides/Reference/Concepts, identidade visual e QA de docs. Use sempre que o usuário pedir para criar, escrever, expandir, revisar, atualizar, polir, redesenhar ou publicar documentação do sunscreen — incluindo "site de docs", "GitHub Pages", "tutoriais", "quickstart", "reference", "primer", "glossário", "landing", "tema das docs", "docs bonita", "docs tipo TMDCP", "Phase 8 docs", "documentação para iniciantes", "documentação profissional", "guia", "como usar", "manual", "mdBook", "publicar docs". Não use para ADRs (esses ficam com sunscreen-orchestrator → docs-writer) nem para implementação de código. +--- + +# Sunscreen Docs Orchestrator + +Coordena 5 agentes para entregar o site de documentação do sunscreen, alvo de Phase 8 do `ROADMAP.md`. Estilo-alvo: TMDCP — premium, editorial, acessível para iniciantes e denso para profissionais. + +## Phase 0: Contexto + +1. Releia `CLAUDE.md`, `ROADMAP.md`, `README.md`, `docs/adr/ADR-0003-documentation-strategy.md` e `docs/reference/*`. +2. Liste o que já existe em `docs/site/` (se existir) e em `_workspace/`. +3. Decida modo de execução: + - `docs/site/` não existe → **execução inicial completa** (Phases 1→6). + - `docs/site/` existe + pedido específico (ex.: "atualiza reference de chain serve") → **execução parcial** (apenas o agente dono daquela área). + - `_workspace/docs-review.md` existe e tem bloqueadores → **execução de correção** (chamar o autor original do defeito). + +## Time + +| Agente | Domínio | +|--------|---------| +| `docs-architect` | `docs/site/book.toml`, `SUMMARY.md`, workflow Pages, tema base | +| `docs-tutorial-writer` | `learn/`, `guides/` | +| `docs-reference-writer` | `reference/`, `concepts/` | +| `docs-designer` | tema CSS, landing, logo, diagramas | +| `docs-reviewer` | QA cross-doc, link check, build check | + +**Execução: hybrid.** Em ambiente com subagentes, spawn em paralelo onde possível; sem subagentes, executa localmente seguindo a ordem. + +## Phases + +### Phase 1: Arquitetura (sequencial, bloqueia o resto) +**Owner**: `docs-architect`. +Gera `book.toml`, `SUMMARY.md`, esqueleto de diretórios, workflow Pages. Output: `_workspace/done_docs-architect.md` listando rotas e gaps. + +### Phase 2: Identidade visual (paralelo com Phase 3) +**Owner**: `docs-designer`. +Primeiro entrega `_workspace/palettes.md` com 3 paletas. **Orquestrador pausa e pede escolha ao usuário** (via `AskUserQuestion`) antes de aplicar tema. Depois: theme CSS, logo, favicon, landing. + +### Phase 3: Conteúdo (paralelo) +**Owners**: `docs-tutorial-writer` + `docs-reference-writer`. +Trabalham em diretórios disjuntos — sem conflito de arquivo. Cada um sinaliza pronto via `_workspace/done_.md`. + +### Phase 4: Diagramas (depende de Phase 3 + Phase 2) +**Owner**: `docs-designer` (mermaid) em colaboração com `docs-reference-writer` (conteúdo dos diagramas). +Diagramas de: arquitetura, build pipeline, plugin runtime, marker lifecycle. + +### Phase 5: Review (sequencial, depois de tudo) +**Owner**: `docs-reviewer`. +Roda auditoria completa, gera `_workspace/docs-review.md`. Se houver bloqueadores → orquestrador re-aciona os autores (loop max 2 iterações; depois reporta ao usuário). + +### Phase 6: Build & Deploy check +- `mdbook build docs/site/` local sem warnings. +- `mdbook test docs/site/` (valida snippets Rust marcados). +- Validar workflow Pages com `act` se disponível, senão inspeção manual. +- Não fazer deploy automático — reportar ao usuário "pronto para merge; Pages publicará no push em main". + +## Data flow + +- **`_workspace/`** é a área compartilhada. Cada agente escreve `done_.md` ao terminar. +- Sinalizações importantes (paleta escolhida, gaps de conteúdo) vão em arquivos dedicados (`_workspace/palettes.md`, `_workspace/content-gaps.md`). +- Site final em `docs/site/`. Não tocar em `docs/adr/` nem `docs/reference/` (esses são do harness principal — apenas linkar/republicar). + +## Error handling + +- Agente falha → 1 retry com a mensagem do erro. +- Falha persistente → reportar ao usuário com arquivo, comando, output. Não tentar contornar com edição manual genérica. +- Review encontra bloqueador → re-aciona autor original (max 2 iterações). Se persistir, deixa documentado em `docs-review.md` e segue. +- Conflito de design entre `docs-designer` e `docs-architect` → orquestrador decide a favor do `docs-architect` (estrutura > estética). + +## Relatório + +Ao terminar resuma: +- Arquivos criados em `docs/site/` agrupados por trilha (learn/guides/reference/concepts). +- Status do `mdbook build` e `mdbook-linkcheck`. +- Bloqueadores remanescentes do review. +- URL onde ficará publicado (`https://.github.io/sunscreen/`) — pedir confirmação da org. +- Próximos passos (deploy, screenshot, anúncio). + +## Re-run / pedidos parciais + +Quando o usuário pede "atualiza só X": +1. Identifique o agente dono. +2. Pule Phase 1 (arquitetura já existe). +3. Execute só o agente dono + `docs-reviewer` no final (review escopado à área alterada). +4. Atualize variação log no CLAUDE.md. + +## Não use esta skill para + +- ADRs → `sunscreen-orchestrator` (agente `docs-writer`). +- Implementação de feature de CLI → `sunscreen-orchestrator`. +- Perguntas conceituais sobre Solana sem mudança em arquivo → resposta direta. diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..07ac89a --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,78 @@ +name: docs + +on: + push: + branches: [main] + paths: + - "docs/site/**" + - ".github/workflows/docs.yml" + pull_request: + paths: + - "docs/site/**" + - ".github/workflows/docs.yml" + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: "pages" + cancel-in-progress: false + +env: + MDBOOK_VERSION: "0.4.40" + MDBOOK_ADMONISH_VERSION: "1.18.0" + MDBOOK_MERMAID_VERSION: "0.14.0" + MDBOOK_LINKCHECK_VERSION: "0.7.7" + +jobs: + build: + name: Build + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - uses: actions/checkout@v4 + + - name: Cache cargo bin + uses: actions/cache@v4 + with: + path: ~/.cargo/bin + key: mdbook-${{ env.MDBOOK_VERSION }}-${{ env.MDBOOK_ADMONISH_VERSION }}-${{ env.MDBOOK_MERMAID_VERSION }}-${{ env.MDBOOK_LINKCHECK_VERSION }} + + - name: Install mdBook toolchain + run: | + set -euo pipefail + install_if_missing() { + local crate="$1" version="$2" + if ! command -v "$crate" >/dev/null 2>&1; then + cargo install --locked --version "$version" "$crate" + fi + } + install_if_missing mdbook "$MDBOOK_VERSION" + install_if_missing mdbook-admonish "$MDBOOK_ADMONISH_VERSION" + install_if_missing mdbook-mermaid "$MDBOOK_MERMAID_VERSION" + install_if_missing mdbook-linkcheck "$MDBOOK_LINKCHECK_VERSION" + + - name: Build + run: mdbook build docs/site + + - name: Upload artifact + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + uses: actions/upload-pages-artifact@v3 + with: + path: docs/site/book/html + + deploy: + name: Deploy + needs: build + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + runs-on: ubuntu-latest + timeout-minutes: 10 + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - id: deployment + uses: actions/deploy-pages@v4 diff --git a/CLAUDE.md b/CLAUDE.md index 99338b5..92edf63 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -44,3 +44,4 @@ Greenfield Rust CLI inspired by Ignite CLI. Scope: incremental scaffolding of An | 2026-06-02 | Phase 6 CLOSED | src/plugin/*, proto/plugin.proto, src/cli/{app,scaffold}.rs, src/config/schema.rs, src/error.rs, tests/app_lifecycle.rs, docs/reference/app.md, ROADMAP.md, README.md, .github/workflows/ci.yml | Local plugin manifest discovery, stdio JSON-RPC execution, `app commands/run/hook/marketplace`, plugin-backed `scaffold `, gRPC proto contract, sandbox/trust boundaries, `plugin_runtime` exit 9, schema v1 runtime fields, and explicit CI app smoke. 366/366 tests, clippy, no-default check, and release build green. Next phase is Phase 8 distribution/docs. | | 2026-06-02 | Phase 7 CLOSED | src/cli/{chain,generate,scaffold}.rs, src/runtime/pipeline.rs, src/toolchain/preflight.rs, templates/workspace/pinocchio-minimal/, tests/{chain_new,chain_build,integration_chain,runtime_pipeline,compile_generated_workspace,golden/render_workspace}.rs, docs/{adr/ADR-0006,reference/pinocchio}.md, ROADMAP.md, README.md | `chain new --framework pinocchio` now creates a minimal Pinocchio workspace, preflight skips Anchor, `chain build` emits `pinocchio_build` and runs `cargo build-sbf`, Anchor-only scaffold/generate commands fail clearly, and offline/golden/binary tests cover the path. Next phase is Phase 8 distribution/docs. | | 2026-06-02 | v0.1.0 preview release prepared | .github/workflows/release.yml, .github/releases/v0.1.0.md, CHANGELOG.md, Cargo.toml, README.md, ROADMAP.md | Phase 8 distribution slice: tag-driven `cargo-dist` workflow for Linux/macOS archives and shell installer, crate version `0.1.0`, release notes, SemVer preview policy, and GitHub Release publishing path. | +| 2026-06-02 | Docs team harness adicionado | .claude/agents/{docs-architect,docs-tutorial-writer,docs-reference-writer,docs-designer,docs-reviewer}.md, .claude/skills/sunscreen-docs-orchestrator/ | Phase 8 docs slice: time dedicado para entregar site mdBook (GitHub Pages) com trilhas Learn/Guides/Reference/Concepts, identidade visual estilo TMDCP e QA de docs. Orquestrador separado do `sunscreen-orchestrator` — acionado por pedidos relacionados a "docs", "site", "tutoriais", "GitHub Pages", "Phase 8 docs". O agente `docs-writer` continua dono apenas de ADRs. Trigger inicial: invocar `sunscreen-docs-orchestrator`. | diff --git a/docs/site/README.md b/docs/site/README.md new file mode 100644 index 0000000..448e30a --- /dev/null +++ b/docs/site/README.md @@ -0,0 +1,38 @@ +# Sunscreen documentation site + +This is the source for , built with [mdBook](https://rust-lang.github.io/mdBook/). + +## Local preview + +```bash +# Install the toolchain (one-time) +cargo install mdbook mdbook-admonish mdbook-mermaid mdbook-linkcheck + +# Serve at http://localhost:3000 with live reload +mdbook serve docs/site --open +``` + +## Build + +```bash +mdbook build docs/site +# Output: docs/site/book/html/ +``` + +## Adding a page + +1. Create the `.md` file under `docs/site/src//`. +2. Add an entry in `docs/site/src/SUMMARY.md` under the matching section. +3. `mdbook serve` will pick it up immediately. + +## Conventions + +- One topic per page. +- `Learn` pages define every term on first use; link to `concepts/` for depth. +- `Reference` pages are scannable: synopsis → flags table → examples → exit codes. +- Code blocks must be copy-paste runnable against the current `main` branch. +- Diagrams use Mermaid. Embed with a ` ```mermaid ` fenced block. + +## Deploy + +Pushes to `main` that touch `docs/site/**` trigger `.github/workflows/docs.yml`, which publishes to the `gh-pages` branch. diff --git a/docs/site/book.toml b/docs/site/book.toml new file mode 100644 index 0000000..011c5d1 --- /dev/null +++ b/docs/site/book.toml @@ -0,0 +1,61 @@ +[book] +title = "Sunscreen" +description = "A Rust CLI for scaffolding, repairing, and orchestrating Solana Anchor and Pinocchio workspaces." +authors = ["The Sunscreen Contributors"] +language = "en" +multilingual = false +src = "src" + +[rust] +edition = "2021" + +[build] +build-dir = "book" +create-missing = false + +[preprocessor.admonish] +command = "mdbook-admonish" +assets_version = "3.0.2" + +[preprocessor.mermaid] +command = "mdbook-mermaid" + +[output.html] +default-theme = "sunscreen-dark" +preferred-dark-theme = "sunscreen-dark" +git-repository-url = "https://github.com/Pantani/sunscreen" +git-repository-icon = "fa-github" +edit-url-template = "https://github.com/Pantani/sunscreen/edit/main/docs/site/{path}" +site-url = "/sunscreen/" +cname = "" +additional-css = [ + "theme/css/variables.css", + "theme/css/general.css", + "theme/css/admonish.css", +] +additional-js = ["theme/js/mermaid-init.js"] +no-section-label = false +mathjax-support = false +copy-fonts = true + +[output.html.fold] +enable = true +level = 1 + +[output.html.print] +enable = true + +[output.html.search] +enable = true +limit-results = 30 +use-boolean-and = true +boost-title = 2 +boost-hierarchy = 1 +boost-paragraph = 1 +expand = true +heading-split-level = 3 + +[output.linkcheck] +follow-web-links = false +warning-policy = "error" +exclude = ["github\\.com/Pantani/sunscreen/edit/main/.*"] diff --git a/docs/site/src/SUMMARY.md b/docs/site/src/SUMMARY.md new file mode 100644 index 0000000..cc17825 --- /dev/null +++ b/docs/site/src/SUMMARY.md @@ -0,0 +1,61 @@ + + +# Summary + +[Introduction](./index.md) + +--- + +# Learn + +- [What is sunscreen?](./learn/what-is-sunscreen.md) +- [Installing](./learn/installing.md) +- [Your first workspace](./learn/first-workspace.md) +- [Your first NFT](./learn/your-first-nft.md) +- [Rust primer](./learn/rust-primer.md) +- [Solana primer](./learn/solana-primer.md) +- [Glossary](./learn/glossary.md) + +# Guides + +- [Scaffolding a CRUD resource](./guides/scaffolding-crud.md) +- [The dev loop with `chain serve`](./guides/dev-loop.md) +- [Deploying to devnet](./guides/deploying-to-devnet.md) +- [Deploying to mainnet](./guides/deploying-to-mainnet.md) +- [Working with plugins](./guides/plugins.md) +- [Troubleshooting](./guides/troubleshooting.md) + +# Reference + +- [CLI overview](./reference/cli/index.md) + - [`chain`](./reference/cli/chain.md) + - [`scaffold`](./reference/cli/scaffold.md) + - [`generate`](./reference/cli/generate.md) + - [`app`](./reference/cli/app.md) + - [`doctor`](./reference/cli/doctor.md) + - [Onboarding commands](./reference/cli/onboarding.md) +- [Configuration](./reference/config/schema.md) +- [Recipes](./reference/recipes/index.md) + - [CRUD](./reference/recipes/crud.md) + - [SPL Token](./reference/recipes/spl-token.md) + - [Metaplex NFT](./reference/recipes/metaplex-nft.md) +- [Plugin protocol](./reference/plugin-protocol/index.md) +- [NDJSON events](./reference/events.md) +- [Errors & exit codes](./reference/errors.md) +- [Marker protocol](./reference/markers.md) + +# Concepts + +- [Architecture](./concepts/architecture.md) +- [Workspace model](./concepts/workspace-model.md) +- [Incremental scaffolding](./concepts/incremental-scaffolding.md) +- [Build pipeline](./concepts/build-pipeline.md) +- [Plugin runtime](./concepts/plugin-runtime.md) +- [Anchor vs Pinocchio](./concepts/framework-pinocchio-vs-anchor.md) + +# Contributing + +- [Roadmap](./contributing/roadmap.md) +- [Architecture decisions](./contributing/adrs.md) +- [Developer setup](./contributing/dev-setup.md) +- [Documentation style](./contributing/docs-style.md) diff --git a/docs/site/src/assets/logo.svg b/docs/site/src/assets/logo.svg new file mode 100644 index 0000000..af07e4a --- /dev/null +++ b/docs/site/src/assets/logo.svg @@ -0,0 +1,8 @@ + + + + + + + sunscreen + diff --git a/docs/site/src/concepts/architecture.md b/docs/site/src/concepts/architecture.md new file mode 100644 index 0000000..56908a6 --- /dev/null +++ b/docs/site/src/concepts/architecture.md @@ -0,0 +1,70 @@ +# Architecture + +Sunscreen is organized in four layers. Each one has a single responsibility and a clean interface to the next. + +```mermaid +flowchart TB + User([User]) --> CLI[CLI surface
clap commands] + CLI --> Runtime[Runtime layer
watcher · supervisor · pipeline] + CLI --> Templates[Templates engine
rust-embed · minijinja] + CLI --> Plugins[Plugin runtime
stdio JSON-RPC · sandbox] + Runtime --> Tools[(External tools
anchor · cargo-build-sbf
codama · surfpool)] + Templates --> Workspace[(Workspace files
programs/ · app/
sunscreen.yml)] + Plugins --> Workspace + Runtime --> Workspace +``` + +## Layer 1 — CLI surface + +[`src/cli/`](https://github.com/Pantani/sunscreen/tree/main/src/cli). One module per top-level command (`chain.rs`, `scaffold.rs`, `generate.rs`, `app.rs`, `onboarding/`). Argument parsing via [`clap`](https://docs.rs/clap), exit codes from a single [`error.rs`](https://github.com/Pantani/sunscreen/blob/main/src/error.rs). + +The CLI layer is thin: it parses, validates, then hands off to the runtime or templates layers. + +## Layer 2 — Runtime + +[`src/runtime/`](https://github.com/Pantani/sunscreen/tree/main/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`) — `notify` events, debounced, dedup'd, filtered. +- **Validator adapters** (`surfpool.rs`, `testvalidator.rs`) — abstracted behind a `LocalValidator` trait. +- **Supervisor** (`supervisor.rs`, `serve.rs`) — long-running orchestrator for `chain serve`. + +The runtime never reads CLI args directly. It receives configured `*Spec` structs. + +## Layer 3 — Templates + +[`src/templates/`](https://github.com/Pantani/sunscreen/tree/main/src/templates) + [`templates/`](https://github.com/Pantani/sunscreen/tree/main/templates) (embedded via `rust-embed`). + +Two responsibilities: + +- **Scaffold workspaces and primitives** with deterministic [`minijinja`](https://docs.rs/minijinja) rendering. Golden-tested. +- **Patch existing files** through [`src/rustpatch/`](https://github.com/Pantani/sunscreen/tree/main/src/rustpatch) — the marker engine. Line-based, AST-free, resilient to malformed input. + +## Layer 4 — Plugin runtime + +[`src/plugin/`](https://github.com/Pantani/sunscreen/tree/main/src/plugin). Discovers plugins from `sunscreen.yml`, validates manifests, opens a stdio JSON-RPC session, enforces the sandbox, and routes `scaffold ` 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/`](https://github.com/Pantani/sunscreen/tree/main/src/config) — schema, loader, migrations | +| Toolchain detection | [`src/toolchain/`](https://github.com/Pantani/sunscreen/tree/main/src/toolchain) — uniform `ToolReport` for every external tool | +| Errors | [`src/error.rs`](https://github.com/Pantani/sunscreen/blob/main/src/error.rs) — single `Error` enum, `code` + `next_step` | +| TUI (chain serve) | [`src/tui/serve_model.rs`](https://github.com/Pantani/sunscreen/blob/main/src/tui/serve_model.rs) | + +## Design principles + +1. **Determinism**. Same inputs → same outputs. Golden tests cover scaffolds; round-trip tests cover marker patches. +2. **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. +3. **Two-layer plugin model**. JSON-RPC over stdio is the floor; gRPC is the ceiling. Both share the same logical contract. +4. **CLI is the API**. `--json` output is part of stability. Editor integrations don't need an SDK. + +## See also + +- [Build pipeline](./build-pipeline.md) — what runs in what order. +- [Incremental scaffolding](./incremental-scaffolding.md) — markers in depth. +- [Plugin runtime](./plugin-runtime.md) — sandbox and trust. diff --git a/docs/site/src/concepts/build-pipeline.md b/docs/site/src/concepts/build-pipeline.md new file mode 100644 index 0000000..312039b --- /dev/null +++ b/docs/site/src/concepts/build-pipeline.md @@ -0,0 +1,92 @@ +# Build pipeline + +`sunscreen chain build` (and the build phase of `chain serve`) runs a deterministic pipeline of stages. Each stage is gated on the previous one's success. + +## Stages + +```mermaid +flowchart LR + A[anchor build
or cargo build-sbf] --> B{exit 0?} + B -- no --> X[emit build_fail
exit non-zero] + B -- yes --> C[IDL export
idl/*.json] + C --> D{frontend declared?} + D -- no --> Z[emit build_ok
exit 0] + D -- yes --> E[codama run
app/src/clients/] + E --> F{exit 0?} + F -- no --> Y[emit codama_fail
exit non-zero] + F -- yes --> G[touch app/.sunscreen/reload] + G --> Z +``` + +## Stage 1 — Compile + +- **Anchor workspaces**: `anchor build`. Produces `target/idl/.json` and `target/deploy/.so` per program. +- **Pinocchio workspaces**: `cargo build-sbf`. Produces only the `.so` (Pinocchio has no IDL emission yet). + +Failure → emit `build_fail`, return Anchor's exit code (sunscreen preserves it). + +## Stage 2 — IDL export + +For Anchor workspaces, sunscreen copies the IDL into `idl/.json` (normalized, sorted, byte-deterministic). This is the file Codama and your CI consume. + +Skipped for Pinocchio. + +## Stage 3 — Codama + +Runs only if `frontend != none` in `sunscreen.yml`, and `--no-codama` was not passed. + +Sunscreen manages a `codama.config.mjs` in the workspace root, pointing at the exported IDLs and the configured `app/src/clients/` output. The command is: + +```bash +pnpm exec codama run +``` + +Failure → emit `codama_fail`. Sunscreen does *not* roll back the IDL (the partial state is recoverable on the next successful build). + +## Stage 4 — Frontend notify + +On success of stage 3, sunscreen touches `app/.sunscreen/reload`. Frontend dev servers (Vite, Next) watching this file trigger HMR. + +If `frontend != none` but you don't have a dev server running, this is a harmless no-op. + +## NDJSON event sequence (success case) + +```json +{"event":"build_start","framework":"anchor","programs":["my_app"]} +{"event":"build_progress","step":"anchor_build"} +{"event":"build_ok","programs":["my_app"],"duration_ms":4200} +{"event":"codama_start","frontend":"react"} +{"event":"codama_ok","files_written":14,"duration_ms":850} +{"event":"frontend_notified","path":"app/.sunscreen/reload"} +``` + +## `chain serve` extras + +In `serve`, the pipeline runs every time the watcher emits a debounced batch: + +```mermaid +sequenceDiagram + participant FS as Filesystem + participant W as Watcher + participant P as Pipeline + + FS->>W: save event(s) + Note over W: debounce ~200ms + W->>P: run(paths) + P->>P: stages 1..4 + P-->>W: result + Note over W: ready for next batch +``` + +## Why this shape? + +- **Compile must precede everything.** No IDL → nothing else can run. +- **IDL is the protocol boundary.** Once written, every downstream tool consumes it. Sunscreen does not feed Codama from in-memory IDL objects — the file *is* the contract. +- **Codama is optional.** Library projects without a frontend skip it. CI flags `--no-codama` to keep builds fast. +- **Frontend notify is the last step.** Any failure earlier and the frontend keeps showing the previous client. + +## See also + +- [`chain build` reference](../reference/cli/chain.md#build) +- [NDJSON events](../reference/events.md) +- [Architecture](./architecture.md) diff --git a/docs/site/src/concepts/framework-pinocchio-vs-anchor.md b/docs/site/src/concepts/framework-pinocchio-vs-anchor.md new file mode 100644 index 0000000..dd7abed --- /dev/null +++ b/docs/site/src/concepts/framework-pinocchio-vs-anchor.md @@ -0,0 +1,52 @@ +# Anchor vs Pinocchio + +Sunscreen supports two Solana program frameworks. Choose based on what you're optimizing for. + +## TL;DR + +| | Anchor | Pinocchio | +|---|---|---| +| Mental model | High-level, macros do the heavy lifting | Low-level, you wire account checks yourself | +| Compile-time guarantees | Strong (`#[derive(Accounts)]` checks at build time) | Minimal (you assert at runtime) | +| Compute units | Higher overhead | Near-bare-metal | +| Code volume | Less code per instruction | More code per instruction | +| Sunscreen support | Full (scaffold, codegen, recipes, plugins) | Workspace bootstrap + build only | +| When to pick | New projects, productivity-first | Performance-critical paths, audit-sensitive code | + +## Anchor in one paragraph + +[Anchor](https://www.anchor-lang.com/) is a Rust framework on top of the Solana program SDK. It defines three macros (`#[program]`, `#[derive(Accounts)]`, `#[account]`) that hide most of the account-handling boilerplate. It also auto-generates an IDL, which sunscreen feeds into Codama for client generation. Default choice for productivity. + +## Pinocchio in one paragraph + +[Pinocchio](https://github.com/anza-xyz/pinocchio) is a thin, no-allocation alternative for Solana programs. It exposes the runtime more directly, so you pay fewer compute units (CUs) per instruction. There's no IDL emission, no codegen, no Anchor macros — you write the dispatch and account validation by hand. Use for programs where CU budget matters. + +## Sunscreen's coverage today + +| Feature | Anchor | Pinocchio | +|---------|--------|-----------| +| `chain new` | ✅ | ✅ (minimal template) | +| `chain build` | ✅ | ✅ (`cargo build-sbf`) | +| `chain serve` | ✅ | ✅ | +| `scaffold instruction/account/event/error` | ✅ | ❌ (manual) | +| `scaffold crud/spl-token/metaplex-nft` | ✅ | ❌ | +| `generate idl` | ✅ | ❌ (no IDL) | +| `generate clients` | ✅ | ❌ | +| `generate frontend-hooks` | ✅ | ❌ | +| Plugin `scaffold ` | ✅ | partial (plugins decide) | + +If you choose Pinocchio, sunscreen still gives you the workspace bootstrap, build, and local dev loop. Scaffolders and codegen require Anchor's IDL. + +## Migrating between + +There's no automatic migration. Anchor programs use macros and account layouts that don't map 1:1 to Pinocchio. If you want to migrate a hot path to Pinocchio, the typical pattern is: + +1. Keep the Anchor program as the user-facing surface. +2. Extract the hot instruction into a separate Pinocchio program. +3. Have the Anchor program CPI into the Pinocchio program. + +## See also + +- [`chain new --framework`](../reference/cli/chain.md#new) — choosing at creation time. +- [Anchor docs](https://www.anchor-lang.com/). +- [Pinocchio repo](https://github.com/anza-xyz/pinocchio). diff --git a/docs/site/src/concepts/incremental-scaffolding.md b/docs/site/src/concepts/incremental-scaffolding.md new file mode 100644 index 0000000..962f0a8 --- /dev/null +++ b/docs/site/src/concepts/incremental-scaffolding.md @@ -0,0 +1,59 @@ +# Incremental scaffolding + +The problem: you scaffold a program, then add an instruction six weeks later. You don't want sunscreen to regenerate `lib.rs` from scratch — that would clobber the body you wrote. You also don't want to maintain a hand-edited list of dispatch entries. + +The solution: **markers**. + +## Lifecycle + +```mermaid +sequenceDiagram + participant U as User + participant S as Sunscreen + participant F as File (lib.rs) + + Note over F: initial state — markers present, empty + U->>S: scaffold instruction Foo + S->>F: read + S->>F: inject pub fn foo into "dispatch" marker + S->>F: write + Note over F: dispatch marker now contains foo handler + + U->>F: hand-edit outside markers + Note over F: your edits live below the marker + + U->>S: scaffold instruction Bar + S->>F: read + S->>F: inject pub fn bar into "dispatch" marker
(your hand-edits untouched) + S->>F: write +``` + +## Three guarantees + +1. **Idempotency**. Re-running with the same args is a no-op. Sunscreen detects "this symbol already exists in the marker" and exits `4` unless `--force` is passed. +2. **Marker isolation**. Content outside markers is never touched. Content inside markers may be fully regenerated on every run. +3. **Drift detection**. `chain doctor` finds missing or duplicated markers and reports them. `chain doctor --fix-markers` repairs *safe* drift (it refuses to repair sites where reconstruction would be ambiguous). + +## Where markers live + +Sunscreen places markers in files it scaffolds. As of v0.1.0: + +| File | Marker tag | +|------|-----------| +| `programs/*/src/lib.rs` | `dispatch` | +| `programs/*/src/instructions/mod.rs` | `instructions` | +| `programs/*/src/state/mod.rs` | `state` | +| `programs/*/src/events.rs` | `events` | +| `programs/*/src/errors.rs` | `errors` | +| Any error enum inside `errors.rs` | `error_variants` (inside the enum) | + +## Why not AST-based? + +We tried. `syn`-based patching is brittle when the surrounding code is malformed (e.g. mid-edit, half-typed). Line-based marker editing tolerates anything as long as the marker pair is intact, and merges cleanly with editor undo histories. + +The cost: you must keep the markers. `chain doctor` will remind you. + +## Read also + +- [Marker protocol reference](../reference/markers.md) — exact syntax, generator tags, repair rules. +- [`chain doctor`](../reference/cli/chain.md#doctor) — repair command. diff --git a/docs/site/src/concepts/plugin-runtime.md b/docs/site/src/concepts/plugin-runtime.md new file mode 100644 index 0000000..b5d9278 --- /dev/null +++ b/docs/site/src/concepts/plugin-runtime.md @@ -0,0 +1,96 @@ +# Plugin runtime + +Sunscreen lets you extend the `scaffold ` surface via external binaries. The runtime is intentionally minimal: stdio JSON-RPC, a small set of capabilities, an explicit sandbox. + +## Why plugins? + +The CLI cannot ship every scaffold the world needs. Teams have internal conventions, third-party libraries have idiomatic shapes, indexer providers have their own templates. Plugins let those live close to where they're maintained, without forking sunscreen. + +## Lifecycle + +```mermaid +sequenceDiagram + participant User + participant Sunscreen + participant Plugin + + User->>Sunscreen: app install ./plugins/foo --version 0.2.0 + Sunscreen->>Sunscreen: write entry to sunscreen.yml + Note over Sunscreen: status: declared + + User->>Sunscreen: scaffold indexer Trades + Sunscreen->>Plugin: spawn binary in sandbox + Sunscreen->>Plugin: stdio: {method:"commands"} + Plugin-->>Sunscreen: {scaffold:{indexer:"…"}} + Sunscreen->>Plugin: stdio: {method:"run",params:{...}} + Plugin-->>Sunscreen: {files_written:[…]} + Sunscreen->>User: summary +``` + +## Trust model + +Plugins are arbitrary code. Sunscreen assumes nothing about a plugin's intent and enforces capabilities at the sandbox layer: + +| Capability | Default | Opt-in mechanism | +|------------|---------|------------------| +| Read inside workspace | allowed | always on | +| Write inside workspace | denied | `permissions.workspace_write = true` in manifest | +| Read outside workspace | denied | declare each path in `permissions.declared_paths[]` | +| Network | denied | `permissions.network = true` + user approval at install | +| Subprocess spawn | denied | not toggleable in current version | + +Violations terminate the session and return exit `9` (`plugin_runtime.sandbox`). + +## Discovery + +Plugins are not magic. They live in `sunscreen.yml`: + +```yaml +plugins: + - source: ./plugins/foo # local + version: "0.2.0" + - source: github.com/org/bar.git # remote (download not implemented yet) + version: "1.0.0" +``` + +Sunscreen reads this on every command and refreshes its plugin registry. + +## JSON-RPC contract + +Three methods over line-delimited stdin/stdout: + +- `commands` — returns the plugin's command map. +- `run` — invokes a single command with args, flags, and workspace context. +- `hook` — fires a lifecycle hook (`before_build`, `after_build`, …). + +See [Plugin protocol reference](../reference/plugin-protocol/index.md) for full schemas. + +## gRPC + +`proto/plugin.proto` defines a streaming-friendly equivalent. Useful for plugins that need: + +- Bidirectional streaming (live progress, log forwarding). +- Non-stdio runtimes (JVM, .NET). + +The proto is stable; the gRPC transport is not yet end-to-end live in sunscreen's runtime. + +## Reference plugins + +[`sunscreen-apps/`](https://github.com/Pantani/sunscreen-apps) contains canonical examples: + +- `spl-token-2022` — adds `scaffold spl-token-2022 ` with the 2022-extension features. +- `yellowstone-indexer` — adds `scaffold indexer ` wiring a Yellowstone gRPC consumer. + +Read their code to learn the protocol from a working example. + +## When *not* to write a plugin + +- The scaffold is generic enough to belong in core (open a PR). +- You only need a small variation on an existing recipe — consider a `--flag` instead. +- Your team can just copy-paste a snippet (don't over-engineer). + +## See also + +- [Plugin protocol reference](../reference/plugin-protocol/index.md) — wire format. +- [`app` command reference](../reference/cli/app.md) — operate on plugins. +- [Working with plugins guide](../guides/plugins.md) — install/run walkthrough. diff --git a/docs/site/src/concepts/workspace-model.md b/docs/site/src/concepts/workspace-model.md new file mode 100644 index 0000000..cfede62 --- /dev/null +++ b/docs/site/src/concepts/workspace-model.md @@ -0,0 +1,92 @@ +# Workspace model + +A sunscreen workspace is a directory containing four things: + +1. A `sunscreen.yml` at the root. +2. A Rust workspace `Cargo.toml`. +3. An `Anchor.toml` (for Anchor) or nothing extra (for Pinocchio). +4. One or more programs under `programs//`. + +Optional: + +- A frontend under `app/` (React or Solid). +- A `plugins/` directory with local plugins. +- A `tests/` directory with TypeScript integration tests. + +## Anchor layout + +```text +my-app/ +├── sunscreen.yml +├── Anchor.toml +├── Cargo.toml ← workspace member list +├── package.json ← TypeScript test dependencies +├── tsconfig.json +├── programs/ +│ ├── core/ +│ │ ├── Cargo.toml +│ │ └── src/ +│ │ ├── lib.rs ← #[program], dispatch markers +│ │ ├── state/ +│ │ ├── instructions/ +│ │ ├── events.rs +│ │ └── errors.rs +│ └── governance/ ← multi-program workspaces are supported +├── app/ ← frontend (when --frontend != none) +│ ├── package.json +│ ├── vite.config.ts +│ ├── src/ +│ │ ├── clients/ ← Codama-generated +│ │ ├── hooks/ ← from generate frontend-hooks +│ │ └── App.tsx +│ └── .sunscreen/ +│ └── reload ← touch trigger for frontend HMR +└── tests/ ← TypeScript integration tests +``` + +## Pinocchio layout + +```text +bare/ +├── sunscreen.yml +├── Cargo.toml +└── programs/ + └── bare/ + ├── Cargo.toml ← `cargo build-sbf` target + └── src/ + ├── lib.rs ← entrypoint! macro, instruction dispatch + └── instructions/ +``` + +Pinocchio workspaces are minimal: no `Anchor.toml`, no built-in IDL. Codegen and recipes are Anchor-only today. + +## Multi-program workspaces + +A workspace can declare multiple programs. Sunscreen treats each one independently for `scaffold`, but bundles them for `build`, `deploy`, `serve`: + +```yaml +programs: + - name: core + path: programs/core + - name: governance + path: programs/governance + - name: treasury + path: programs/treasury +``` + +`sunscreen chain build` compiles all three. `sunscreen scaffold instruction Foo --program governance` targets only the named one. + +## Discovery + +When you invoke any workspace-scoped command, sunscreen walks up the directory tree from cwd looking for `sunscreen.yml`. Found → that's your workspace root. Not found → exit `5` (`missing_workspace`). + +You can override with `SUNSCREEN_CONFIG=/path/to/sunscreen.yml`. + +## Lock files + +Sunscreen does not write its own lock file. The Anchor/Cargo/pnpm lock files are the source of truth for dependency versions. Sunscreen's templates pin minor versions in `Cargo.toml`; you control the rest. + +## See also + +- [`sunscreen.yml` schema](../reference/config/schema.md) +- [Incremental scaffolding](./incremental-scaffolding.md) diff --git a/docs/site/src/contributing/adrs.md b/docs/site/src/contributing/adrs.md new file mode 100644 index 0000000..00667f0 --- /dev/null +++ b/docs/site/src/contributing/adrs.md @@ -0,0 +1,34 @@ +# Architecture decisions + +Sunscreen's design rationale lives in Architecture Decision Records (ADRs) in [`docs/adr/`](https://github.com/Pantani/sunscreen/tree/main/docs/adr). + +## Current ADRs + +| ID | Title | Status | +|----|-------|--------| +| [ADR-0001](https://github.com/Pantani/sunscreen/blob/main/docs/adr/ADR-0001-solis-cli.md) | The sunscreen CLI: scope, vision, principles | Accepted | +| [ADR-0002](https://github.com/Pantani/sunscreen/blob/main/docs/adr/ADR-0002-cli-design-conventions.md) | CLI design conventions (exit codes, flags, JSON contract) | Accepted | +| [ADR-0003](https://github.com/Pantani/sunscreen/blob/main/docs/adr/ADR-0003-documentation-strategy.md) | Documentation strategy | Accepted | +| [ADR-0004](https://github.com/Pantani/sunscreen/blob/main/docs/adr/ADR-0004-incremental-scaffolding.md) | Incremental scaffolding (markers) | Accepted | +| [ADR-0005](https://github.com/Pantani/sunscreen/blob/main/docs/adr/ADR-0005-beginner-onboarding.md) | Beginner onboarding surface | Accepted | +| [ADR-0006](https://github.com/Pantani/sunscreen/blob/main/docs/adr/ADR-0006-pinocchio-bootstrap.md) | Pinocchio bootstrap | Accepted | + +## ADR format + +Each ADR has: + +- A meta table (status, date, authors, tags, supersedes, related). +- TL;DR. +- Context. +- Decision drivers. +- Considered options. +- Decision. +- Consequences. + +Template: see ADR-0001. + +## Writing a new ADR + +Open a PR adding `docs/adr/ADR-NNNN-.md`, status `Proposed`. Discuss in the PR. When merged, status flips to `Accepted`. If a later ADR overrides it, set `Status: Superseded by ADR-NNNN` rather than deleting. + +ADRs document *why we chose what we chose*, not *how to use the code*. For user-facing docs, contribute to this site instead. diff --git a/docs/site/src/contributing/dev-setup.md b/docs/site/src/contributing/dev-setup.md new file mode 100644 index 0000000..05c6a03 --- /dev/null +++ b/docs/site/src/contributing/dev-setup.md @@ -0,0 +1,71 @@ +# Developer setup + +How to clone, build, and test sunscreen itself. + +## Pre-requisites + +- Rust stable (matches `rust-toolchain.toml` if present). +- For the integration tests that exercise Anchor: `anchor`, `solana`, `cargo-build-sbf`, `pnpm`, Node 18+. +- The CI runner does *not* need Anchor/Solana — Anchor-touching tests are `#[ignore]` by default and explicitly gated. + +## Clone and build + +```bash +git clone https://github.com/Pantani/sunscreen.git +cd sunscreen +cargo build --locked --release --all-features +``` + +The release binary lands at `target/release/sunscreen`. Symlink it onto your PATH if you want to use the locally-built version. + +## Run the test suite + +```bash +# Fast unit + golden + integration smoke (no Anchor toolchain required) +cargo test --locked --all --all-features --no-fail-fast + +# Feature-gate check (`--no-default-features` must compile) +cargo check --locked --no-default-features --all-targets + +# Lints +cargo fmt --all -- --check +cargo clippy --locked --all-targets --all-features -- -D warnings +``` + +## Anchor-touching tests + +These are ignored by default. To run them you need the full Solana stack: + +```bash +cargo test --locked --test integration_anchor -- --include-ignored +``` + +If your toolchain is missing parts, the tests skip with a clear message rather than fail. + +## Editor + +Any editor that speaks `rust-analyzer` works. The repo includes no editor-specific config — open the workspace root and rust-analyzer figures out the rest. + +## Pre-commit + +The repo expects `cargo fmt` + `cargo clippy --no-warnings` before commits. CI fails the PR otherwise. + +## Where to start + +- [`src/cli/`](https://github.com/Pantani/sunscreen/tree/main/src/cli) — to add a flag or subcommand. +- [`src/templates/`](https://github.com/Pantani/sunscreen/tree/main/src/templates) + [`templates/`](https://github.com/Pantani/sunscreen/tree/main/templates) — to change scaffold output. +- [`src/runtime/`](https://github.com/Pantani/sunscreen/tree/main/src/runtime) — for build/serve pipeline behavior. +- [`tests/golden/`](https://github.com/Pantani/sunscreen/tree/main/tests/golden) — snapshot tests for scaffolds. + +## Docs + +To preview this site locally: + +```bash +cargo install mdbook mdbook-admonish mdbook-mermaid mdbook-linkcheck +mdbook serve docs/site --open +``` + +## Releases + +Releases are tag-driven: push `vX.Y.Z`, the `release.yml` workflow builds and publishes binaries. The docs site auto-deploys from `main` via `.github/workflows/docs.yml`. diff --git a/docs/site/src/contributing/docs-style.md b/docs/site/src/contributing/docs-style.md new file mode 100644 index 0000000..6c7ffe1 --- /dev/null +++ b/docs/site/src/contributing/docs-style.md @@ -0,0 +1,86 @@ +# Documentation style + +Rules of thumb for writing pages on this site. Apply them as defaults; break them when the topic actually needs it. + +## Tracks have purposes + +- **Learn** — teaches concepts to newcomers. Defines every term on first use. Linear reading. +- **Guides** — solves one specific task. Has a tangible artifact at the end. Time-boxed (declare `⏱`). +- **Reference** — catalogs. Optimized for `Ctrl-F`. Tables and lists over prose. +- **Concepts** — explains *why*. The model behind the code. Pairs with reference pages. + +Don't blur tracks. If a page is doing two jobs, split it. + +## Tone + +- **Plain, present, active.** "Sunscreen runs `anchor build`." Not "It is the case that sunscreen will run `anchor build`." +- **Second person for tutorials and guides.** "You'll have a workspace." +- **Third person for reference.** "The command returns exit 4 when…" +- **Honest.** If a feature is partial, say so. If a step might fail, say what to do. + +## Length + +- Aim for short sentences. Long sentences are usually two short ones in a trenchcoat. +- A Learn page: 200–500 lines, paced with subheads every ~50 lines. +- A Reference page: as long as needed; structure with tables, not prose. +- A Concept page: 100–300 lines with a diagram if it helps. + +## Code blocks + +Every code block on a Learn or Guide page must run against `main` exactly as written. Pre-commit: run the command yourself, paste the output. + +For Reference, code blocks may show the *shape* of input/output without being directly runnable — but call that out (e.g. "request body shape:"). + +## Diagrams + +Use [Mermaid](https://mermaid.js.org/) for diagrams. Embed inline: + +```` +```mermaid +flowchart LR + A --> B +``` +```` + +Avoid screenshots. Mermaid renders cleanly in dark and light, scales, and stays editable. + +## Admonitions + +Use sparingly. They draw attention; over-use teaches readers to skip them. + +``` +::: tip +For when there's a non-obvious helpful shortcut. +::: + +::: warning +For when something can damage state or burn money. +::: + +::: danger +For irreversible operations. +::: +``` + +## Links + +- Link to other docs on first mention of a concept: `[Marker protocol](../reference/markers.md)`. +- Use repo links (`github.com/Pantani/sunscreen/...`) sparingly — they go stale faster than internal links. Prefer internal docs when possible. +- Never link to "click here" — always link the noun. + +## Headings + +- One `H1` per page (the title). +- `H2` for major sections. +- `H3` sparingly. If you need `H4`, the section probably wants splitting. + +## What not to write + +- Don't describe the *implementation* of a command — describe the *contract*. Implementation belongs in code comments or ADRs. +- Don't include a "Conclusion" or "Summary" section. The page is the conclusion. +- Don't include a date or version-of-writing in the page body. Versions live in `CHANGELOG.md`; the docs track `main`. +- Don't apologize ("This is a quick guide…"). Just give the content. + +## When in doubt + +Look at a sibling page in the same track. Match its rhythm. diff --git a/docs/site/src/contributing/roadmap.md b/docs/site/src/contributing/roadmap.md new file mode 100644 index 0000000..98d2d84 --- /dev/null +++ b/docs/site/src/contributing/roadmap.md @@ -0,0 +1,47 @@ +# Roadmap + +The canonical, living roadmap is [`ROADMAP.md`](https://github.com/Pantani/sunscreen/blob/main/ROADMAP.md) at the repo root. It is the source of truth for what's done, in progress, and queued. + +This page summarizes the high-level shape so you can orient quickly. + +## Current state + +| Phase | Status | +|-------|--------| +| 0 — Foundation (CLI shell, config, toolchain, templates, error) | ✅ closed | +| 1 — Workspace bootstrap (`chain new` for Anchor + frontend variants) | ✅ closed | +| 2 — Incremental scaffolders (`scaffold instruction/account/event/error/program` + `doctor --fix-markers`) | ✅ closed | +| 3 — Build/serve pipeline + watcher + runtime supervisor | ✅ closed | +| 4 — Codegen (`generate clients/idl/frontend-hooks`) | ✅ closed | +| 5 — Recipes (`scaffold crud/spl-token/metaplex-nft`) | ✅ closed | +| 5.5 — Onboarding surface (`init`, `quickstart`, `wallet`, `deploy`, `learn`) | ✅ closed | +| 6 — Plugin runtime (manifest, JSON-RPC, sandbox, marketplace) | ✅ closed | +| 7 — Pinocchio bootstrap (`chain new --framework pinocchio` + build) | ✅ closed | +| **8 — Distribution & docs (this site, completions, Homebrew, Windows)** | **in progress** | + +## Phase 8 work items + +- ✅ tag-driven `cargo-dist` release pipeline (Linux + macOS). +- ✅ documentation site (this site). +- ⏳ shell completions (`bash`, `zsh`, `fish`, `pwsh`). +- ⏳ Homebrew tap. +- ⏳ Windows artifact (`cargo-dist` matrix). +- ⏳ `cargo dist plan` CI verification. +- ⏳ `cargo-binstall` support. + +## After v1.0 + +- Remote plugin artifact download. +- Pinocchio-native scaffolders. +- Richer marketplace (signed plugins, search). +- Codama provider abstraction (alternative codegen backends). + +## Semantic versioning + +`v0.x` is a preview line. Breaking changes can happen between minor versions, always called out in [`CHANGELOG.md`](https://github.com/Pantani/sunscreen/blob/main/CHANGELOG.md). `v1.0` will lock the CLI surface and the JSON contracts. After v1.0, breaking changes require a major bump. + +## How to contribute to the roadmap + +- For larger work: open a discussion or draft an ADR (see [Architecture decisions](./adrs.md)). +- For Phase 8 items: pick an unticked box above, comment on the issue, ship a PR. +- For polish/docs: PRs are welcome on `docs/site/` without an issue. diff --git a/docs/site/src/guides/deploying-to-devnet.md b/docs/site/src/guides/deploying-to-devnet.md new file mode 100644 index 0000000..b58f96b --- /dev/null +++ b/docs/site/src/guides/deploying-to-devnet.md @@ -0,0 +1,124 @@ +# Deploying to devnet + +⏱ 6 min · 🎯 you'll have: your program deployed on Solana devnet, with the program ID wired into your config. + +## Pre-requisites + +- Built workspace (`sunscreen chain build` succeeded). +- `solana` CLI on PATH (`solana --version`). +- A Solana keypair, funded with devnet SOL. + +## Step 1 — Wallet + +If you don't have a keypair yet: + +```bash +sunscreen wallet new +``` + +This writes `~/.config/solana/id.json` and prints the public key. **Save the recovery words** if prompted. + +If you already have a keypair, point to it: + +```bash +export SUNSCREEN_WALLET=$HOME/.config/solana/id.json +``` + +Or set `wallet:` in `sunscreen.yml`. The CLI defaults to the solana-cli default location. + +## Step 2 — Airdrop devnet SOL + +You need ~2 SOL for a fresh deploy. + +```bash +sunscreen wallet airdrop --network devnet --amount 2 +``` + +If you see `Network: rate-limited`, the public faucet throttled you. Options: + +- Wait a few minutes and retry. +- Use a public web faucet: . +- Run `solana airdrop 2 --url devnet` directly. + +## Step 3 — Deploy plan (dry run) + +Always inspect the plan first: + +```bash +sunscreen deploy --network devnet --dry-run +``` + +You'll see: + +```text +plan +─ network: devnet (https://api.devnet.solana.com) +─ payer: (balance: 2.0 SOL) +─ programs: + my_app → target/deploy/my_app.so (180 KB) +─ estimated cost: ~1.6 SOL +``` + +If the estimated cost exceeds your balance, the dry run fails with `exit 4` and tells you to airdrop more. + +## Step 4 — Deploy + +```bash +sunscreen deploy --network devnet +``` + +Under the hood, this runs `solana program deploy` for each program in your workspace and updates `Anchor.toml` + `sunscreen.yml` with the new program IDs. + +On success: + +``` +✓ deployed my_app at +✓ Anchor.toml updated +✓ sunscreen.yml updated +``` + +Your IDL is also published if you pass `--with-idl`. + +## Step 5 — Sanity check + +```bash +solana program show --url devnet +``` + +You should see the program account with the correct authority and data length. + +## Step 6 — Regenerate clients + +If you have a frontend: + +```bash +sunscreen generate clients +``` + +This rewrites `app/src/clients/` against the now-deployed program ID. Restart your `pnpm dev` so it picks up the new clients. + +## Re-deploying after code changes + +Each subsequent deploy is incremental: + +```bash +sunscreen chain build +sunscreen deploy --network devnet +``` + +Solana's `solana program deploy` handles the upgrade in place, reusing the program ID. + +## Common pitfalls + +| Symptom | Cause | Fix | +|---------|-------|-----| +| `insufficient funds` | not enough SOL | airdrop or use the web faucet | +| `program too large` | binary > buffer size | check `--max-len`, or upgrade in chunks | +| `transaction simulation failed` | program-side check failed | run the test suite locally first | +| `BlockhashNotFound` | RPC overloaded or clock skew | retry; consider a private RPC (Helius, Triton) | + +## Next + +- [Deploying to mainnet](./deploying-to-mainnet.md) — the same flow with more caution. +- [Working with plugins](./plugins.md). +- [`deploy` reference](../reference/cli/onboarding.md#deploy). diff --git a/docs/site/src/guides/deploying-to-mainnet.md b/docs/site/src/guides/deploying-to-mainnet.md new file mode 100644 index 0000000..5a2b525 --- /dev/null +++ b/docs/site/src/guides/deploying-to-mainnet.md @@ -0,0 +1,104 @@ +# Deploying to mainnet + +⏱ 10 min · 🎯 you'll have: your program live on mainnet-beta, with the same shape as your devnet deploy. + +Mainnet is real money. Read this whole page before running anything. + +## Pre-flight checklist + +- [ ] Tests pass locally (`anchor test`). +- [ ] Deployed to devnet first and tested end-to-end. +- [ ] You have a dedicated mainnet keypair, separate from your dev wallet. +- [ ] That keypair has ~3 SOL (typical Anchor program deploy is 1.5–2.5 SOL). +- [ ] You're using a private RPC (Helius, Triton, QuickNode). Public mainnet RPCs throttle aggressively. +- [ ] Your program's upgrade authority is your control (a multisig, ideally). + +## Step 1 — Configure the RPC + +Pass `--rpc-url`: + +```bash +sunscreen deploy --network mainnet-beta --rpc-url https://your-rpc-endpoint.example.com +``` + +Or set it in `sunscreen.yml`: + +```yaml +networks: + mainnet-beta: + rpc_url: https://your-rpc-endpoint.example.com +``` + +## Step 2 — Dry run + +```bash +sunscreen deploy --network mainnet-beta --dry-run +``` + +Read the plan carefully: + +- Confirm the **payer** is your mainnet wallet, not your devnet one. +- Confirm the **program count** matches what you intend. +- Confirm the **estimated cost** is within your wallet balance. + +If anything looks off, stop and investigate. Mainnet deploys do not refund "I clicked too fast". + +## Step 3 — Deploy + +```bash +sunscreen deploy --network mainnet-beta +``` + +The CLI will: + +1. Build (if `target/deploy/*.so` is missing or stale). +2. Deploy each program via `solana program deploy`. +3. Update `Anchor.toml` and `sunscreen.yml` with the mainnet program IDs. +4. Optionally publish the IDL on chain if you pass `--with-idl`. + +A typical deploy takes 30–90 seconds per program, depending on RPC. + +## Step 4 — Verify + +```bash +solana program show --url mainnet-beta +``` + +Confirm the program is owned by `BPFLoaderUpgradeab1e11111111111111111111111` and the upgrade authority is what you expect. + +If you want users to verify the source matches the deployed bytecode, publish the program with [Solana verifiable builds](https://github.com/Ellipsis-Labs/solana-verifiable-build) (sunscreen doesn't automate this yet). + +## Step 5 — Lock down upgrade authority + +By default, the deploying keypair becomes the upgrade authority. For production, transfer it to a multisig (e.g. [Squads](https://squads.so/)) or, if you don't want anyone to upgrade, set it to `None`: + +```bash +solana program set-upgrade-authority --final +``` + +::: warning +`--final` is irreversible. The program can never be upgraded again. Only do this for programs that you've audited and tested exhaustively. +::: + +## Step 6 — Update clients + +```bash +sunscreen generate clients +``` + +Commit the regenerated clients. Your frontend now points at mainnet program IDs. + +## Common pitfalls + +| Symptom | Cause | Fix | +|---------|-------|-----| +| `RPC error: 429 Too Many Requests` | public RPC throttled | use a private RPC | +| `BlockhashNotFound` mid-deploy | RPC dropped you | retry; deploys are idempotent | +| Wrong wallet used | environment variable leaked | inspect `solana config get` before deploying | +| Forgot to update IDL | clients have stale shape | `sunscreen generate clients` and redeploy frontend | + +## Going further + +- [`deploy` reference](../reference/cli/onboarding.md#deploy) +- [Squads multisig docs](https://docs.squads.so/main/) — to manage upgrade authority. +- [Helius RPC](https://www.helius.dev/) or [Triton](https://triton.one/) for private endpoints. diff --git a/docs/site/src/guides/dev-loop.md b/docs/site/src/guides/dev-loop.md new file mode 100644 index 0000000..53d9c68 --- /dev/null +++ b/docs/site/src/guides/dev-loop.md @@ -0,0 +1,96 @@ +# The dev loop with `chain serve` + +⏱ 6 min · 🎯 you'll have: a single-command dev loop that rebuilds, regenerates clients, and notifies your frontend on every save. + +`chain serve` is sunscreen's supervised dev process. It runs a local validator, watches your files, and orchestrates everything else. + +## What it runs + +```text +┌─────────────────────────────────────────┐ +│ chain serve │ +│ │ +│ ┌──────────┐ ┌──────────┐ ┌────────┐ │ +│ │ validator│ │ watcher │ │pipeline│ │ +│ │ (Surfpool│ │ (notify)│→ │anchor │ │ +│ │ or t-v) │ │ debounce│ │ build │ │ +│ └──────────┘ └──────────┘ │ ↓ │ │ +│ │codama │ │ +│ │ ↓ │ │ +│ │frontend│ │ +│ │notify │ │ +│ └────────┘ │ +└─────────────────────────────────────────┘ +``` + +## Start + +From your workspace root: + +```bash +sunscreen chain serve +``` + +The TUI shows four panels: validator status, build log, faucet, frontend status. Press `?` for keybindings, `q` to quit. + +Headless (CI / editor integration): + +```bash +sunscreen chain serve --headless +``` + +Headless mode emits one NDJSON event per line on stdout. See [NDJSON events](../reference/events.md). + +## What "watching" means + +Sunscreen watches your workspace tree minus a list of ignored paths (`target/`, `node_modules/`, `app/.sunscreen/`, hidden dirs). When you save a Rust file, sunscreen: + +1. Debounces (waits ~200ms for related saves). +2. Runs `anchor build`. +3. If build succeeded *and* a frontend is configured, runs Codama to regenerate clients. +4. Touches `app/.sunscreen/reload` so your frontend dev server (Vite, Next, …) hot-reloads. + +If any step fails, the TUI shows the error and the validator stays up — fix and save again. + +## Choose your validator + +Sunscreen prefers Surfpool if found on `$PATH`. Override: + +```bash +sunscreen chain serve --validator test-validator +sunscreen chain serve --validator surfpool +``` + +If Surfpool is the default but missing, sunscreen falls back to `solana-test-validator` automatically and logs the fallback. + +## Skip Codama on rebuild + +If you don't have a frontend and don't need clients: + +```bash +sunscreen chain serve --no-codama +``` + +## A typical session + +1. `sunscreen chain serve` — TUI appears, validator boots in ~2s. +2. Open `programs/my_app/src/instructions/create_post.rs`, edit a handler. +3. Save. Within ~3s: anchor builds, Codama regenerates `app/src/clients/`, your Vite dev server reloads. +4. Test in browser. Fix bugs. Repeat. +5. `q` to quit. Sunscreen tears down the validator, kills the process group cleanly. + +## Ctrl-C + +`Ctrl-C` shuts everything down. Sunscreen stops the Unix process group of the validator (and any subprocess it spawned). If something doesn't exit within a grace period, sunscreen sends `SIGKILL`. + +## When it doesn't help + +- You don't have an Anchor or Pinocchio workspace. `chain serve` requires one. +- You're on Windows. Process-group teardown isn't yet implemented for Windows; Phase 8 work. +- You want to manage a remote validator. `chain serve` is for local dev only. + +## Going further + +- [`chain serve` reference](../reference/cli/chain.md#serve) — every flag. +- [NDJSON events](../reference/events.md) — for editor integrations. +- [Build pipeline](../concepts/build-pipeline.md) — what runs and in what order. diff --git a/docs/site/src/guides/plugins.md b/docs/site/src/guides/plugins.md new file mode 100644 index 0000000..d321283 --- /dev/null +++ b/docs/site/src/guides/plugins.md @@ -0,0 +1,109 @@ +# Working with plugins + +⏱ 7 min · 🎯 you'll: install a local plugin, list its commands, run one, and understand the trust model. + +Plugins extend sunscreen's `scaffold ` surface. They are external binaries speaking sunscreen's JSON-RPC protocol over stdio. Use them to add team-specific scaffolds (custom CRUD shapes, internal protocol patterns, indexer hooks). + +## Plugin model in one paragraph + +A plugin is a binary that, when invoked, reads JSON-RPC requests from stdin and writes JSON-RPC responses to stdout. Sunscreen calls the plugin's `commands` method to learn what it offers, then `run` when the user invokes one. Plugins are sandboxed: they can only write within the workspace and cannot reach the network unless declared. + +## Discover + +```bash +sunscreen app marketplace +``` + +Lists reference plugins (`sunscreen-apps/spl-token-2022`, `sunscreen-apps/yellowstone-indexer`) and any plugins you've already installed. + +## Install a local plugin + +Sunscreen reads plugins declared in `sunscreen.yml`: + +```yaml +plugins: + - source: ./plugins/my-plugin + version: "0.2.0" +``` + +Or install via CLI (idempotent, writes to `sunscreen.yml`): + +```bash +sunscreen app install ./plugins/my-plugin --version 0.2.0 +``` + +After install: + +```bash +sunscreen app list +# my-plugin 0.2.0 ./plugins/my-plugin (status: declared) +``` + +`status: declared` means sunscreen knows about it. The first time you `run` a plugin command, sunscreen verifies the binary, reads its manifest, and starts a JSON-RPC session. + +## List a plugin's commands + +```bash +sunscreen app commands my-plugin +# scaffold: +# indexer Scaffold a Yellowstone indexer slice +# listener Scaffold an event listener +``` + +## Run a plugin command + +```bash +sunscreen app run my-plugin -- scaffold indexer Trades +# or, when the plugin registers a top-level scaffold noun: +sunscreen scaffold indexer Trades --program my_app +``` + +## Trust and sandbox + +Sunscreen's plugin runtime enforces: + +- **Filesystem**: writes restricted to the workspace root and below. Reads outside the workspace are allowed only for plugin-declared paths in its manifest. +- **Network**: blocked by default. Plugins must declare network needs in their manifest (`needs_network: true`) and the user must approve at install time. +- **Subprocess**: blocked. Plugins cannot spawn child processes. + +If a plugin violates the sandbox, sunscreen kills the session and exits with code `9` (`plugin_runtime`). See [Errors & exit codes](../reference/errors.md). + +## Uninstall + +```bash +sunscreen app uninstall my-plugin +``` + +Removes the entry from `sunscreen.yml`. Does not delete the plugin binary from disk (you do that manually). + +## Update + +```bash +sunscreen app update my-plugin --version 0.3.0 +``` + +Bumps the `version` in `sunscreen.yml`. + +## Hooks + +Plugins can register hooks for build/serve events. Configure: + +```yaml +plugins: + - source: ./plugins/coverage + version: "0.1.0" + hooks: + - after_build +``` + +After every `chain build`, sunscreen calls the plugin's `hook` method with the build context. Use this for coverage reports, telemetry, custom artifacts. + +## Writing your own plugin + +See the [Plugin protocol reference](../reference/plugin-protocol/index.md) for the JSON-RPC schema, manifest format, and gRPC contract. + +## Going further + +- [`app` reference](../reference/cli/app.md) — all subcommands. +- [Plugin protocol](../reference/plugin-protocol/index.md) — wire format. +- [Plugin runtime concepts](../concepts/plugin-runtime.md) — sandbox model. diff --git a/docs/site/src/guides/scaffolding-crud.md b/docs/site/src/guides/scaffolding-crud.md new file mode 100644 index 0000000..5b9af54 --- /dev/null +++ b/docs/site/src/guides/scaffolding-crud.md @@ -0,0 +1,99 @@ +# Scaffolding a CRUD resource + +⏱ 6 min · 🎯 you'll have: a complete `Post` resource with create/read/update/delete instructions, events, errors, and TS test. + +The CRUD recipe is the fastest way to add a new resource to an existing program. It composes the primitive scaffolders (`account`, `instruction`, `event`, `error`) into a coherent slice. + +## Pre-requisites + +- A workspace already created with `sunscreen chain new`. +- At least one program in `programs/`. + +## Run + +```bash +sunscreen scaffold crud Post --program my_app +``` + +Output (truncated): + +``` +✓ scaffolded account: Post +✓ scaffolded instructions: create_post, read_post, update_post, delete_post +✓ scaffolded events: PostCreated, PostUpdated, PostDeleted +✓ scaffolded errors: PostNotFound, PostUnauthorized +✓ tests/post.spec.ts +``` + +## What was generated + +| File | Purpose | +|------|---------| +| `programs/my_app/src/state/post.rs` | `Post` account struct with `#[account]` | +| `programs/my_app/src/instructions/create_post.rs` | `create_post` handler | +| `programs/my_app/src/instructions/read_post.rs` | `read_post` handler | +| `programs/my_app/src/instructions/update_post.rs` | `update_post` handler | +| `programs/my_app/src/instructions/delete_post.rs` | `delete_post` handler | +| `programs/my_app/src/events.rs` (patched) | `PostCreated`, `PostUpdated`, `PostDeleted` events | +| `programs/my_app/src/errors.rs` (patched) | `PostNotFound`, `PostUnauthorized` variants | +| `tests/post.spec.ts` | TypeScript test scaffolding the four ops | + +All writes happen inside marker regions. Hand-edit anywhere outside the markers and your changes survive future scaffolds. + +## Options + +```bash +sunscreen scaffold crud Post \ + --program my_app \ + --fields "title:string,body:string,author:pubkey,created_at:i64" \ + --frontend-hook \ + --json +``` + +| Flag | Default | What it does | +|------|---------|--------------| +| `--program ` | required | program to scaffold into | +| `--fields ""` | `title:string,body:string` | comma-separated `name:type` pairs for the account struct | +| `--frontend-hook` | off | also generate React Query hooks (requires `frontend: react` in `sunscreen.yml`) | +| `--dry-run` | off | print what would change without writing | +| `--json` | off | machine-readable summary | +| `--force` | off | overwrite existing conflicting symbols | + +## Idempotency + +Re-running the same command is a no-op: + +```bash +sunscreen scaffold crud Post --program my_app +# error: account "Post" already exists in program "my_app" (exit 4) +``` + +Add `--force` if you really want to regenerate (will *not* clobber hand-edits outside markers). + +## What "fields" supports + +| Spec syntax | Anchor type | +|-------------|------------| +| `name:string` | `String` | +| `name:bool` | `bool` | +| `name:u64` (also `u8`, `u16`, `u32`, `u128`) | matching unsigned int | +| `name:i64` (also `i8`, `i16`, `i32`, `i128`) | matching signed int | +| `name:pubkey` | `Pubkey` | +| `name:vec` | `Vec` (limited to 256 bytes) | + +Complex types (nested structs, large vectors) need manual editing. + +## Build and test + +```bash +sunscreen chain build +anchor test +``` + +The generated `tests/post.spec.ts` exercises each of the four operations end-to-end against a local validator. + +## Going further + +- [Recipe reference: CRUD](../reference/recipes/crud.md) — every flag, every generated file. +- [SPL Token recipe](../reference/recipes/spl-token.md) and [Metaplex NFT recipe](../reference/recipes/metaplex-nft.md) — other composite recipes. +- [Marker protocol](../reference/markers.md) — understand how regenerations stay safe. diff --git a/docs/site/src/guides/troubleshooting.md b/docs/site/src/guides/troubleshooting.md new file mode 100644 index 0000000..57c19e1 --- /dev/null +++ b/docs/site/src/guides/troubleshooting.md @@ -0,0 +1,112 @@ +# Troubleshooting + +Top issues you'll run into, with concrete fixes. Sunscreen errors include a `next_step` hint by default — this page is the longer form of those hints. + +## `toolchain_missing: anchor` + +Sunscreen needs `anchor` for `chain build`, `chain serve`, and most `scaffold` operations. + +**Fix:** install via AVM: + +```bash +cargo install --git https://github.com/coral-xyz/anchor avm --locked +avm install latest +avm use latest +``` + +Then `sunscreen doctor` to confirm. + +## `toolchain_missing: solana` + +Needed for `deploy`, `wallet airdrop`, and `chain serve --validator test-validator`. + +**Fix:** + +```bash +sh -c "$(curl -sSfL https://release.solana.com/stable/install)" +``` + +## `invalid_config` (exit 3) + +`sunscreen.yml` failed schema validation. The error message names the offending field. + +**Fix:** run `sunscreen doctor --json` to see the parsed config, and compare to the [schema reference](../reference/config/schema.md). Common mistakes: + +- Misspelled keys (`frontend: ract` instead of `react`). +- Missing required field for a chosen variant (e.g. `framework: anchor` requires a `programs:` list). +- Version string without semver shape. + +## `user_input` (exit 4) + +You asked sunscreen to do something that conflicts with the current state — e.g. scaffold an instruction that already exists, or install a plugin twice. + +**Fix:** re-read the message. Sunscreen refuses to clobber. Pass `--force` only when you understand what gets overwritten. + +## `missing_workspace` (exit 5) + +You ran a workspace-scoped command from a directory that has no `sunscreen.yml` upward. + +**Fix:** `cd` into a sunscreen workspace, or `sunscreen chain new` first. + +## `plugin_runtime` (exit 9) + +A plugin crashed or violated the sandbox. + +**Fix:** + +- Run `sunscreen app run -- --help` to confirm the binary responds. +- Check the plugin manifest declares the network/filesystem permissions it actually needs. +- `sunscreen app describe ` shows the manifest sunscreen sees. + +## `chain build` succeeds but Codama fails + +Common when the IDL changed shape in a way that breaks Codama's config. + +**Fix:** + +```bash +sunscreen generate clients --rebuild-config +``` + +This regenerates the Codama config from the IDL. Then re-run `chain build`. + +## `chain serve` doesn't pick up file saves + +Usually one of: + +- The file is inside a path sunscreen ignores (e.g. `target/`, `node_modules/`, `app/.sunscreen/`). +- macOS: too many files in the watch tree exceeds the descriptor limit. Run `ulimit -n 4096`. +- Linux: inotify limit. `echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p`. + +## Frontend doesn't hot reload + +Sunscreen writes `app/.sunscreen/reload` after Codama runs. Your dev server must watch that file. + +**Fix:** for Vite, add to `vite.config.ts`: + +```ts +server: { + watch: { ignored: ['!**/.sunscreen/reload'] } +} +``` + +For Next.js, place a `useEffect` polling the file mtime, or use the included `useReload` hook from `generate frontend-hooks`. + +## `airdrop` is rate-limited + +The public devnet faucet throttles aggressively. + +**Fix:** use (web), or a private RPC's faucet (Helius, QuickNode), or wait 10 minutes. + +## "I forgot to deploy after a code change" + +```bash +sunscreen chain build && sunscreen deploy --network devnet +``` + +Sunscreen detects out-of-date `target/deploy/*.so` and rebuilds automatically before deploy. + +## Still stuck? + +- Search existing issues: . +- Open a new issue with `sunscreen doctor --json` output and the command you ran. diff --git a/docs/site/src/index.md b/docs/site/src/index.md new file mode 100644 index 0000000..f994e82 --- /dev/null +++ b/docs/site/src/index.md @@ -0,0 +1,58 @@ +# Sunscreen + +> A Rust CLI for scaffolding, repairing, and orchestrating Solana Anchor and Pinocchio workspaces. + +Sunscreen takes you from an empty folder to a working Solana program — Anchor or Pinocchio — without hand-stitching `anchor`, `solana`, `cargo`, `codama`, `surfpool`, and frontend tooling. It is deterministic, marker-driven, and built for the long term: incremental edits, plugin extension, supervised dev loop. + +
+ +[Start in 10 minutes →](./learn/your-first-nft.md) +  [Read the reference →](./reference/cli/index.md) + +
+ +--- + +## Pick your path + +::::{.cards} + +:::{.card} +### 🌱 Learn + +New to Solana, Rust, or both? Start here. We assume zero prior knowledge. + +[Begin learning →](./learn/what-is-sunscreen.md) +::: + +:::{.card} +### 🛠 Guides + +Task-oriented walkthroughs. "How do I scaffold a CRUD?" "How do I deploy to devnet?" + +[Browse guides →](./guides/scaffolding-crud.md) +::: + +:::{.card} +### 📖 Reference + +Every command, every flag, every exit code. For when you know what you want. + +[Open reference →](./reference/cli/index.md) +::: + +:::: + +--- + +## Why sunscreen? + +- **Deterministic generation.** Same inputs, same files, byte-for-byte. Golden-tested. +- **Marker-based incremental edits.** Add a new instruction to an existing program without breaking what's there. `chain doctor --fix-markers` repairs safe drift. +- **Supervised dev loop.** `chain serve` runs Surfpool (or `solana-test-validator`), watches files, rebuilds, regenerates Codama clients, notifies the frontend — all in one process. +- **Plugins.** Local plugins extend `scaffold ` via stdio JSON-RPC; gRPC contract for the future. Trust and sandboxing are explicit. +- **Two frameworks.** Anchor for the productive default; Pinocchio for the bare-metal path. + +## Status + +`v0.1.0` is the first preview release. The path to `v1.0` is tracked in the [Roadmap](./contributing/roadmap.md). Phase 8 (this site, completions, Homebrew/Windows distribution, release polish) is in progress. diff --git a/docs/site/src/learn/first-workspace.md b/docs/site/src/learn/first-workspace.md new file mode 100644 index 0000000..a2b2eff --- /dev/null +++ b/docs/site/src/learn/first-workspace.md @@ -0,0 +1,106 @@ +# Your first workspace + +⏱ 8 min · 🎯 you'll have: a freshly scaffolded Anchor workspace, building locally. + +We'll create an empty workspace, look at what was generated, and run our first build. + +## Pre-requisites + +- `sunscreen` installed ([Installing](./installing.md)). +- `anchor` CLI on your PATH (`anchor --version` should print a version). If missing, install via [AVM](https://www.anchor-lang.com/docs/installation). +- `cargo` (comes with Rust). + +Don't have everything? Run `sunscreen doctor` first — it tells you exactly what's missing. + +## Step 1 — Create + +```bash +sunscreen chain new my-app --framework anchor --frontend none +``` + +You'll see NDJSON-style progress lines, then: + +``` +✓ workspace my-app/ created (anchor, no frontend) +``` + +## Step 2 — Look around + +```bash +cd my-app +tree -L 2 -I node_modules +``` + +``` +my-app/ +├── Anchor.toml +├── Cargo.toml +├── package.json +├── programs/ +│ └── my_app/ +│ ├── Cargo.toml +│ └── src/ +│ └── lib.rs +├── sunscreen.yml ← sunscreen's config (the source of truth) +├── tests/ +│ └── my_app.ts +└── target/ +``` + +The interesting files: + +- `sunscreen.yml` — your project config. Programs, plugins, runtime settings. +- `programs/my_app/src/lib.rs` — the Anchor program. Has `// sunscreen:begin ... end` markers that sunscreen can edit on later commands without breaking what you wrote in between. +- `Anchor.toml` — standard Anchor config, points at the program. + +::: tip +The markers in `lib.rs` are how sunscreen does *incremental* edits. When you run `sunscreen scaffold instruction Foo` later, it injects code only into marked regions. Your hand-written code is left alone. Read more in [Incremental scaffolding](../concepts/incremental-scaffolding.md). +::: + +## Step 3 — Build + +```bash +sunscreen chain build +``` + +This runs `anchor build` under the hood and reports progress as NDJSON lines (one event per line, useful for editor integrations). On success: + +``` +✓ anchor build +✓ codama clients regenerated (skipped: no frontend) +``` + +Behind the scenes: + +1. `anchor build` produced `target/idl/my_app.json` and the program's `.so`. +2. Sunscreen skipped Codama client generation because we picked `--frontend none`. + +## Step 4 — Add an instruction + +```bash +sunscreen scaffold instruction Greet --program my_app +``` + +Open `programs/my_app/src/lib.rs` — you'll see a new `pub fn greet(...)` handler inside the program module, registered in the dispatch. The marker regions made the edit safe. + +Re-run the same command: + +```bash +sunscreen scaffold instruction Greet --program my_app +# error: instruction "Greet" already exists in program "my_app" (exit code 4) +``` + +Idempotent: sunscreen refuses to clobber. Use `--force` if you really want to regenerate. + +## What just happened + +- One command (`chain new`) gave you a buildable Anchor workspace plus sunscreen's own config. +- The generated code has marker comments that let sunscreen safely edit the same files in later runs. +- `chain build` is a thin orchestration over `anchor build` + Codama generation. +- `scaffold instruction` mutated marked regions only, leaving your code untouched. + +## Next + +- [Your first NFT in 10 minutes](./your-first-nft.md) — chain `init` + scaffold + deploy. +- [Scaffolding a CRUD resource](../guides/scaffolding-crud.md) — composite recipes. +- [Incremental scaffolding](../concepts/incremental-scaffolding.md) — the marker model in depth. diff --git a/docs/site/src/learn/glossary.md b/docs/site/src/learn/glossary.md new file mode 100644 index 0000000..309bca5 --- /dev/null +++ b/docs/site/src/learn/glossary.md @@ -0,0 +1,79 @@ +# Glossary + +One- or two-sentence definitions for every term used elsewhere in the docs. Linked from primers and tutorials. + +## Solana + +**Account** — addressable chunk of memory on Solana. Holds lamports, data, an owner program, and an executable flag. Everything on chain is an account. + +**Anchor** — a Rust framework that hides Solana's low-level account boilerplate behind macros (`#[program]`, `#[derive(Accounts)]`, `#[account]`). Sunscreen targets Anchor by default. + +**BPF** — Berkeley Packet Filter. The bytecode format Solana programs compile to. + +**Codama** — a code generator that produces typed clients (JavaScript, Rust, …) from an Anchor IDL. Sunscreen wraps it. + +**Devnet** — the public Solana test network. Free SOL via faucet, throwaway, perfect for staging before mainnet. + +**Discriminator** — an 8-byte prefix Anchor writes at the start of every program-owned account, identifying its type. + +**Fee payer** — the signer who pays transaction fees. Usually the wallet that initiated the transaction. + +**IDL (Interface Definition Language)** — JSON file describing a program's instructions and account layouts. Anchor builds emit one; sunscreen exports it. + +**Instruction** — a single call to a program inside a transaction. Specifies the program ID, the accounts touched, and a data payload. + +**Lamport** — the smallest unit of SOL. 1 SOL = 10⁹ lamports. + +**Localnet** — a local Solana validator running on your laptop. Sunscreen's `chain serve` launches one (via Surfpool or `solana-test-validator`). + +**Mainnet-beta** — Solana production. + +**Metaplex** — a collection of standards and tools for NFTs on Solana. The Token Metadata program is the canonical NFT spec. + +**PDA (Program-Derived Address)** — an address derived deterministically from seeds + a program ID. Has no private key. Only the owning program can sign for it. + +**Pinocchio** — a minimal-overhead alternative to Anchor for Solana programs. Sunscreen supports it via `chain new --framework pinocchio`. + +**Program** — an executable account on Solana. Stateless. Reads and writes other accounts. + +**Program ID** — the public key of a deployed program. + +**Pubkey** — a 32-byte public key. Addresses, program IDs, signers — all `Pubkey`s. + +**Rent** — a one-time deposit when creating an account, proportional to its data size. Refunded on close. + +**RPC** — the JSON-RPC endpoint of a Solana validator (e.g. `https://api.devnet.solana.com`). + +**Signer** — an account whose private key signed the transaction. Required for any account that gets debited or whose authority is asserted. + +**SOL** — the native token. Used for fees and rent. + +**SPL Token** — Solana's fungible token standard (and its program). Sunscreen's `scaffold spl-token` recipe generates a slice that mints / transfers. + +**Surfpool** — a community-driven local validator alternative to `solana-test-validator`, optimized for dev loops. Sunscreen prefers it when present. + +**Transaction** — a bundle of instructions, signed by one or more accounts, submitted to the network atomically. + +## Sunscreen + +**Chain (subcommand)** — `chain new`, `chain build`, `chain serve`, `chain doctor`. Workspace lifecycle. + +**Codama config** — a JSON file Sunscreen manages so Codama knows where to read the IDL and where to write clients. + +**Frontend hooks** — React/Solid Query hooks Sunscreen generates from an IDL via `generate frontend-hooks`. + +**Generator tag** — the value (e.g. `account`, `event`) Sunscreen writes inside a marker so re-runs know what to regenerate. + +**Marker** — a comment pair (`// sunscreen:begin {generator=…}` … `// sunscreen:end`) inside generated files. Sunscreen edits only the content between them on subsequent runs. Read [Marker protocol](../reference/markers.md). + +**NDJSON events** — newline-delimited JSON emitted by `chain build` / `chain serve` for editor and CI integration. See [NDJSON events](../reference/events.md). + +**Plugin** — an external binary speaking sunscreen's JSON-RPC protocol that extends `scaffold `. See [Plugin protocol](../reference/plugin-protocol/index.md). + +**Recipe** — a composite scaffold built on top of primitives. `scaffold crud`, `scaffold spl-token`, `scaffold metaplex-nft`. + +**Scaffold (subcommand)** — adds an instruction, account, event, error, program, or recipe to an existing workspace. + +**Sunscreen.yml** — the project's config file. Declarative source of truth for programs, plugins, runtime preferences. + +**Quickstart** — beginner-friendly composite command that wires `chain new` + recipes + frontend hooks in one step. diff --git a/docs/site/src/learn/installing.md b/docs/site/src/learn/installing.md new file mode 100644 index 0000000..2ff4d72 --- /dev/null +++ b/docs/site/src/learn/installing.md @@ -0,0 +1,78 @@ +# Installing + +⏱ 3 min · 🎯 you'll have: `sunscreen --version` working in your terminal. + +Sunscreen ships as a single binary. Three install paths, in order of recommendation. + +## Pre-requisites + +You need a recent stable Rust toolchain on your PATH. If you don't have it: + +```bash +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh +``` + +The other Solana tools (`anchor`, `solana`, `cargo-build-sbf`, `pnpm`, …) are checked at runtime by `sunscreen doctor`. You only need them when you actually run the corresponding command. + +## Option 1 — installer script (recommended) + +The fastest path. Works on macOS (x86_64, aarch64) and Linux (x86_64, aarch64). + +```bash +curl --proto '=https' --tlsv1.2 -LsSf \ + https://github.com/Pantani/sunscreen/releases/download/v0.1.0/sunscreen-installer.sh \ + | sh +``` + +The script installs to `~/.cargo/bin/sunscreen`. Restart your shell or `source ~/.cargo/env` so the binary is on PATH. + +## Option 2 — `cargo install` + +If you already have a Rust toolchain and prefer to build locally: + +```bash +cargo install --git https://github.com/Pantani/sunscreen --tag v0.1.0 sunscreen +``` + +This compiles from source (~2 minutes on a modern laptop) and places the binary in `~/.cargo/bin`. + +## Option 3 — download the archive + +Releases include pre-built tarballs and zips: + +1. Open . +2. Download the archive matching your platform (`sunscreen-aarch64-apple-darwin.tar.xz`, `sunscreen-x86_64-unknown-linux-gnu.tar.xz`, …). +3. Extract and move `sunscreen` into a directory on your `$PATH`. + +## Verify + +```bash +sunscreen --version +# sunscreen 0.1.0 +``` + +Run the doctor to see which Solana tools sunscreen detected: + +```bash +sunscreen doctor +``` + +You'll see a table. Anything reported `missing` will be required *only* when you run a command that needs it. For example, you can `chain new` without `anchor` installed, but you'll need `anchor` to `chain build`. + +## Updating + +- Installer script: re-run with the new version URL. +- `cargo install`: re-run with `--force` and the new `--tag`. +- Archive: download a new tarball. + +## Uninstall + +```bash +rm "$(which sunscreen)" +``` + +Sunscreen does not write to global directories outside the binary itself. Per-workspace state lives in your project folder. + +## Next + +- [Create your first workspace](./first-workspace.md). diff --git a/docs/site/src/learn/rust-primer.md b/docs/site/src/learn/rust-primer.md new file mode 100644 index 0000000..87b476b --- /dev/null +++ b/docs/site/src/learn/rust-primer.md @@ -0,0 +1,127 @@ +# Rust primer (for Solana, in 10 minutes) + +⏱ 10 min · 🎯 you'll understand: just enough Rust to read and edit Anchor programs. + +You don't need to be a Rust expert to use sunscreen. You need to read code generated by `scaffold` and tweak it. This primer gives you exactly that. + +## The 30-second mental model + +Rust is statically typed, has no garbage collector, and tracks **who owns each value** at compile time. In Anchor programs, the heavy machinery is hidden behind macros — `#[program]`, `#[derive(Accounts)]`, `#[account]`. You mostly write straight-line code inside instruction handlers. + +## Ownership (in 1 paragraph) + +Every value has one owner. When the owner goes out of scope, the value is dropped (memory freed). You can lend a value out with `&value` (read-only) or `&mut value` (read-write). At any moment, you can have either *many* read-only borrows *or* one mutable borrow. The compiler enforces this — get used to error messages naming "borrow checker". For Anchor programs, this almost never bites you, because the framework manages account data. + +## Structs and impls + +```rust +pub struct Counter { + pub count: u64, +} + +impl Counter { + pub fn increment(&mut self) { + self.count += 1; + } +} +``` + +A struct holds fields. An `impl` block defines methods. `&mut self` means "this method can modify the struct". + +## Anchor's three macros + +This is 90% of what you'll see in scaffolded code. + +### `#[program]` + +```rust +#[program] +pub mod my_app { + use super::*; + + pub fn greet(ctx: Context, name: String) -> Result<()> { + msg!("Hello, {}!", name); + Ok(()) + } +} +``` + +The `#[program]` macro turns each `pub fn` into a callable instruction. `Context` carries the accounts the instruction needs (declared next). `Result<()>` is Rust's standard error type — `Ok(())` means success with no return value. + +### `#[derive(Accounts)]` + +```rust +#[derive(Accounts)] +pub struct Greet<'info> { + #[account(mut)] + pub user: Signer<'info>, + pub system_program: Program<'info, System>, +} +``` + +This declares which accounts the instruction takes. `mut` = will be modified. `Signer` = must sign the transaction. The `'info` is a lifetime — think of it as "these accounts live for the duration of this call". You'll rarely touch lifetimes manually. + +### `#[account]` + +```rust +#[account] +pub struct Counter { + pub count: u64, + pub authority: Pubkey, +} +``` + +`#[account]` marks a struct as on-chain storage. Anchor handles serialization/deserialization, plus a discriminator prefix. + +## Common types you'll see + +| Type | What it is | +|------|-----------| +| `u8 .. u64`, `i8 .. i64` | unsigned/signed integers | +| `bool` | `true` / `false` | +| `String` | heap-allocated UTF-8 string | +| `Vec` | heap-allocated growable array of `T` | +| `Pubkey` | 32-byte Solana public key | +| `Option` | `Some(T)` or `None` | +| `Result` | `Ok(T)` or `Err(E)` | + +## Error handling + +In Anchor handlers you'll see: + +```rust +require!(amount > 0, MyError::AmountZero); +some_account.field = value; +Ok(()) +``` + +`require!` is a macro: if the condition is false, it returns `Err(MyError::AmountZero)`. Otherwise execution continues. `Ok(())` returns success. + +## The `?` operator + +```rust +let data = account.try_borrow_data()?; +``` + +The `?` says: if the call returned `Err`, propagate it up; if `Ok`, unwrap the value and continue. It's a shortcut for `match`. + +## What you can safely ignore (for now) + +- Traits and generics beyond what Anchor uses. +- Async/await (Anchor programs are synchronous). +- Smart pointers (`Box`, `Rc`, `Arc`) — Anchor doesn't need them in handlers. +- Macros internals — just use them. + +## Going deeper + +When you're ready: is the canonical, well-paced book. The chapters that matter for Anchor: + +- Ch. 4: ownership + borrowing. +- Ch. 5–6: structs and enums. +- Ch. 9: error handling. +- Ch. 13: closures and iterators (useful for client code). + +## Next + +- [Solana primer](./solana-primer.md) — accounts, programs, PDAs, transactions. +- [Your first workspace](./first-workspace.md) — apply this to read what sunscreen generates. diff --git a/docs/site/src/learn/solana-primer.md b/docs/site/src/learn/solana-primer.md new file mode 100644 index 0000000..b86d788 --- /dev/null +++ b/docs/site/src/learn/solana-primer.md @@ -0,0 +1,89 @@ +# Solana primer (in 5 minutes) + +⏱ 5 min · 🎯 you'll understand: accounts, programs, PDAs, fees, devnet/mainnet. + +Everything on Solana is an **account**. Programs are accounts. Wallets are accounts. State is accounts. Once you internalize that, the rest follows. + +## Accounts + +An account on Solana is a chunk of memory at an address (a 32-byte public key). It has: + +- **lamports**: balance in the native token (1 SOL = 1,000,000,000 lamports). +- **data**: arbitrary bytes (program-defined layout). +- **owner**: the program that controls writes to this account. +- **executable**: `true` if the account *is* a program. + +Two accounts of interest in your code: + +- **Wallet account**: a keypair (you control). Holds SOL, signs transactions. +- **Data account**: holds program state (a counter, a user profile, an NFT mint). + +## Programs + +A **program** is an account marked `executable`. Its `data` is BPF bytecode. Programs are stateless — they read and write *other* accounts. Your Anchor program is exactly this: a single executable account, deployed to an address (the *program ID*). + +## Transactions and instructions + +You don't call a program directly. You build a **transaction** containing one or more **instructions**: + +```text +transaction +├── signers: [wallet] +├── instructions: +│ └── { program_id, accounts, data } +``` + +The runtime delivers the instruction to the program, which mutates the listed accounts and returns success or error. + +## PDAs (Program-Derived Addresses) + +A normal address has a private key. A **PDA** does *not* — it's deterministically derived from seeds + a program ID, and only that program can sign for it. This is how programs "own" data accounts. + +```rust +let (pda, bump) = Pubkey::find_program_address( + &[b"counter", user.key().as_ref()], + program_id, +); +``` + +You'll see PDAs everywhere in Anchor: `#[account(seeds = [...], bump)]` declares them. + +## Fees + +Every transaction pays a small fee in SOL, charged to the *fee payer* (usually the signer). On devnet this is essentially free. On mainnet, 5,000 lamports per signature is the base. + +Account creation also costs **rent**: a one-time deposit proportional to the account's data size. You get it back when you close the account. + +## Networks + +Three official networks plus your local validator: + +| Network | URL | Use | +|---------|-----|-----| +| `localnet` | `http://127.0.0.1:8899` | a validator on your laptop (sunscreen's `chain serve` launches one) | +| `devnet` | `https://api.devnet.solana.com` | free SOL via faucet, public, test programs here first | +| `testnet` | `https://api.testnet.solana.com` | validator testing, not for app dev | +| `mainnet-beta` | `https://api.mainnet-beta.solana.com` | production | + +## The development flow you'll use + +1. Write the program (sunscreen scaffolds it). +2. `chain serve` — runs against `localnet` with hot reload. +3. `deploy --network devnet` — push to devnet, test with real fees + real wallets. +4. `deploy --network mainnet-beta` — production. + +## Common pitfalls (the ones that bite everyone) + +- **You forgot to airdrop SOL** before deploying to devnet. Run `sunscreen wallet airdrop`. +- **Wrong program ID**: after a deploy, your client must use the new ID. Sunscreen's Codama clients regenerate automatically. +- **Account already initialized**: PDAs are deterministic. If you `init` the same PDA twice, the second call fails. Use `init_if_needed` or design for one-shot init. +- **Out of rent**: closing an account refunds rent; not closing leaves SOL locked. + +## Want more? + +The Solana docs are good: . For Anchor specifically: . + +## Next + +- [Your first workspace](./first-workspace.md) — apply this to a real program. +- [Glossary](./glossary.md) — every term in one place. diff --git a/docs/site/src/learn/what-is-sunscreen.md b/docs/site/src/learn/what-is-sunscreen.md new file mode 100644 index 0000000..6e2776e --- /dev/null +++ b/docs/site/src/learn/what-is-sunscreen.md @@ -0,0 +1,48 @@ +# What is sunscreen? + +⏱ 5 min read · 🎯 you'll understand: what sunscreen does, when to use it, when not to. + +Sunscreen is a command-line tool for Solana developers. It scaffolds programs, manages a local dev loop, generates clients, and is extensible by plugins. + +If you've ever written a Solana program, you know the friction: `cargo`, `anchor`, `solana-cli`, IDL generation, a frontend, a local validator, hot reload — five tools, five mental models. Sunscreen is one CLI that orchestrates them, with one config file (`sunscreen.yml`). + +## What sunscreen does + +- **Creates** a new Anchor or Pinocchio workspace from a single command. +- **Adds** instructions, accounts, events, errors, or full recipes (CRUD, SPL Token, Metaplex NFT) to an existing program — without breaking what's there. It uses **markers** in your code to edit the same files safely on every run. +- **Builds and watches** in one supervised process: `chain serve` runs Surfpool (or `solana-test-validator`), watches files, rebuilds with Anchor, regenerates Codama clients, and tells your frontend to reload. +- **Generates clients**: IDL, JavaScript via Codama, React/Solid Query hooks — all deterministic, all idempotent. +- **Onboards beginners**: `init`, `quickstart token|nft|dao|blog`, embedded examples, wallet helpers, deploy plans, and actionable errors that tell you the next step. + +## When to use sunscreen + +- You're starting a new Solana program and want a clean workspace in 30 seconds. +- You want a single tool that scaffolds, builds, runs a local validator, and regenerates clients on file changes. +- You like declarative configs (`sunscreen.yml`) over running 6 separate commands. +- You want the option to extend the CLI with plugins for your team. + +## When *not* to use sunscreen + +- Your program is already deeply customized in a non-Anchor, non-Pinocchio workflow. Sunscreen targets these two frameworks. +- You need Windows distribution today — that's planned for v1.0 (see [Roadmap](../contributing/roadmap.md)). macOS and Linux work now. +- You want to manage validator clusters in production — sunscreen targets local dev. Use [Helius](https://www.helius.dev/) or similar for prod RPC. + +## How it compares to Anchor CLI alone + +Anchor CLI gives you `anchor init`, `anchor build`, `anchor deploy`. Great primitives. Sunscreen sits one level above: + +| Task | Anchor CLI | Sunscreen | +|------|-----------|-----------| +| Create workspace | `anchor init` | `sunscreen chain new --framework anchor --frontend react` | +| Add instruction | hand-edit lib.rs | `sunscreen scaffold instruction Create --program app` | +| Add CRUD slice | manual (~200 lines) | `sunscreen scaffold crud Post --program app` | +| Watch + rebuild + regenerate clients | run 3 terminals | `sunscreen chain serve` | +| Diagnose toolchain | trial and error | `sunscreen doctor` | + +Anchor stays under the hood. Sunscreen does not replace it; it composes with it. + +## Next + +- [Install it](./installing.md). +- [Make your first workspace](./first-workspace.md). +- Or jump straight to [your first NFT in 10 minutes](./your-first-nft.md). diff --git a/docs/site/src/learn/your-first-nft.md b/docs/site/src/learn/your-first-nft.md new file mode 100644 index 0000000..60c8979 --- /dev/null +++ b/docs/site/src/learn/your-first-nft.md @@ -0,0 +1,104 @@ +# Your first NFT in 10 minutes + +⏱ 10 min · 🎯 you'll have: a Metaplex NFT scaffold ready to mint on devnet. + +We'll go from empty folder to a mintable NFT program using sunscreen's quickstart recipe. + +## Pre-requisites + +- `sunscreen` ([Installing](./installing.md)). +- `anchor` CLI, `solana` CLI, Node 18+ with `pnpm`. +- A Solana keypair (we'll create one if you don't have it). + +Run `sunscreen doctor` to confirm. Anything missing will be flagged. + +## Step 1 — Quickstart + +```bash +sunscreen quickstart nft my-first-nft +cd my-first-nft +``` + +This single command: + +1. Created an Anchor workspace with a React frontend. +2. Scaffolded a Metaplex NFT recipe slice (mint, metadata, master edition). +3. Added a sample frontend hook for minting. + +You'll see a summary table at the end listing files created. + +::: tip +`quickstart` is a composition of more granular commands: `chain new`, `scaffold metaplex-nft`, `generate frontend-hooks`. You can run them individually for more control — quickstart is the beginner-friendly shortcut. +::: + +## Step 2 — Build + +```bash +sunscreen chain build +``` + +Successful build produces `target/idl/my_first_nft.json` and regenerates Codama clients into `app/src/clients/` (auto-wired because we picked the React frontend). + +## Step 3 — Configure a devnet wallet + +If you don't have a Solana keypair: + +```bash +sunscreen wallet new +``` + +This creates `~/.config/solana/id.json` and prints the public key. Save it somewhere. + +Get devnet SOL: + +```bash +sunscreen wallet airdrop --network devnet --amount 2 +``` + +If the airdrop is throttled (common), the error tells you the next step (use `solana airdrop` directly or a public faucet). + +## Step 4 — Deploy plan + +```bash +sunscreen deploy --network devnet --dry-run +``` + +Sunscreen prints a deploy plan: how much SOL you need, which keys will be created, what's going to be uploaded. No on-chain action yet — it's a `--dry-run`. + +When ready: + +```bash +sunscreen deploy --network devnet +``` + +You'll get a program ID. Save it. + +## Step 5 — Mint + +The React frontend in `app/` is already wired with the Codama-generated client and the mint hook. Start it: + +```bash +cd app +pnpm install +pnpm dev +``` + +Open . Connect your wallet (the same one you funded), click *Mint*, approve. After a few seconds you'll see the mint signature. + +You just minted an NFT through a program *you* wrote — even though sunscreen wrote most of it for you. + +## What happened end-to-end + +```text +quickstart nft → workspace + Metaplex recipe + frontend hooks +chain build → anchor build → IDL → Codama clients +wallet new / airdrop → fund a keypair on devnet +deploy --network devnet → upload program, register program ID +frontend pnpm dev → React app calls the generated client → mints +``` + +## Next + +- [The dev loop with `chain serve`](../guides/dev-loop.md) — hot reload on save. +- [Deploying to mainnet](../guides/deploying-to-mainnet.md) — going from devnet to prod. +- [Metaplex NFT recipe reference](../reference/recipes/metaplex-nft.md) — what was generated, and why. diff --git a/docs/site/src/reference/cli/app.md b/docs/site/src/reference/cli/app.md new file mode 100644 index 0000000..92dee84 --- /dev/null +++ b/docs/site/src/reference/cli/app.md @@ -0,0 +1,116 @@ +# `app` + +Manage plugins. + +``` +sunscreen app [ARGS] +``` + +Plugins are declared in `sunscreen.yml` under `plugins:`. Most `app` subcommands edit that file declaratively. + +## `install` + +``` +sunscreen app install [--version ] [--dry-run] [--json] +``` + +Add a plugin entry to `sunscreen.yml`. **Idempotent**: re-running with the same source is a no-op. + +| Arg / flag | Description | +|------------|-------------| +| `` | local path (`./plugins/foo`) or git URL (`github.com/org/foo.git`) | +| `--version` | semver string; with or without `v` prefix | +| `--dry-run` | print planned change without writing | +| `--json` | `{"status": "declared"|"already_declared", ...}` | + +Source normalization: `github.com/org/foo.git` → plugin name `foo`. Conflicting basenames → exit `4`. + +## `uninstall` + +``` +sunscreen app uninstall +``` + +Remove a plugin entry. Does not delete files from disk. + +## `list` + +``` +sunscreen app list [--json] +``` + +Print declared plugins: + +``` +NAME VERSION SOURCE STATUS +my-plugin 0.2.0 ./plugins/my-plugin declared +``` + +## `describe` + +``` +sunscreen app describe [--json] +``` + +Read the plugin manifest and report: + +- name, version +- declared commands (with `scaffold:`, `hook:` namespacing) +- declared permissions (`needs_network`, `needs_workspace_write`, declared file paths) + +## `update` + +``` +sunscreen app update --version +``` + +Bump the `version` field in `sunscreen.yml`. + +## `commands` + +``` +sunscreen app commands [] [--filter ] [--json] +``` + +List commands offered by one or all plugins. + +## `run` + +``` +sunscreen app run -- ... +``` + +Invoke the plugin's `run` JSON-RPC method with the trailing args. Sunscreen sets up the sandbox, opens stdio JSON-RPC, and proxies stdout/stderr. + +## `hook` + +``` +sunscreen app hook [--json] +``` + +Manually trigger a registered hook. Normally hooks fire automatically (e.g. `after_build` on `chain build`). + +## `marketplace` + +``` +sunscreen app marketplace [--json] +``` + +List reference plugins maintained under `sunscreen-apps/`. Currently: + +- `sunscreen-apps/spl-token-2022` +- `sunscreen-apps/yellowstone-indexer` + +This is a static, embedded list. Remote registry support is planned. + +## Exit codes + +| Code | When | +|------|------| +| `0` | success | +| `3` | `sunscreen.yml` invalid | +| `4` | name conflict, missing source, version not semver | +| `5` | not in a workspace | +| `9` | plugin binary crashed, sandbox violation, or JSON-RPC protocol error | + +See [Plugin protocol](../plugin-protocol/index.md) for the JSON-RPC wire format. diff --git a/docs/site/src/reference/cli/chain.md b/docs/site/src/reference/cli/chain.md new file mode 100644 index 0000000..396cc54 --- /dev/null +++ b/docs/site/src/reference/cli/chain.md @@ -0,0 +1,102 @@ +# `chain` + +Workspace lifecycle: create, build, serve, doctor. + +## `new` + +``` +sunscreen chain new [FLAGS] +``` + +Create a new workspace at `.//`. + +| Flag | Default | Description | +|------|---------|-------------| +| `--framework ` | `anchor` | `anchor` or `pinocchio` | +| `--frontend ` | `none` | `none`, `react`, `solid` | +| `--programs ` | `` | comma-separated list of program names | +| `--license ` | `MIT OR Apache-2.0` | SPDX expression for generated `Cargo.toml` | +| `--git/--no-git` | `--git` | init a git repo with first commit | +| `--dry-run` | off | print planned files without writing | +| `--json` | off | machine-readable summary | + +**Exit codes:** `0` ok · `2` toolchain · `4` directory exists. + +**Examples:** + +```bash +sunscreen chain new my-app +sunscreen chain new my-app --framework anchor --frontend react +sunscreen chain new bare --framework pinocchio +sunscreen chain new multi --programs "core,governance,treasury" +``` + +## `build` + +``` +sunscreen chain build [FLAGS] +``` + +Run the build pipeline: `anchor build` (or `cargo build-sbf` for Pinocchio), then Codama client regeneration (if frontend is configured). + +| Flag | Default | Description | +|------|---------|-------------| +| `--no-codama` | off | skip Codama regeneration | +| `--headless` | off | NDJSON events on stdout, no TUI | +| `--release` | off | release profile build | +| `--json` | off | one summary object at the end | + +**Exit codes:** `0` ok · `2` toolchain missing · `5` no workspace · build-tool exit codes preserved on failure. + +**NDJSON events:** + +```json +{"event":"build_start","framework":"anchor","programs":["my_app"]} +{"event":"build_progress","step":"anchor_build"} +{"event":"build_ok","programs":["my_app"],"duration_ms":4200} +{"event":"codama_start","frontend":"react"} +{"event":"codama_ok","files_written":12} +{"event":"frontend_notified","path":"app/.sunscreen/reload"} +``` + +Full event list in [NDJSON events](../events.md). + +## `serve` + +``` +sunscreen chain serve [FLAGS] +``` + +Long-running supervised dev loop: validator + watcher + build pipeline + frontend notify. + +| Flag | Default | Description | +|------|---------|-------------| +| `--validator ` | auto | `surfpool`, `test-validator`, or omit for auto-detect with fallback | +| `--no-codama` | off | skip Codama on rebuild | +| `--headless` | off | NDJSON stream, no TUI | +| `--rpc-port ` | `8899` | bind validator RPC port | +| `--ws-port ` | `8900` | bind validator WS port | +| `--quiet` | off | suppress validator stdout in TUI | + +**Exit codes:** `0` ok (Ctrl-C) · `2` toolchain · `5` no workspace · `1` unexpected. + +**Termination:** Ctrl-C sends SIGTERM to the validator's process group, waits up to 5s, then SIGKILL. + +## `doctor` + +``` +sunscreen chain doctor [FLAGS] +``` + +Diagnose toolchain *and* workspace markers. + +| Flag | Default | Description | +|------|---------|-------------| +| `--fix-markers` | off | reconstruct safe non-appendable markers (see [Marker protocol](../markers.md)) | +| `--json` | off | flat array of `ToolReport` objects | + +Calls the same toolchain detectors as the top-level `sunscreen doctor`, plus marker integrity over your workspace. + +**Exit codes:** `0` ok · `2` something critical missing · `4` non-fixable marker drift. + +See also: [`doctor`](./doctor.md) for the toolchain-only command. diff --git a/docs/site/src/reference/cli/doctor.md b/docs/site/src/reference/cli/doctor.md new file mode 100644 index 0000000..2c413b2 --- /dev/null +++ b/docs/site/src/reference/cli/doctor.md @@ -0,0 +1,62 @@ +# `doctor` + +Detect installed toolchain versions. + +``` +sunscreen doctor [--json] +``` + +Outputs a table of tools sunscreen knows how to detect, with their installed version and availability flag. + +## Detected tools + +| Tool | Detected via | +|------|--------------| +| `rustc` | `rustc --version` | +| `cargo` | `cargo --version` | +| `anchor` | `anchor --version` | +| `solana` | `solana --version` | +| `cargo-build-sbf` | `cargo build-sbf --help` | +| `pnpm` | `pnpm --version` | +| `node` | `node --version` | +| `codama` | from local `node_modules/.bin/codama` | +| `surfpool` | `surfpool --version` | + +If a tool is missing, sunscreen reports `available: false` and a `next_step` hinting installation. Missing tools are only blocking when you actually run a command that needs them. + +## Human output + +``` +TOOL VERSION STATUS +rustc 1.79.0 ok +cargo 1.79.0 ok +anchor 0.30.1 ok +solana 1.18.18 ok +cargo-build-sbf 1.18.18 ok +pnpm 9.4.0 ok +node 20.13.1 ok +codama (not found) missing +surfpool (not found) missing +``` + +## `--json` output + +A flat array of `ToolReport` objects: + +```json +[ + {"tool":"rustc","version":"1.79.0","available":true,"next_step":null}, + {"tool":"anchor","version":"0.30.1","available":true,"next_step":null}, + {"tool":"codama","version":null,"available":false,"next_step":"pnpm add -D codama in your frontend, or sunscreen will install on demand"} +] +``` + +Use this in CI to assert your runner has the expected toolchain. + +## Exit codes + +| Code | When | +|------|------| +| `0` | always — `doctor` reports, it does not fail on missing tools. Inspect `available` per row. | + +For workspace-marker diagnostics, see [`chain doctor`](./chain.md#doctor). diff --git a/docs/site/src/reference/cli/generate.md b/docs/site/src/reference/cli/generate.md new file mode 100644 index 0000000..e7a9d5f --- /dev/null +++ b/docs/site/src/reference/cli/generate.md @@ -0,0 +1,70 @@ +# `generate` + +Generate artifacts from the IDL. + +``` +sunscreen generate [FLAGS] +``` + +`generate` is implicitly called by `chain build` and `chain serve`. Use it directly when you want to regenerate without rebuilding the program. + +## `clients` + +``` +sunscreen generate clients [FLAGS] +``` + +Run Codama against the workspace IDL and write a JavaScript/TypeScript client into `app/src/clients/` (or the path configured in `sunscreen.yml`). + +| Flag | Default | Description | +|------|---------|-------------| +| `--rebuild-config` | off | rewrite `codama.config.mjs` from scratch (use when IDL shape changes drastically) | +| `--out ` | from config | client output directory | +| `--json` | off | summary on stdout | + +**Requires:** `pnpm` on PATH (sunscreen uses `pnpm exec codama`). + +## `idl` + +``` +sunscreen generate idl [FLAGS] +``` + +Export a deterministic IDL into `idl/`. Useful for CI artifacts and clients consumed outside Codama. + +| Flag | Default | Description | +|------|---------|-------------| +| `--out ` | `idl/` | output directory | +| `--pretty` | on | format JSON with 2-space indent | + +The exported IDL is byte-identical between runs as long as the source hasn't changed (sorted fields, normalized numeric types). + +## `frontend-hooks` + +``` +sunscreen generate frontend-hooks [FLAGS] +``` + +Generate React Query or Solid Query hooks from the IDL. + +| Flag | Default | Description | +|------|---------|-------------| +| `--framework ` | from `sunscreen.yml` | `react` or `solid` | +| `--out ` | `app/src/hooks/` | output directory | +| `--json` | off | summary on stdout | + +For each instruction in the IDL, generates a hook (`useCreatePost`, `useReadPost`, …) wrapping the Codama client. The hook handles transaction building, signing, and refetching account queries. + +## Exit codes + +| Code | When | +|------|------| +| `0` | success | +| `2` | `pnpm` or required dependency missing | +| `3` | `sunscreen.yml` does not declare a frontend | +| `5` | not in a workspace | + +## Tips + +- `chain build` calls `generate clients` automatically. Run `generate` directly when you've edited the IDL by hand or need clients without rebuilding the `.so`. +- Re-runs are idempotent. Codama overwrites only files it owns; your hand-written code in `app/src/` is untouched. diff --git a/docs/site/src/reference/cli/index.md b/docs/site/src/reference/cli/index.md new file mode 100644 index 0000000..89856f1 --- /dev/null +++ b/docs/site/src/reference/cli/index.md @@ -0,0 +1,69 @@ +# CLI overview + +``` +sunscreen [GLOBAL_FLAGS] [ARGS] [FLAGS] +``` + +## Top-level commands + +| Command | What it does | +|---------|-------------| +| [`chain new`](./chain.md#new) | Create a new workspace (Anchor or Pinocchio) | +| [`chain build`](./chain.md#build) | Run anchor build + Codama regeneration | +| [`chain serve`](./chain.md#serve) | Supervised dev loop with local validator | +| [`chain doctor`](./chain.md#doctor) | Diagnose toolchain + workspace markers | +| [`scaffold`](./scaffold.md) | Add instruction, account, event, error, program, or recipe | +| [`generate`](./generate.md) | Generate IDL, Codama clients, frontend hooks | +| [`app`](./app.md) | Manage plugins (install, list, run, hook, marketplace) | +| [`doctor`](./doctor.md) | Detect installed toolchain versions | +| [`init`](./onboarding.md#init) | Interactive wizard for new users | +| [`examples`](./onboarding.md#examples) | Browse embedded example projects | +| [`quickstart`](./onboarding.md#quickstart) | Composite recipe shortcuts (token, nft, dao, blog) | +| [`wallet`](./onboarding.md#wallet) | Wallet helpers (new, airdrop) | +| [`deploy`](./onboarding.md#deploy) | Deploy programs to a network | +| [`learn`](./onboarding.md#learn) | Open a topic in the embedded learn index | + +## Global flags + +| Flag | What it does | +|------|-------------| +| `--json` | machine-readable output on stdout; human messages on stderr | +| `--no-color` | disable ANSI colors | +| `-v / -vv / -vvv` | verbosity (warn / info / debug) | +| `--help` | per-command help | +| `--version` | print sunscreen version | + +## Exit codes + +| Code | Meaning | +|------|---------| +| `0` | success | +| `1` | unexpected error (bug) — please report | +| `2` | toolchain missing (anchor, solana, cargo, pnpm, …) | +| `3` | invalid config (`sunscreen.yml` schema violation) | +| `4` | user input conflict (resource exists, ambiguous flag, …) | +| `5` | missing workspace (no `sunscreen.yml` upward from cwd) | +| `9` | plugin runtime failure | + +Full list with `next_step` strings in [Errors & exit codes](../errors.md). + +## Environment variables + +| Variable | Effect | +|----------|--------| +| `SUNSCREEN_CONFIG` | path to an alternative `sunscreen.yml` | +| `SUNSCREEN_WALLET` | path to a Solana keypair JSON file | +| `SUNSCREEN_NO_COLOR` | same as `--no-color` | +| `SUNSCREEN_LOG` | log filter (e.g. `info`, `debug,sunscreen::runtime=trace`) | +| `SUNSCREEN_FRAMEWORK` | override framework detection during command runs | + +## `--json` contract + +When you pass `--json`, sunscreen guarantees: + +- **One JSON object per command on stdout**, or **NDJSON** (one object per line) for streaming commands (`chain build --headless`, `chain serve --headless`). +- Human-readable status messages, errors, hints, and progress go to **stderr** — never mixed into stdout. +- Top-level shape: `{ "status": "ok"|"error", "code": "", "data": {…}, "next_step": "" }`. +- Error objects include `error.code`, `error.message`, and `error.next_step`. + +Machine integrations should always pass `--json` and parse stdout only. diff --git a/docs/site/src/reference/cli/onboarding.md b/docs/site/src/reference/cli/onboarding.md new file mode 100644 index 0000000..543508b --- /dev/null +++ b/docs/site/src/reference/cli/onboarding.md @@ -0,0 +1,101 @@ +# Onboarding commands + +Beginner-friendly shortcuts that compose other sunscreen commands. + +## `init` + +``` +sunscreen init [--non-interactive] [--name ] [--framework ] [--frontend ] +``` + +Interactive wizard that asks 3–5 questions and runs `chain new` under the hood. With `--non-interactive`, behaves like `chain new` with explicit flags. + +## `examples` + +``` +sunscreen examples [list|show |init [--out ]] +``` + +Browse embedded example projects. `init ` copies the example into a new directory. + +Available examples (embedded at compile time): + +- `counter` — minimal counter program. +- `token-faucet` — SPL token mint with a free-claim instruction. +- `nft-collection` — Metaplex NFT collection with mint. +- `dao-voting` — a stripped-down DAO voting program. + +## `quickstart` + +``` +sunscreen quickstart +``` + +Composite recipes for "I want a working X in 30 seconds": + +| Kind | What it builds | +|------|---------------| +| `token` | Anchor workspace + SPL Token recipe + React frontend with mint UI | +| `nft` | Anchor workspace + Metaplex NFT recipe + React frontend with mint UI | +| `dao` | Anchor workspace + DAO voting scaffolds | +| `blog` | Anchor workspace + CRUD `Post` resource + React frontend | + +Equivalent to running `chain new` + the matching `scaffold` recipe + `generate frontend-hooks`. + +## `wallet` + +``` +sunscreen wallet new [--out ] +sunscreen wallet airdrop --network --amount [--address ] +sunscreen wallet show [--network ] +``` + +| Subcommand | What it does | +|------------|-------------| +| `new` | Generate a new keypair at `~/.config/solana/id.json` (or `--out`) | +| `airdrop` | Request SOL from a network's faucet | +| `show` | Print the current wallet's pubkey and balance on a network | + +## `deploy` + +``` +sunscreen deploy [--network ] [--rpc-url ] [--with-idl] [--dry-run] [--json] +``` + +Build and deploy programs in the workspace to a Solana network. + +| Flag | Default | Description | +|------|---------|-------------| +| `--network` | `localnet` | `localnet`, `devnet`, `testnet`, `mainnet-beta` | +| `--rpc-url` | network default | override RPC endpoint | +| `--with-idl` | off | also publish IDL on chain | +| `--dry-run` | off | print plan only | +| `--json` | off | structured output | + +**Exit codes:** `0` ok · `2` toolchain · `4` insufficient balance / network unreachable · `5` no workspace. + +## `learn` + +``` +sunscreen learn [list|] +``` + +Open an embedded topic in the terminal pager. Topics: + +- `markers` — the marker protocol in 1 page. +- `pdas` — PDA basics. +- `idl-flow` — IDL → Codama → clients. +- `rent` — Solana rent in 1 page. + +`learn` requires no network. It's the offline equivalent of pointing users at the docs site. + +## Exit code: `next_step` contract + +Every onboarding error includes a `next_step` field in JSON output and a final line in human output telling the user exactly what to do. Example: + +```text +error: Network: rate-limited (exit 4) +next_step: Try the web faucet at https://faucet.solana.com/ or wait 10 minutes. +``` + +This contract is tested in `tests/errors_contract.rs` and is part of sunscreen's stable API surface. diff --git a/docs/site/src/reference/cli/scaffold.md b/docs/site/src/reference/cli/scaffold.md new file mode 100644 index 0000000..cac8d26 --- /dev/null +++ b/docs/site/src/reference/cli/scaffold.md @@ -0,0 +1,100 @@ +# `scaffold` + +Add code to an existing workspace. Primitives + recipes. + +``` +sunscreen scaffold [FLAGS] +``` + +All scaffold operations are **idempotent** (re-running with the same args is a no-op) and **marker-aware** (writes happen inside marker regions; hand-edits outside markers survive). See [Marker protocol](../markers.md). + +## Common flags + +| Flag | Default | Description | +|------|---------|-------------| +| `--program ` | required for most | which program to scaffold into | +| `--dry-run` | off | print planned diffs without writing | +| `--json` | off | machine-readable summary | +| `--force` | off | overwrite marker content even if a symbol exists | + +## Primitives + +### `instruction ` + +Add an instruction handler. + +```bash +sunscreen scaffold instruction CreatePost --program blog +``` + +Creates `programs/blog/src/instructions/create_post.rs`, registers it in `lib.rs` dispatch, and updates `instructions/mod.rs`. + +### `account ` + +Add an account struct. + +```bash +sunscreen scaffold account Post --program blog \ + --fields "title:string,body:string,author:pubkey" +``` + +`--fields` syntax: `name:type[,name:type…]`. See [CRUD recipe](../recipes/crud.md#field-types) for accepted types. + +### `event ` + +Add an event. + +```bash +sunscreen scaffold event PostCreated --program blog \ + --fields "post:pubkey,author:pubkey,timestamp:i64" +``` + +### `error ` + +Add an error variant. + +```bash +sunscreen scaffold error PostNotFound --program blog \ + --message "Post not found" +``` + +### `program ` + +Add a *new program* to an existing workspace. + +```bash +sunscreen scaffold program governance +``` + +Creates `programs/governance/`, updates `Cargo.toml` workspace members, `Anchor.toml` `[programs.localnet]`, and `sunscreen.yml`. + +## Recipes + +Composite scaffolds that combine primitives: + +| Noun | What it scaffolds | +|------|-------------------| +| [`crud `](../recipes/crud.md) | account + 4 instructions + 3 events + 2 errors + TS test | +| [`spl-token `](../recipes/spl-token.md) | SPL Token mint/transfer slice | +| [`metaplex-nft `](../recipes/metaplex-nft.md) | Metaplex NFT mint with metadata + master edition | + +Each recipe runs a dry-run preflight: if any of its constituent primitives would conflict, it bails before writing. + +## Plugin-provided nouns + +When a plugin declares `scaffold ` commands, sunscreen routes `sunscreen scaffold ` through that plugin. List available plugin scaffolds: + +```bash +sunscreen app commands --filter scaffold +``` + +## Exit codes + +| Code | When | +|------|------| +| `0` | success | +| `2` | toolchain missing (rare; only for recipes that compile-check) | +| `3` | invalid config | +| `4` | symbol already exists, ambiguous flag, or recipe preflight failure | +| `5` | not in a workspace | +| `9` | plugin runtime failure (plugin-routed nouns) | diff --git a/docs/site/src/reference/config/schema.md b/docs/site/src/reference/config/schema.md new file mode 100644 index 0000000..80d37d7 --- /dev/null +++ b/docs/site/src/reference/config/schema.md @@ -0,0 +1,105 @@ +# `sunscreen.yml` schema + +Single source of truth for a workspace. Generated by `chain new`, read by every other command. + +## Top-level shape + +```yaml +version: 1 +name: my-app +framework: anchor # anchor | pinocchio +frontend: react # none | react | solid +wallet: ~/.config/solana/id.json + +programs: + - name: my_app + path: programs/my_app + program_id: ~ # filled by deploy + +networks: + localnet: + rpc_url: http://127.0.0.1:8899 + devnet: + rpc_url: https://api.devnet.solana.com + mainnet-beta: + rpc_url: ~ # use --rpc-url at deploy time + +plugins: + - source: ./plugins/my-plugin + version: "0.2.0" + hooks: [after_build] + +runtime: + validator: surfpool # surfpool | test-validator | auto + codama: true + frontend_notify_path: app/.sunscreen/reload +``` + +## Field reference + +### Top level + +| Field | Type | Required | Default | Description | +|-------|------|----------|---------|-------------| +| `version` | int | yes | `1` | schema version. Sunscreen migrates older versions automatically. | +| `name` | string | yes | — | workspace name (kebab-case). | +| `framework` | enum | yes | — | `anchor` or `pinocchio`. | +| `frontend` | enum | no | `none` | `none`, `react`, `solid`. | +| `wallet` | path | no | solana-cli default | path to Solana keypair JSON. | +| `programs` | array | yes | — | one entry per program in the workspace. | +| `networks` | map | no | localnet+devnet | per-network RPC config. | +| `plugins` | array | no | `[]` | declared plugins. | +| `runtime` | object | no | — | dev-loop preferences. | + +### `programs[]` + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `name` | string | yes | snake_case program name. | +| `path` | path | yes | relative to workspace root. | +| `program_id` | string | no | filled by `deploy`; `null` until first deploy. | + +### `networks.` + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `rpc_url` | URL | no | overrides the network's default RPC endpoint. | + +### `plugins[]` + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `source` | string | yes | local path or git URL. | +| `version` | semver | yes | with or without `v` prefix. | +| `hooks` | array | no | list of hook names (`before_build`, `after_build`, `after_serve`). | + +### `runtime` + +| Field | Type | Default | Description | +|-------|------|---------|-------------| +| `validator` | enum | `auto` | `surfpool`, `test-validator`, `auto` (Surfpool if found, else test-validator). | +| `codama` | bool | `true` if `frontend != none` | run Codama during builds. | +| `frontend_notify_path` | path | `app/.sunscreen/reload` | file sunscreen touches on successful client regen. | + +## Environment overrides + +Any string field can be overridden via environment variable, name-prefixed with `SUNSCREEN_`: + +| Env var | Field overridden | +|---------|------------------| +| `SUNSCREEN_WALLET` | `wallet` | +| `SUNSCREEN_FRAMEWORK` | `framework` (per-invocation) | + +Env overrides take precedence over the YAML. + +## Migrations + +When sunscreen reads a `sunscreen.yml` with a lower `version` than the current binary supports, it migrates in-place (writes a backup `sunscreen.yml.bak`). Migrations are deterministic — same input, same output. + +## Validation + +`sunscreen.yml` is validated on every command. Failures exit with code `3` and a message naming the field: + +``` +error: invalid_config: programs[0].path: directory does not exist (programs/missing) +``` diff --git a/docs/site/src/reference/errors.md b/docs/site/src/reference/errors.md new file mode 100644 index 0000000..5567953 --- /dev/null +++ b/docs/site/src/reference/errors.md @@ -0,0 +1,94 @@ +# Errors & exit codes + +Every sunscreen error has three things: a numeric **exit code**, a stable **string code**, and an actionable **next_step**. + +## Exit codes + +| Exit | Code | Meaning | +|------|------|---------| +| `0` | — | success | +| `1` | `unexpected` | a bug — please report with `RUST_BACKTRACE=1` output | +| `2` | `toolchain_missing` | a required external tool (anchor, solana, …) was not found | +| `3` | `invalid_config` | `sunscreen.yml` failed schema validation | +| `4` | `user_input` | conflicting / ambiguous / unrecognized user request | +| `5` | `missing_workspace` | no `sunscreen.yml` found upward from cwd | +| `9` | `plugin_runtime` | a plugin crashed, violated the sandbox, or returned a protocol error | + +## Common error codes (string-level) + +These are stable identifiers you can match on in scripts. Subset, full list lives in [`src/error.rs`](https://github.com/Pantani/sunscreen/blob/main/src/error.rs). + +### Toolchain (exit 2) + +| `code` | `next_step` | +|--------|-------------| +| `toolchain_missing.anchor` | Install via AVM: `cargo install --git https://github.com/coral-xyz/anchor avm --locked && avm install latest` | +| `toolchain_missing.solana` | `sh -c "$(curl -sSfL https://release.solana.com/stable/install)"` | +| `toolchain_missing.pnpm` | `npm i -g pnpm` | +| `toolchain_missing.cargo_build_sbf` | Comes with the `solana` install — re-run the solana installer | + +### Invalid config (exit 3) + +| `code` | Trigger | +|--------|---------| +| `invalid_config.schema` | Top-level shape doesn't match v1 | +| `invalid_config.field.` | A specific field is wrong (path follows the YAML, e.g. `invalid_config.field.plugins.0.version`) | +| `invalid_config.version_unknown` | YAML declares a `version` newer than this binary supports | + +### User input (exit 4) + +| `code` | Trigger | +|--------|---------| +| `user_input.path_conflict` | Target directory exists (sunscreen refuses to overwrite without `--force`) | +| `user_input.symbol_exists` | A scaffold target (instruction, account, …) is already defined | +| `user_input.recipe_preflight` | A recipe's preflight detected conflicts | +| `user_input.ambiguous_flag` | Two flags conflict (e.g. `--dry-run` + `--force`) | +| `user_input.semver` | `--version` argument isn't valid semver | +| `network.rate_limited` | Faucet returned 429 | +| `network.unreachable` | RPC didn't respond | + +### Missing workspace (exit 5) + +| `code` | Trigger | +|--------|---------| +| `missing_workspace` | No `sunscreen.yml` in cwd or ancestors | + +### Plugin runtime (exit 9) + +| `code` | Trigger | +|--------|---------| +| `plugin_runtime.protocol` | Plugin emitted invalid JSON-RPC | +| `plugin_runtime.sandbox` | Plugin tried to write outside workspace or open a forbidden network connection | +| `plugin_runtime.crash` | Plugin exited unexpectedly (non-zero, no response) | +| `plugin_runtime.manifest` | Plugin manifest invalid or unreadable | + +## `next_step` contract + +Every error includes a `next_step` field — a short, imperative sentence telling you what to do. This is part of the stable surface (tested by `tests/errors_contract.rs`). + +Human-readable form: +``` +error: user_input.symbol_exists: instruction "create_post" already exists (exit 4) +next_step: Pass --force to overwrite, or pick a different name. +``` + +JSON form: +```json +{ + "status": "error", + "error": { + "code": "user_input.symbol_exists", + "message": "instruction \"create_post\" already exists", + "next_step": "Pass --force to overwrite, or pick a different name" + } +} +``` + +## Reporting bugs + +Exit `1` (`unexpected`) is a bug. Include in the issue: + +- `sunscreen --version` +- The full command you ran +- Output with `RUST_BACKTRACE=1` +- `sunscreen doctor --json` diff --git a/docs/site/src/reference/events.md b/docs/site/src/reference/events.md new file mode 100644 index 0000000..6be4917 --- /dev/null +++ b/docs/site/src/reference/events.md @@ -0,0 +1,72 @@ +# NDJSON events + +When you pass `--headless` (or `--json` to streaming commands), sunscreen emits **one JSON object per line** on stdout. Use this for editor integrations, CI parsers, and dashboards. + +## Event envelope + +Every event has at least: + +```json +{ + "event": "", + "ts": "2026-06-02T14:33:12.412Z" +} +``` + +Plus event-specific fields. + +## Build pipeline events + +Emitted by `chain build` and the build phase of `chain serve`. + +| Event | When | Fields | +|-------|------|--------| +| `build_start` | pipeline starts | `framework`, `programs[]` | +| `build_progress` | step transition | `step` (`anchor_build` / `cargo_build_sbf` / `codama`) | +| `build_ok` | pipeline succeeded | `programs[]`, `duration_ms` | +| `build_fail` | pipeline failed | `step`, `exit_code`, `stderr_tail` | + +## Codama events + +| Event | When | Fields | +|-------|------|--------| +| `codama_start` | Codama invocation begins | `frontend` | +| `codama_ok` | clients written | `files_written` (int), `duration_ms` | +| `codama_fail` | Codama failed | `exit_code`, `stderr_tail` | + +## Frontend notify + +| Event | When | Fields | +|-------|------|--------| +| `frontend_notified` | reload file touched | `path` | + +## Serve / watcher events + +Emitted by `chain serve --headless`. + +| Event | When | Fields | +|-------|------|--------| +| `serve_start` | server ready | `rpc_url`, `ws_url`, `validator` | +| `validator_log` | validator wrote a line | `line` (string) | +| `watcher_batch` | file changes debounced | `paths[]` (relative), `kind` (`save`/`delete`) | +| `pipeline_triggered` | watcher kicked a build | `paths[]` | +| `serve_stop` | shutdown complete | `reason` (`ctrl_c`/`error`) | + +## Toolchain events + +Emitted by any command that reaches a missing toolchain. + +| Event | Fields | +|-------|--------| +| `toolchain_missing` | `tool`, `next_step` | + +## Stability + +The set of events listed here is part of sunscreen's stable API. Adding new events is non-breaking. Removing or renaming events is a breaking change handled via the SemVer policy in [Roadmap](../contributing/roadmap.md). + +## Parsing tips + +- Treat unknown events as informational. Do not fail on new event names. +- `event` is always a stable string. Use it as the discriminator. +- Stdout is line-buffered; partial lines mean the process is still writing — wait for `\n`. +- Human messages may still hit stderr — don't merge stderr into your parser. diff --git a/docs/site/src/reference/markers.md b/docs/site/src/reference/markers.md new file mode 100644 index 0000000..c5b8e71 --- /dev/null +++ b/docs/site/src/reference/markers.md @@ -0,0 +1,76 @@ +# Marker protocol + +Markers are how sunscreen edits files it has generated, without clobbering your hand-edits. + +## Anatomy + +```rust +// sunscreen:begin {generator=instructions} +pub use create_post::*; +pub use read_post::*; +// sunscreen:end +``` + +A marker is a *region* delimited by two comment lines: + +- `// sunscreen:begin {generator=}` — opens the region; carries the generator tag (`dispatch`, `instructions`, `account`, `event`, `error`, `error_variants`). +- `// sunscreen:end` — closes the region. + +Sunscreen edits **only the content between** the markers. Everything outside is yours. + +## Why markers exist + +When you re-run `sunscreen scaffold instruction Foo`, sunscreen needs to: + +1. Create a new file (`foo.rs`). +2. Edit `instructions/mod.rs` to re-export from it. +3. Edit `lib.rs` to register the dispatcher. + +Marker regions tell sunscreen *where* to inject in step 2 and 3, without re-parsing your whole file with `syn`. + +## Generator tags + +| Tag | Lives in | Content | +|-----|----------|---------| +| `dispatch` | `lib.rs` | `pub fn (...) -> Result<()> { ... }` wrappers | +| `instructions` | `instructions/mod.rs` | `pub use ::*;` re-exports | +| `state` | `state/mod.rs` | `pub mod ;` declarations | +| `events` | `events.rs` | `pub use ::*;` re-exports | +| `errors` | `errors.rs` | `pub use ::*;` re-exports | +| `error_variants` | inside an error enum | enum variants like `#[msg("…")] Foo,` | + +## Editing rules + +You **may** edit outside markers. Sunscreen will not touch your changes. + +You **may not** rely on hand-edits *inside* markers surviving a regeneration. The content of markers is regenerated from sunscreen's templates. + +If you absolutely need a hand-customized version of generated code, copy it out of the marker into the unmanaged area below it. + +## Drift and repair + +Sometimes a file gets into a state sunscreen can't safely edit (you deleted a marker; the file was scaffolded by an older sunscreen version; merge conflict left stray markers). Detect and repair: + +```bash +sunscreen chain doctor +sunscreen chain doctor --fix-markers +``` + +`--fix-markers` only rebuilds markers when it can do so *safely*: + +| Site | Repair behaviour | +|------|------------------| +| `instructions/mod.rs` `instructions` marker missing | reconstruct, listing files present in `instructions/` | +| `lib.rs` `dispatch` marker missing | reconstruct **only** if all generated instruction files define `pub fn handler` (else refuse) | +| `errors.rs` `error_variants` marker missing | reconstruct **only** if the enum body is empty or clearly delimited (else refuse) | + +When `--fix-markers` refuses, you get an exit `4` with a description of the ambiguity and a manual fix path. + +## Implementation notes (for the curious) + +Sunscreen's marker engine lives in [`src/rustpatch/`](https://github.com/Pantani/sunscreen/tree/main/src/rustpatch). It uses line-based parsing (not AST) for resilience — even malformed Rust around the markers is fine as long as the marker pair is intact. + +## See also + +- [Incremental scaffolding](../concepts/incremental-scaffolding.md) — the mental model. +- [`chain doctor`](./cli/chain.md#doctor) — the repair command. diff --git a/docs/site/src/reference/plugin-protocol/index.md b/docs/site/src/reference/plugin-protocol/index.md new file mode 100644 index 0000000..62eb5f0 --- /dev/null +++ b/docs/site/src/reference/plugin-protocol/index.md @@ -0,0 +1,118 @@ +# Plugin protocol + +Sunscreen plugins are external binaries that speak two transport layers: + +- **stdio JSON-RPC** (default, used today). +- **gRPC** (defined in `proto/plugin.proto`, planned for richer plugins). + +## Manifest + +Each plugin ships a `plugin.toml` next to its binary: + +```toml +[plugin] +name = "yellowstone-indexer" +version = "0.3.0" +description = "Scaffold a Yellowstone gRPC indexer slice." +entry = "./bin/plugin" + +[permissions] +workspace_write = true # default: false +network = false # if true, prompts user at install time +declared_paths = [] # extra read-only paths outside workspace + +[commands.scaffold] +indexer = "Scaffold a Yellowstone indexer slice" +listener = "Scaffold an event listener" + +[commands.hook] +after_build = "Update indexer config when IDL changes" +``` + +## JSON-RPC methods + +Sunscreen calls these on the plugin via stdin/stdout. One JSON object per line. + +### `commands` + +Request: +```json +{"jsonrpc":"2.0","id":1,"method":"commands"} +``` + +Response: +```json +{"jsonrpc":"2.0","id":1,"result":{ + "scaffold": {"indexer": "…", "listener": "…"}, + "hook": {"after_build": "…"} +}} +``` + +### `run` + +Request: +```json +{"jsonrpc":"2.0","id":2,"method":"run","params":{ + "command": "scaffold:indexer", + "args": ["Trades"], + "flags": {"program": "app"}, + "workspace_root": "/abs/path/to/workspace", + "config": {"version": 1, "name": "my-app", "..." : "..."} +}} +``` + +Response (success): +```json +{"jsonrpc":"2.0","id":2,"result":{ + "files_written": ["programs/app/src/instructions/index_trades.rs", "..."], + "summary": "scaffolded indexer Trades" +}} +``` + +Response (error): +```json +{"jsonrpc":"2.0","id":2,"error":{ + "code": 4, + "message": "instruction index_trades already exists", + "data": {"next_step": "pass --force or pick a different name"} +}} +``` + +### `hook` + +Request: +```json +{"jsonrpc":"2.0","id":3,"method":"hook","params":{ + "name": "after_build", + "context": {"workspace_root": "...", "idl": {...}} +}} +``` + +Response: same shape as `run`. + +## Sandbox + +Sunscreen enforces: + +| Capability | Default | Toggle | +|------------|---------|--------| +| Read workspace files | allowed | always on | +| Write workspace files | denied | `permissions.workspace_write = true` | +| Read outside workspace | denied | declare each path in `permissions.declared_paths` | +| Network access | denied | `permissions.network = true` (user-approved at install) | +| Spawn subprocesses | denied | not toggleable in current version | + +Sandbox violations terminate the session and return exit `9` to the caller. + +## gRPC contract + +`proto/plugin.proto` (in the sunscreen repo) defines the streaming-friendly equivalent of the JSON-RPC surface. Use when: + +- Plugin needs bidirectional streaming (live progress, log forwarding). +- Plugin is implemented in a non-stdio-friendly runtime (e.g. JVM). + +The gRPC transport is wire-defined but not yet end-to-end implemented in the runtime. + +## Conformance tests + +Reference plugins under [`sunscreen-apps/`](https://github.com/Pantani/sunscreen-apps) exercise the full protocol surface and serve as templates. The `spl-token-2022` plugin is the canonical example. diff --git a/docs/site/src/reference/recipes/crud.md b/docs/site/src/reference/recipes/crud.md new file mode 100644 index 0000000..1d2b62e --- /dev/null +++ b/docs/site/src/reference/recipes/crud.md @@ -0,0 +1,93 @@ +# CRUD recipe + +``` +sunscreen scaffold crud --program [FLAGS] +``` + +## Flags + +| Flag | Default | Description | +|------|---------|-------------| +| `--program ` | required | which program to scaffold into | +| `--fields ` | `title:string,body:string` | account fields | +| `--frontend-hook` | off | also generate matching React/Solid hooks | +| `--dry-run` | off | print plan only | +| `--json` | off | machine-readable summary | +| `--force` | off | overwrite marker content even if a symbol exists | + +## Field types + +`--fields` accepts comma-separated `name:type` pairs: + +| Spec | Anchor type | Notes | +|------|-------------|-------| +| `name:string` | `String` | UTF-8, max 256 bytes by default | +| `name:bool` | `bool` | | +| `name:u8` … `name:u128` | `u8` … `u128` | | +| `name:i8` … `name:i128` | `i8` … `i128` | | +| `name:pubkey` | `Pubkey` | | +| `name:vec` | `Vec` | max 256 bytes | +| `name:option` | `Option` | optional fields | + +Types outside this set require hand-editing the generated account. + +## Generated files + +For `scaffold crud Post --program blog`: + +| Path | Status | +|------|--------| +| `programs/blog/src/state/post.rs` | created | +| `programs/blog/src/state/mod.rs` | patched (marker) | +| `programs/blog/src/instructions/create_post.rs` | created | +| `programs/blog/src/instructions/read_post.rs` | created | +| `programs/blog/src/instructions/update_post.rs` | created | +| `programs/blog/src/instructions/delete_post.rs` | created | +| `programs/blog/src/instructions/mod.rs` | patched | +| `programs/blog/src/lib.rs` | patched (dispatch markers) | +| `programs/blog/src/events.rs` | patched (3 new variants) | +| `programs/blog/src/errors.rs` | patched (2 new variants) | +| `tests/post.spec.ts` | created | + +## Generated instructions + +| Instruction | Accounts | Effect | +|-------------|----------|--------| +| `create_post` | `[authority: Signer, post: Account (init), system_program]` | initializes a `Post` PDA seeded by authority + name | +| `read_post` | `[post: Account]` | view-only; emits `PostRead` if the read fee model is enabled (off by default) | +| `update_post` | `[authority: Signer, post: Account (mut, has_one = authority)]` | updates fields, emits `PostUpdated` | +| `delete_post` | `[authority: Signer, post: Account (close = authority, has_one = authority)]` | closes the account, emits `PostDeleted` | + +PDA seeds: `["post", authority.key, name.as_bytes()]`. + +## Generated events + +- `PostCreated { post: Pubkey, author: Pubkey, timestamp: i64 }` +- `PostUpdated { post: Pubkey, timestamp: i64 }` +- `PostDeleted { post: Pubkey, timestamp: i64 }` + +## Generated errors + +- `PostNotFound` — account discriminator mismatch. +- `PostUnauthorized` — signer is not the `authority` on the account. + +## Frontend hook (`--frontend-hook`) + +For React + React Query: + +```ts +useCreatePost({ authority, name, fields }); +useReadPost({ post }); +useUpdatePost({ authority, post, fields }); +useDeletePost({ authority, post }); +``` + +Hooks invalidate the relevant queries on mutation success. Generated in `app/src/hooks/post.ts`. + +## Exit codes + +| Code | When | +|------|------| +| `0` | success | +| `4` | preflight conflict (symbol already exists; pass `--force` to overwrite marker content) | +| `5` | not in a workspace | diff --git a/docs/site/src/reference/recipes/index.md b/docs/site/src/reference/recipes/index.md new file mode 100644 index 0000000..e571186 --- /dev/null +++ b/docs/site/src/reference/recipes/index.md @@ -0,0 +1,27 @@ +# Recipes + +Composite scaffolds built on top of [`scaffold` primitives](../cli/scaffold.md). + +Each recipe runs as a single command and produces a complete, working slice — account, instructions, events, errors, tests, optional frontend hooks. All operations are idempotent and marker-aware. + +## Available recipes + +| Recipe | What it generates | +|--------|------------------| +| [CRUD](./crud.md) | An account + 4 instructions (`create`/`read`/`update`/`delete`) + 3 events + 2 errors + TS test | +| [SPL Token](./spl-token.md) | An SPL Token mint + transfer slice | +| [Metaplex NFT](./metaplex-nft.md) | A Metaplex NFT mint with metadata + master edition | + +## Recipe contract + +Every recipe guarantees: + +- **Preflight dry-run** before any write. If any underlying primitive would conflict, the recipe bails with exit `4` and no partial writes. +- **Single JSON object** under `--json`. The summary contains every file touched and every symbol generated. +- **Idempotency**. Same inputs → same outputs. Re-running is a no-op. +- **Marker-safety**. All writes happen inside marker regions. Hand-edits outside markers survive. +- **Optional frontend coupling**. When the workspace declares a frontend, `--frontend-hook` regenerates the matching React/Solid hook. + +## Writing a custom recipe + +Sunscreen doesn't expose user-defined recipes natively. The supported way to extend the recipe surface is via a plugin that registers `scaffold ` commands. See [Plugin protocol](../plugin-protocol/index.md). diff --git a/docs/site/src/reference/recipes/metaplex-nft.md b/docs/site/src/reference/recipes/metaplex-nft.md new file mode 100644 index 0000000..db84543 --- /dev/null +++ b/docs/site/src/reference/recipes/metaplex-nft.md @@ -0,0 +1,84 @@ +# Metaplex NFT recipe + +``` +sunscreen scaffold metaplex-nft --program [FLAGS] +``` + +Generates a Metaplex Token Metadata-compatible NFT mint slice. + +## Flags + +| Flag | Default | Description | +|------|---------|-------------| +| `--program ` | required | which program to scaffold into | +| `--collection ` | none | optional collection account name | +| `--frontend-hook` | off | generate matching React/Solid hook | +| `--dry-run` | off | print plan only | +| `--json` | off | summary on stdout | +| `--force` | off | overwrite marker content even on conflict | + +## Generated files + +For `scaffold metaplex-nft MyNft --program app`: + +| Path | Status | +|------|--------| +| `programs/app/src/state/my_nft.rs` | created | +| `programs/app/src/instructions/mint_my_nft.rs` | created | +| `programs/app/src/instructions/mod.rs` | patched | +| `programs/app/src/lib.rs` | patched (dispatch) | +| `programs/app/src/events.rs` | patched | +| `programs/app/src/errors.rs` | patched | +| `tests/my_nft.spec.ts` | created | + +## Generated instruction: `mint_my_nft` + +Accounts: + +``` +[ + authority: Signer, + mint: UncheckedAccount (init), + token_account: Account (init_if_needed), + metadata: UncheckedAccount (metaplex pda), + master_edition: UncheckedAccount (metaplex pda), + ... + system_program, token_program, associated_token_program, rent +] +``` + +Effect: + +1. Creates a mint with 0 decimals (NFT convention). +2. Mints 1 token to the authority's associated token account. +3. CPI to Metaplex Token Metadata to create `metadata` + `master_edition`. +4. Emits `NftMinted { mint, owner, uri }`. + +## Generated events + +- `NftMinted { mint, owner, uri }` + +## Generated errors + +- `MetadataUriTooLong` +- `MintAuthorityMismatch` + +## Frontend hook (`--frontend-hook`) + +```ts +useMintMyNft({ authority, uri, name, symbol }); +``` + +Builds, signs, and submits the transaction. Invalidates the `useMyNftCollection` query on success. + +## Notes + +- The recipe uses Metaplex Token Metadata via CPI. Your `Cargo.toml` gets `mpl-token-metadata` added as a dep on first scaffold. +- `--collection` wires the minted NFT into a Metaplex collection account. + +## Exit codes + +| Code | When | +|------|------| +| `0` | success | +| `4` | preflight conflict | +| `5` | not in a workspace | diff --git a/docs/site/src/reference/recipes/spl-token.md b/docs/site/src/reference/recipes/spl-token.md new file mode 100644 index 0000000..3ef7063 --- /dev/null +++ b/docs/site/src/reference/recipes/spl-token.md @@ -0,0 +1,66 @@ +# SPL Token recipe + +``` +sunscreen scaffold spl-token --program [FLAGS] +``` + +Generates an SPL Token mint+transfer slice inside an existing program. + +## Flags + +| Flag | Default | Description | +|------|---------|-------------| +| `--program ` | required | which program to scaffold into | +| `--decimals ` | `9` | mint decimals | +| `--frontend-hook` | off | generate matching React/Solid hook | +| `--dry-run` | off | print plan only | +| `--json` | off | summary on stdout | +| `--force` | off | overwrite marker content even on conflict | + +## Generated files + +For `scaffold spl-token MyToken --program app`: + +| Path | Status | +|------|--------| +| `programs/app/src/state/my_token.rs` | created (mint metadata account) | +| `programs/app/src/instructions/init_my_token.rs` | created | +| `programs/app/src/instructions/mint_my_token.rs` | created | +| `programs/app/src/instructions/transfer_my_token.rs` | created | +| `programs/app/src/instructions/mod.rs` | patched | +| `programs/app/src/lib.rs` | patched (dispatch) | +| `programs/app/src/events.rs` | patched | +| `programs/app/src/errors.rs` | patched | +| `tests/my_token.spec.ts` | created | + +## Generated instructions + +| Instruction | Effect | +|-------------|--------| +| `init_my_token` | Creates the mint PDA, sets authority and decimals | +| `mint_my_token` | Mints `amount` to a destination token account (CPI to `spl-token`) | +| `transfer_my_token` | Transfers `amount` between token accounts | + +## Generated events + +- `TokenInitialized { mint, authority, decimals }` +- `TokenMinted { mint, recipient, amount }` +- `TokenTransferred { from, to, amount }` + +## Generated errors + +- `MintUnauthorized` +- `InsufficientBalance` + +## Notes + +- This recipe uses the classic SPL Token program (`Tokenkeg…`). For SPL Token-2022, install the [`sunscreen-apps/spl-token-2022`](../plugin-protocol/index.md) plugin and use `sunscreen scaffold spl-token-2022 …`. +- The mint is a PDA seeded by `["mint", name.as_bytes()]`, so the same name yields the same address per program. + +## Exit codes + +| Code | When | +|------|------| +| `0` | success | +| `4` | preflight conflict | +| `5` | not in a workspace | diff --git a/docs/site/theme/css/admonish.css b/docs/site/theme/css/admonish.css new file mode 100644 index 0000000..05b35f0 --- /dev/null +++ b/docs/site/theme/css/admonish.css @@ -0,0 +1,35 @@ +/* Sunscreen — admonish callouts tuned to Eclipse palette */ + +:root { + --md-admonition-bg-color: var(--bg-elevated); + --md-admonition-fg-color: var(--fg); +} + +.admonition { + border-radius: var(--radius-md); + border: 1px solid var(--border); + background: var(--bg-elevated); + margin: 1.4rem 0; + padding: 0; + box-shadow: var(--shadow-1); +} +.admonition-title { + padding: 0.55rem 1rem; + font-weight: 600; + border-bottom: 1px solid var(--border); + color: var(--fg-strong); +} +.admonition > *:not(.admonition-title) { + padding: 0.4rem 1rem; +} +.admonition > p:last-child { padding-bottom: 1rem; } + +.admonition.note { border-left: 3px solid var(--info); } +.admonition.tip, +.admonition.hint { border-left: 3px solid var(--success); } +.admonition.warning, +.admonition.caution { border-left: 3px solid var(--warning); } +.admonition.danger, +.admonition.error { border-left: 3px solid var(--danger); } +.admonition.example, +.admonition.important { border-left: 3px solid var(--brand); } diff --git a/docs/site/theme/css/general.css b/docs/site/theme/css/general.css new file mode 100644 index 0000000..b2f2295 --- /dev/null +++ b/docs/site/theme/css/general.css @@ -0,0 +1,276 @@ +/* Sunscreen — typography, spacing, hero. Editorial dark-first. */ + +:root { + --font-sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif; + --font-mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace; + + --content-max-width: 780px; + --sidebar-width: 290px; + + --radius-sm: 6px; + --radius-md: 10px; + --radius-lg: 14px; +} + +html, body { + font-family: var(--font-sans); + font-feature-settings: "ss01", "cv11"; + font-size: 16px; + line-height: 1.65; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.content main { + max-width: var(--content-max-width); + padding-top: 2.5rem; + padding-bottom: 6rem; +} + +/* Headings: editorial scale */ +.content h1 { + font-size: 2.8rem; + line-height: 1.15; + letter-spacing: -0.02em; + font-weight: 700; + margin-top: 0; + margin-bottom: 1.5rem; + color: var(--fg-strong); +} + +.content h2 { + font-size: 1.85rem; + line-height: 1.25; + letter-spacing: -0.015em; + font-weight: 650; + margin-top: 3rem; + margin-bottom: 1rem; + padding-top: 0.5rem; + border-top: 1px solid var(--border); + color: var(--fg-strong); +} + +.content h3 { + font-size: 1.35rem; + line-height: 1.35; + font-weight: 600; + margin-top: 2.2rem; + margin-bottom: 0.6rem; + color: var(--fg-strong); +} + +.content h4 { + font-size: 1.05rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; + color: var(--fg-muted); + margin-top: 1.6rem; +} + +.content p { + margin: 0 0 1.1rem 0; +} + +.content a { + color: var(--brand); + text-decoration: none; + border-bottom: 1px solid transparent; + transition: border-color 80ms ease, color 80ms ease; +} +.content a:hover { + color: var(--brand-soft); + border-bottom-color: var(--brand-soft); +} + +/* Inline code */ +.content code { + font-family: var(--font-mono); + font-size: 0.92em; + background: var(--bg-inline-code); + color: var(--inline-code-color); + padding: 0.12em 0.4em; + border-radius: var(--radius-sm); + border: 1px solid var(--border); +} + +/* Code blocks */ +.content pre { + background: var(--bg-code); + border: 1px solid var(--border); + border-radius: var(--radius-md); + padding: 1rem 1.1rem; + box-shadow: var(--shadow-1); + overflow-x: auto; +} +.content pre code { + background: transparent; + border: none; + padding: 0; + font-size: 0.88rem; + line-height: 1.6; + color: var(--fg); +} + +/* Blockquotes */ +.content blockquote { + background: var(--quote-bg); + border-left: 3px solid var(--brand); + padding: 0.8rem 1.2rem; + margin: 1.4rem 0; + border-radius: 0 var(--radius-md) var(--radius-md) 0; + color: var(--fg); +} +.content blockquote p:last-child { margin-bottom: 0; } + +/* Tables */ +.content table { + width: 100%; + border-collapse: collapse; + margin: 1.4rem 0; + font-size: 0.95rem; + border: 1px solid var(--border); + border-radius: var(--radius-md); + overflow: hidden; +} +.content table th { + background: var(--table-header-bg); + text-align: left; + font-weight: 600; + padding: 0.65rem 0.9rem; + color: var(--fg-strong); + border-bottom: 1px solid var(--border-strong); +} +.content table td { + padding: 0.6rem 0.9rem; + border-top: 1px solid var(--border); +} + +/* Sidebar polish */ +.sidebar { + font-size: 0.93rem; + border-right: 1px solid var(--border); + width: var(--sidebar-width); +} +.sidebar .chapter li.chapter-item { + padding: 0.05rem 0; +} +.sidebar .chapter li.chapter-item a { + color: var(--fg); + padding: 0.32rem 0.7rem; + border-radius: var(--radius-sm); + transition: background 80ms ease; +} +.sidebar .chapter li.chapter-item a:hover { + background: var(--border); +} +.sidebar .chapter li.chapter-item a.active { + color: var(--brand); + background: rgba(244, 163, 64, 0.08); + font-weight: 600; +} +.sidebar .part-title { + text-transform: uppercase; + letter-spacing: 0.08em; + font-size: 0.74rem; + color: var(--fg-muted); + font-weight: 600; + padding: 1.2rem 0.7rem 0.4rem; +} + +/* Top menu / nav buttons */ +.menu-title { + font-weight: 600; + letter-spacing: -0.01em; +} +.icon-button { + color: var(--icons); +} +.icon-button:hover { + color: var(--icons-hover); +} + +/* Hero on landing */ +.hero-cta { + display: flex; + gap: 0.75rem; + flex-wrap: wrap; + margin: 1.5rem 0 2rem 0; +} +.hero-cta a { + display: inline-block; + padding: 0.7rem 1.2rem; + border-radius: var(--radius-md); + background: var(--brand); + color: var(--bg); + font-weight: 600; + border-bottom: none; + transition: transform 80ms ease, background 80ms ease; +} +.hero-cta a:hover { + background: var(--brand-soft); + border-bottom: none; + transform: translateY(-1px); +} +.hero-cta a:nth-child(2) { + background: transparent; + color: var(--brand); + border: 1px solid var(--brand); +} +.hero-cta a:nth-child(2):hover { + background: rgba(244, 163, 64, 0.08); +} + +/* Card grid on landing */ +.cards { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); + gap: 1.1rem; + margin: 1.5rem 0 2.5rem 0; +} +.card { + background: var(--bg-elevated); + border: 1px solid var(--border); + border-radius: var(--radius-lg); + padding: 1.3rem 1.4rem; + transition: border-color 100ms ease, transform 100ms ease; +} +.card:hover { + border-color: var(--brand); + transform: translateY(-2px); +} +.card h3 { + margin-top: 0 !important; + margin-bottom: 0.5rem !important; +} +.card a { + color: var(--brand); + font-weight: 500; + border-bottom: none; +} + +/* Selection */ +::selection { + background: var(--brand); + color: var(--bg); +} + +/* Scrollbars (webkit) */ +::-webkit-scrollbar { + width: 10px; + height: 10px; +} +::-webkit-scrollbar-track { background: transparent; } +::-webkit-scrollbar-thumb { + background: var(--border-strong); + border-radius: 6px; +} +::-webkit-scrollbar-thumb:hover { background: var(--fg-muted); } + +/* Reduce motion */ +@media (prefers-reduced-motion: reduce) { + *, *::before, *::after { + transition: none !important; + animation: none !important; + } +} diff --git a/docs/site/theme/css/variables.css b/docs/site/theme/css/variables.css new file mode 100644 index 0000000..d2b31a6 --- /dev/null +++ b/docs/site/theme/css/variables.css @@ -0,0 +1,104 @@ +/* Sunscreen — Eclipse palette + * Dark-first. Light variant inherits the same hue family, + * lifted by ~88% to keep the brand âmbar warm against paper. + */ + +:root, +.sunscreen-dark, +.navy { + --bg: #0B0D12; + --bg-elevated: #13161D; + --bg-code: #13161D; + --bg-inline-code: #1A1E27; + --fg: #E8E6E1; + --fg-strong: #FFFFFF; + --fg-muted: #8A8B92; + --fg-faint: #5C5E66; + + --brand: #F4A340; + --brand-soft: #FBD38D; + --brand-strong: #E08B1A; + + --accent: #7AB7FF; + --accent-soft: #A8D0FF; + + --border: #1F232C; + --border-strong: #2A2F3A; + + --success: #6FCF97; + --warning: #F4C462; + --danger: #F47272; + --info: #7AB7FF; + + --shadow-1: 0 1px 2px rgba(0,0,0,0.4); + --shadow-2: 0 8px 24px rgba(0,0,0,0.45); + + /* mdBook variable bridge */ + --bg: var(--bg); + --fg: var(--fg); + --sidebar-bg: var(--bg-elevated); + --sidebar-fg: var(--fg); + --sidebar-non-existant: var(--fg-faint); + --sidebar-active: var(--brand); + --sidebar-spacer: var(--border); + --scrollbar: var(--border-strong); + --icons: var(--fg-muted); + --icons-hover: var(--brand); + --links: var(--brand); + --inline-code-color: var(--brand-soft); + --theme-popup-bg: var(--bg-elevated); + --theme-popup-border: var(--border); + --theme-hover: var(--border); + --quote-bg: var(--bg-elevated); + --quote-border: var(--brand); + --warning-border: var(--warning); + --table-border-color: var(--border); + --table-header-bg: var(--bg-elevated); + --table-alternate-bg: var(--bg-elevated); + --searchbar-border-color: var(--border); + --searchbar-bg: var(--bg-elevated); + --searchbar-fg: var(--fg); + --searchbar-shadow-color: transparent; + --searchresults-header-fg: var(--fg-muted); + --searchresults-border-color: var(--border); + --searchresults-li-bg: var(--bg-elevated); + --search-mark-bg: var(--brand-soft); +} + +/* Light fallback — kept on-brand, used if user toggles to "light" */ +.light, +.rust, +.ayu, +.coal { + --bg: #FAF8F4; + --bg-elevated: #F2EFE8; + --bg-code: #1A1E27; + --bg-inline-code: #EEEAE0; + --fg: #1A1C22; + --fg-strong: #0B0D12; + --fg-muted: #6B6D75; + --fg-faint: #A8AAB0; + + --brand: #B8761E; + --brand-soft: #D89A3A; + --brand-strong: #8B5A14; + + --accent: #2E6FBF; + --accent-soft: #5A92D8; + + --border: #E2DDD2; + --border-strong: #C9C3B5; + + --sidebar-bg: var(--bg-elevated); + --sidebar-fg: var(--fg); + --sidebar-active: var(--brand); + --links: var(--brand); + --inline-code-color: var(--brand-strong); + --theme-popup-bg: var(--bg-elevated); + --quote-bg: var(--bg-elevated); + --quote-border: var(--brand); + --table-border-color: var(--border); + --table-header-bg: var(--bg-elevated); + --searchbar-bg: #FFFFFF; + --searchresults-li-bg: var(--bg-elevated); +} diff --git a/docs/site/theme/favicon.svg b/docs/site/theme/favicon.svg new file mode 100644 index 0000000..3206af4 --- /dev/null +++ b/docs/site/theme/favicon.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/docs/site/theme/js/mermaid-init.js b/docs/site/theme/js/mermaid-init.js new file mode 100644 index 0000000..039c960 --- /dev/null +++ b/docs/site/theme/js/mermaid-init.js @@ -0,0 +1,26 @@ +// Sunscreen — Mermaid theme aligned with Eclipse palette. +(function () { + if (typeof window === "undefined") return; + window.addEventListener("load", function () { + if (typeof mermaid === "undefined") return; + mermaid.initialize({ + startOnLoad: true, + theme: "base", + themeVariables: { + background: "#0B0D12", + primaryColor: "#13161D", + primaryTextColor: "#E8E6E1", + primaryBorderColor: "#2A2F3A", + lineColor: "#8A8B92", + secondaryColor: "#1A1E27", + tertiaryColor: "#13161D", + nodeBorder: "#F4A340", + edgeLabelBackground: "#0B0D12", + fontFamily: "Inter, -apple-system, sans-serif", + fontSize: "14px" + }, + flowchart: { curve: "basis", padding: 14 }, + sequence: { actorMargin: 50, mirrorActors: false } + }); + }); +})(); From 8c7f68574531060278041a95b69a4971016dc775 Mon Sep 17 00:00:00 2001 From: Pantani Date: Tue, 2 Jun 2026 16:06:58 -0300 Subject: [PATCH 2/2] docs: align CLI reference with actual clap surface Codex review on PR #20 flagged that the reference pages were drifting from the real CLI definitions in src/cli/. They had been synthesized from CLAUDE.md / ROADMAP.md rather than read from source, so they advertised flags that don't exist (chain new --programs/--license/--git, wallet show, deploy --network mainnet-beta, --validator surfpool, --frontend react/solid) and would have produced clap parse errors for anyone copy-pasting. This commit rewrites the affected pages against the actual Args structs: - chain.md: NewArgs (--framework/--frontend/--path/--dry-run only), BuildArgs (--headless/--no-codama), ServeArgs (--runtime/--debounce-ms/ --no-frontend), DoctorArgs (--fix-markers only). chain deploy noted as stub; real deploy is the top-level command. - onboarding.md: WalletCmd subcommands (new/list/airdrop/balance/ set-default), positional amount + --cluster for airdrop, balance not show. DeployArgs positional target (localnet/devnet/mainnet) + --yes-i-understand-cost requirement for mainnet. - generate.md: actual flags only (--program/--out-dir/--frontend-path/ --target react|solid|all). Removed invented --rebuild-config/--pretty/ --out. - cli/index.md: real global flags (--workdir/--config/--verbose/--json); removed --no-color and fabricated SUNSCREEN_* env vars. - config/schema.md: rewritten against actual Config / ClustersCfg / RuntimeCfg shape (project.*, clusters.{localnet,devnet,mainnet}, runtime.{engine,port,faucet_sol}). - Guides updated to match: deploy not --network ; wallet airdrop --cluster ; chain serve --runtime; vite/next frontend (not react/solid which is hook-target only). Co-Authored-By: Claude Opus 4.7 --- docs/site/src/guides/deploying-to-devnet.md | 55 ++++---- docs/site/src/guides/deploying-to-mainnet.md | 38 +++--- docs/site/src/guides/dev-loop.md | 8 +- docs/site/src/guides/troubleshooting.md | 4 +- docs/site/src/learn/first-workspace.md | 6 +- docs/site/src/learn/solana-primer.md | 6 +- docs/site/src/learn/what-is-sunscreen.md | 2 +- docs/site/src/learn/your-first-nft.md | 31 ++--- docs/site/src/reference/cli/chain.md | 60 ++++----- docs/site/src/reference/cli/generate.md | 40 +++--- docs/site/src/reference/cli/index.md | 11 +- docs/site/src/reference/cli/onboarding.md | 118 ++++++++++------- docs/site/src/reference/config/schema.md | 126 ++++++++++--------- 13 files changed, 250 insertions(+), 255 deletions(-) diff --git a/docs/site/src/guides/deploying-to-devnet.md b/docs/site/src/guides/deploying-to-devnet.md index b58f96b..9a6e926 100644 --- a/docs/site/src/guides/deploying-to-devnet.md +++ b/docs/site/src/guides/deploying-to-devnet.md @@ -13,25 +13,23 @@ If you don't have a keypair yet: ```bash -sunscreen wallet new +sunscreen wallet new dev ``` -This writes `~/.config/solana/id.json` and prints the public key. **Save the recovery words** if prompted. +This writes a new keypair under `.sunscreen/wallets/dev.json` and prints the public key. **Save the recovery words** if prompted. -If you already have a keypair, point to it: +Make it the default for localnet/devnet so subsequent commands pick it up: ```bash -export SUNSCREEN_WALLET=$HOME/.config/solana/id.json +sunscreen wallet set-default dev --cluster devnet ``` -Or set `wallet:` in `sunscreen.yml`. The CLI defaults to the solana-cli default location. - ## Step 2 — Airdrop devnet SOL -You need ~2 SOL for a fresh deploy. +You need ~2 SOL for a fresh deploy: ```bash -sunscreen wallet airdrop --network devnet --amount 2 +sunscreen wallet airdrop 2 --cluster devnet ``` If you see `Network: rate-limited`, the public faucet throttled you. Options: @@ -40,44 +38,35 @@ If you see `Network: rate-limited`, the public faucet throttled you. Options: - Use a public web faucet: . - Run `solana airdrop 2 --url devnet` directly. -## Step 3 — Deploy plan (dry run) - -Always inspect the plan first: +Check the balance: ```bash -sunscreen deploy --network devnet --dry-run +sunscreen wallet balance --cluster devnet ``` -You'll see: +## Step 3 — Deploy plan (dry run) -```text -plan -─ network: devnet (https://api.devnet.solana.com) -─ payer: (balance: 2.0 SOL) -─ programs: - my_app → target/deploy/my_app.so (180 KB) -─ estimated cost: ~1.6 SOL +Always inspect the plan first: + +```bash +sunscreen deploy devnet --dry-run ``` -If the estimated cost exceeds your balance, the dry run fails with `exit 4` and tells you to airdrop more. +The dry run prints the planned Anchor invocation, the wallet that will pay, and the balance check. ## Step 4 — Deploy ```bash -sunscreen deploy --network devnet +sunscreen deploy devnet ``` -Under the hood, this runs `solana program deploy` for each program in your workspace and updates `Anchor.toml` + `sunscreen.yml` with the new program IDs. - -On success: +Or deploy only one program in a multi-program workspace: -``` -✓ deployed my_app at -✓ Anchor.toml updated -✓ sunscreen.yml updated +```bash +sunscreen deploy devnet --program my_app ``` -Your IDL is also published if you pass `--with-idl`. +On success Anchor updates `Anchor.toml` with the new program ID. Sunscreen surfaces the result and the program addresses. ## Step 5 — Sanity check @@ -95,7 +84,7 @@ If you have a frontend: sunscreen generate clients ``` -This rewrites `app/src/clients/` against the now-deployed program ID. Restart your `pnpm dev` so it picks up the new clients. +This rewrites `clients//` against the now-deployed program ID. Restart your `pnpm dev` so it picks up the new clients. ## Re-deploying after code changes @@ -103,7 +92,7 @@ Each subsequent deploy is incremental: ```bash sunscreen chain build -sunscreen deploy --network devnet +sunscreen deploy devnet ``` Solana's `solana program deploy` handles the upgrade in place, reusing the program ID. @@ -113,7 +102,7 @@ Solana's `solana program deploy` handles the upgrade in place, reusing the progr | Symptom | Cause | Fix | |---------|-------|-----| | `insufficient funds` | not enough SOL | airdrop or use the web faucet | -| `program too large` | binary > buffer size | check `--max-len`, or upgrade in chunks | +| `program too large` | binary > buffer size | check `--max-len` on `solana program deploy`, or upgrade in chunks | | `transaction simulation failed` | program-side check failed | run the test suite locally first | | `BlockhashNotFound` | RPC overloaded or clock skew | retry; consider a private RPC (Helius, Triton) | diff --git a/docs/site/src/guides/deploying-to-mainnet.md b/docs/site/src/guides/deploying-to-mainnet.md index 5a2b525..fb87650 100644 --- a/docs/site/src/guides/deploying-to-mainnet.md +++ b/docs/site/src/guides/deploying-to-mainnet.md @@ -1,6 +1,6 @@ # Deploying to mainnet -⏱ 10 min · 🎯 you'll have: your program live on mainnet-beta, with the same shape as your devnet deploy. +⏱ 10 min · 🎯 you'll have: your program live on mainnet, with the same shape as your devnet deploy. Mainnet is real money. Read this whole page before running anything. @@ -13,50 +13,44 @@ Mainnet is real money. Read this whole page before running anything. - [ ] You're using a private RPC (Helius, Triton, QuickNode). Public mainnet RPCs throttle aggressively. - [ ] Your program's upgrade authority is your control (a multisig, ideally). -## Step 1 — Configure the RPC +## Step 1 — Configure your wallet and RPC -Pass `--rpc-url`: +Create or import a mainnet wallet and make it the default for `mainnet`: ```bash -sunscreen deploy --network mainnet-beta --rpc-url https://your-rpc-endpoint.example.com +sunscreen wallet new prod +sunscreen wallet set-default prod --cluster mainnet ``` -Or set it in `sunscreen.yml`: +Sunscreen passes through to the Solana CLI for RPC selection. Set a private endpoint in your shell or `solana config`: -```yaml -networks: - mainnet-beta: - rpc_url: https://your-rpc-endpoint.example.com +```bash +solana config set --url https://your-rpc-endpoint.example.com ``` ## Step 2 — Dry run ```bash -sunscreen deploy --network mainnet-beta --dry-run +sunscreen deploy mainnet --yes-i-understand-cost --dry-run ``` +`--yes-i-understand-cost` is **required** for mainnet — sunscreen refuses to run without it. The `--dry-run` makes this safe: it prints the plan without sending transactions. + Read the plan carefully: - Confirm the **payer** is your mainnet wallet, not your devnet one. - Confirm the **program count** matches what you intend. -- Confirm the **estimated cost** is within your wallet balance. +- Confirm your balance is enough. If anything looks off, stop and investigate. Mainnet deploys do not refund "I clicked too fast". ## Step 3 — Deploy ```bash -sunscreen deploy --network mainnet-beta +sunscreen deploy mainnet --yes-i-understand-cost ``` -The CLI will: - -1. Build (if `target/deploy/*.so` is missing or stale). -2. Deploy each program via `solana program deploy`. -3. Update `Anchor.toml` and `sunscreen.yml` with the mainnet program IDs. -4. Optionally publish the IDL on chain if you pass `--with-idl`. - -A typical deploy takes 30–90 seconds per program, depending on RPC. +The CLI builds (if `target/deploy/*.so` is missing or stale), then runs `anchor deploy --provider.cluster mainnet`. A typical deploy takes 30–90 seconds per program, depending on RPC. ## Step 4 — Verify @@ -92,8 +86,8 @@ Commit the regenerated clients. Your frontend now points at mainnet program IDs. | Symptom | Cause | Fix | |---------|-------|-----| -| `RPC error: 429 Too Many Requests` | public RPC throttled | use a private RPC | -| `BlockhashNotFound` mid-deploy | RPC dropped you | retry; deploys are idempotent | +| `RPC error: 429 Too Many Requests` | public RPC throttled | use a private RPC via `solana config set --url` | +| `BlockhashNotFound` mid-deploy | RPC dropped you | retry; Anchor's deploy is resumable | | Wrong wallet used | environment variable leaked | inspect `solana config get` before deploying | | Forgot to update IDL | clients have stale shape | `sunscreen generate clients` and redeploy frontend | diff --git a/docs/site/src/guides/dev-loop.md b/docs/site/src/guides/dev-loop.md index 53d9c68..7ef9852 100644 --- a/docs/site/src/guides/dev-loop.md +++ b/docs/site/src/guides/dev-loop.md @@ -52,13 +52,13 @@ Sunscreen watches your workspace tree minus a list of ignored paths (`target/`, If any step fails, the TUI shows the error and the validator stays up — fix and save again. -## Choose your validator +## Choose your runtime -Sunscreen prefers Surfpool if found on `$PATH`. Override: +Sunscreen reads `runtime.engine` from `sunscreen.yml`. Override per-invocation with `--runtime`: ```bash -sunscreen chain serve --validator test-validator -sunscreen chain serve --validator surfpool +sunscreen chain serve --runtime test-validator +sunscreen chain serve --runtime surfpool ``` If Surfpool is the default but missing, sunscreen falls back to `solana-test-validator` automatically and logs the fallback. diff --git a/docs/site/src/guides/troubleshooting.md b/docs/site/src/guides/troubleshooting.md index 57c19e1..0724d2a 100644 --- a/docs/site/src/guides/troubleshooting.md +++ b/docs/site/src/guides/troubleshooting.md @@ -18,7 +18,7 @@ Then `sunscreen doctor` to confirm. ## `toolchain_missing: solana` -Needed for `deploy`, `wallet airdrop`, and `chain serve --validator test-validator`. +Needed for `deploy`, `wallet airdrop`, and `chain serve --runtime test-validator`. **Fix:** @@ -101,7 +101,7 @@ The public devnet faucet throttles aggressively. ## "I forgot to deploy after a code change" ```bash -sunscreen chain build && sunscreen deploy --network devnet +sunscreen chain build && sunscreen deploy devnet ``` Sunscreen detects out-of-date `target/deploy/*.so` and rebuilds automatically before deploy. diff --git a/docs/site/src/learn/first-workspace.md b/docs/site/src/learn/first-workspace.md index a2b2eff..859a8c4 100644 --- a/docs/site/src/learn/first-workspace.md +++ b/docs/site/src/learn/first-workspace.md @@ -18,12 +18,16 @@ Don't have everything? Run `sunscreen doctor` first — it tells you exactly wha sunscreen chain new my-app --framework anchor --frontend none ``` -You'll see NDJSON-style progress lines, then: +You'll see progress output, then a summary similar to: ``` ✓ workspace my-app/ created (anchor, no frontend) ``` +::: tip +`--frontend` accepts `none`, `vite`, or `next`. Pick `vite` if you want React + Vite scaffolded under `app/`; pick `next` for a Next.js scaffold. `none` keeps the workspace lean. +::: + ## Step 2 — Look around ```bash diff --git a/docs/site/src/learn/solana-primer.md b/docs/site/src/learn/solana-primer.md index b86d788..1521525 100644 --- a/docs/site/src/learn/solana-primer.md +++ b/docs/site/src/learn/solana-primer.md @@ -63,14 +63,14 @@ Three official networks plus your local validator: | `localnet` | `http://127.0.0.1:8899` | a validator on your laptop (sunscreen's `chain serve` launches one) | | `devnet` | `https://api.devnet.solana.com` | free SOL via faucet, public, test programs here first | | `testnet` | `https://api.testnet.solana.com` | validator testing, not for app dev | -| `mainnet-beta` | `https://api.mainnet-beta.solana.com` | production | +| `mainnet` | `https://api.mainnet-beta.solana.com` | production (Solana sometimes still calls this `mainnet-beta` in URLs) | ## The development flow you'll use 1. Write the program (sunscreen scaffolds it). 2. `chain serve` — runs against `localnet` with hot reload. -3. `deploy --network devnet` — push to devnet, test with real fees + real wallets. -4. `deploy --network mainnet-beta` — production. +3. `sunscreen deploy devnet` — push to devnet, test with real fees + real wallets. +4. `sunscreen deploy mainnet --yes-i-understand-cost` — production. ## Common pitfalls (the ones that bite everyone) diff --git a/docs/site/src/learn/what-is-sunscreen.md b/docs/site/src/learn/what-is-sunscreen.md index 6e2776e..bf56d61 100644 --- a/docs/site/src/learn/what-is-sunscreen.md +++ b/docs/site/src/learn/what-is-sunscreen.md @@ -33,7 +33,7 @@ Anchor CLI gives you `anchor init`, `anchor build`, `anchor deploy`. Great primi | Task | Anchor CLI | Sunscreen | |------|-----------|-----------| -| Create workspace | `anchor init` | `sunscreen chain new --framework anchor --frontend react` | +| Create workspace | `anchor init` | `sunscreen chain new --framework anchor --frontend vite` | | Add instruction | hand-edit lib.rs | `sunscreen scaffold instruction Create --program app` | | Add CRUD slice | manual (~200 lines) | `sunscreen scaffold crud Post --program app` | | Watch + rebuild + regenerate clients | run 3 terminals | `sunscreen chain serve` | diff --git a/docs/site/src/learn/your-first-nft.md b/docs/site/src/learn/your-first-nft.md index 60c8979..036f1ca 100644 --- a/docs/site/src/learn/your-first-nft.md +++ b/docs/site/src/learn/your-first-nft.md @@ -21,9 +21,9 @@ cd my-first-nft This single command: -1. Created an Anchor workspace with a React frontend. +1. Created an Anchor workspace with a Vite frontend. 2. Scaffolded a Metaplex NFT recipe slice (mint, metadata, master edition). -3. Added a sample frontend hook for minting. +3. Added a sample frontend hook for minting (TanStack Query). You'll see a summary table at the end listing files created. @@ -44,15 +44,16 @@ Successful build produces `target/idl/my_first_nft.json` and regenerates Codama If you don't have a Solana keypair: ```bash -sunscreen wallet new +sunscreen wallet new dev +sunscreen wallet set-default dev --cluster devnet ``` -This creates `~/.config/solana/id.json` and prints the public key. Save it somewhere. +The first command creates a keypair under `.sunscreen/wallets/dev.json` and prints the public key. The second makes it sunscreen's default for devnet operations. Get devnet SOL: ```bash -sunscreen wallet airdrop --network devnet --amount 2 +sunscreen wallet airdrop 2 --cluster devnet ``` If the airdrop is throttled (common), the error tells you the next step (use `solana airdrop` directly or a public faucet). @@ -60,22 +61,22 @@ If the airdrop is throttled (common), the error tells you the next step (use `so ## Step 4 — Deploy plan ```bash -sunscreen deploy --network devnet --dry-run +sunscreen deploy devnet --dry-run ``` -Sunscreen prints a deploy plan: how much SOL you need, which keys will be created, what's going to be uploaded. No on-chain action yet — it's a `--dry-run`. +Sunscreen prints a deploy plan: how much SOL you need, what's going to be uploaded. No on-chain action yet — it's a `--dry-run`. When ready: ```bash -sunscreen deploy --network devnet +sunscreen deploy devnet ``` You'll get a program ID. Save it. ## Step 5 — Mint -The React frontend in `app/` is already wired with the Codama-generated client and the mint hook. Start it: +The Vite frontend in `app/` is already wired with the Codama-generated client and the mint hook. Start it: ```bash cd app @@ -83,18 +84,18 @@ pnpm install pnpm dev ``` -Open . Connect your wallet (the same one you funded), click *Mint*, approve. After a few seconds you'll see the mint signature. +Open (Vite's default). Connect your wallet (the same one you funded), click *Mint*, approve. After a few seconds you'll see the mint signature. You just minted an NFT through a program *you* wrote — even though sunscreen wrote most of it for you. ## What happened end-to-end ```text -quickstart nft → workspace + Metaplex recipe + frontend hooks -chain build → anchor build → IDL → Codama clients -wallet new / airdrop → fund a keypair on devnet -deploy --network devnet → upload program, register program ID -frontend pnpm dev → React app calls the generated client → mints +quickstart nft → workspace + Metaplex recipe + frontend hooks +chain build → anchor build → IDL → Codama clients +wallet new / airdrop → fund a keypair on devnet +deploy devnet → upload program, register program ID +frontend pnpm dev → Vite app calls the generated client → mints ``` ## Next diff --git a/docs/site/src/reference/cli/chain.md b/docs/site/src/reference/cli/chain.md index 396cc54..071a31f 100644 --- a/docs/site/src/reference/cli/chain.md +++ b/docs/site/src/reference/cli/chain.md @@ -2,33 +2,34 @@ Workspace lifecycle: create, build, serve, doctor. +`sunscreen chain deploy` is a stub today (Phase 2+). For real deploys use the top-level [`sunscreen deploy`](./onboarding.md#deploy). + ## `new` ``` sunscreen chain new [FLAGS] ``` -Create a new workspace at `.//`. +Create a new workspace at `.//` (or at `--path` if provided). | Flag | Default | Description | |------|---------|-------------| | `--framework ` | `anchor` | `anchor` or `pinocchio` | -| `--frontend ` | `none` | `none`, `react`, `solid` | -| `--programs ` | `` | comma-separated list of program names | -| `--license ` | `MIT OR Apache-2.0` | SPDX expression for generated `Cargo.toml` | -| `--git/--no-git` | `--git` | init a git repo with first commit | -| `--dry-run` | off | print planned files without writing | -| `--json` | off | machine-readable summary | +| `--frontend ` | `none` | `none`, `vite`, `next` | +| `--path ` | `./` | output directory | +| `--dry-run` | off | print the planned file list without writing | + +Global flags (apply to every subcommand): `--json`, `--verbose`/`-v`, `--workdir `, `--config `. -**Exit codes:** `0` ok · `2` toolchain · `4` directory exists. +**Exit codes:** `0` ok · `2` toolchain missing · `4` directory exists / invalid argument. **Examples:** ```bash sunscreen chain new my-app -sunscreen chain new my-app --framework anchor --frontend react +sunscreen chain new my-app --framework anchor --frontend vite sunscreen chain new bare --framework pinocchio -sunscreen chain new multi --programs "core,governance,treasury" +sunscreen chain new my-app --frontend next --path packages/program ``` ## `build` @@ -37,30 +38,23 @@ sunscreen chain new multi --programs "core,governance,treasury" sunscreen chain build [FLAGS] ``` -Run the build pipeline: `anchor build` (or `cargo build-sbf` for Pinocchio), then Codama client regeneration (if frontend is configured). +Run the build pipeline: `anchor build` (or `cargo build-sbf` for Pinocchio), then Codama client regeneration when the workspace has a frontend. | Flag | Default | Description | |------|---------|-------------| -| `--no-codama` | off | skip Codama regeneration | -| `--headless` | off | NDJSON events on stdout, no TUI | -| `--release` | off | release profile build | -| `--json` | off | one summary object at the end | +| `--headless` | off | NDJSON events on stdout suitable for CI logs | +| `--no-codama` | off | skip Codama regeneration after a successful build | **Exit codes:** `0` ok · `2` toolchain missing · `5` no workspace · build-tool exit codes preserved on failure. -**NDJSON events:** +**NDJSON events** (selection — full list in [NDJSON events](../events.md)): ```json {"event":"build_start","framework":"anchor","programs":["my_app"]} {"event":"build_progress","step":"anchor_build"} {"event":"build_ok","programs":["my_app"],"duration_ms":4200} -{"event":"codama_start","frontend":"react"} -{"event":"codama_ok","files_written":12} -{"event":"frontend_notified","path":"app/.sunscreen/reload"} ``` -Full event list in [NDJSON events](../events.md). - ## `serve` ``` @@ -71,32 +65,28 @@ Long-running supervised dev loop: validator + watcher + build pipeline + fronten | Flag | Default | Description | |------|---------|-------------| -| `--validator ` | auto | `surfpool`, `test-validator`, or omit for auto-detect with fallback | -| `--no-codama` | off | skip Codama on rebuild | | `--headless` | off | NDJSON stream, no TUI | -| `--rpc-port ` | `8899` | bind validator RPC port | -| `--ws-port ` | `8900` | bind validator WS port | -| `--quiet` | off | suppress validator stdout in TUI | +| `--no-codama` | off | skip Codama on rebuild | +| `--no-frontend` | off | skip frontend reload notifications | +| `--runtime ` | from `sunscreen.yml` | `surfpool` or `test-validator`; defaults to the workspace's `runtime.engine`, falling back to `solana-test-validator` when Surfpool is requested but missing | +| `--debounce-ms ` | `150` | watcher debounce window | -**Exit codes:** `0` ok (Ctrl-C) · `2` toolchain · `5` no workspace · `1` unexpected. +**Exit codes:** `0` ok (Ctrl-C) · `2` toolchain · `5` no workspace · `4` invalid arg (e.g. `--debounce-ms 0`). -**Termination:** Ctrl-C sends SIGTERM to the validator's process group, waits up to 5s, then SIGKILL. +**Termination:** Ctrl-C sends SIGTERM to the validator's process group, waits, then SIGKILL. ## `doctor` ``` -sunscreen chain doctor [FLAGS] +sunscreen chain doctor [--fix-markers] ``` -Diagnose toolchain *and* workspace markers. +Workspace-level diagnostic: marker integrity across scaffolded files. | Flag | Default | Description | |------|---------|-------------| | `--fix-markers` | off | reconstruct safe non-appendable markers (see [Marker protocol](../markers.md)) | -| `--json` | off | flat array of `ToolReport` objects | - -Calls the same toolchain detectors as the top-level `sunscreen doctor`, plus marker integrity over your workspace. -**Exit codes:** `0` ok · `2` something critical missing · `4` non-fixable marker drift. +For toolchain diagnostics see the top-level [`doctor`](./doctor.md) command. -See also: [`doctor`](./doctor.md) for the toolchain-only command. +**Exit codes:** `0` ok · `4` non-fixable marker drift detected. diff --git a/docs/site/src/reference/cli/generate.md b/docs/site/src/reference/cli/generate.md index e7a9d5f..4d562b2 100644 --- a/docs/site/src/reference/cli/generate.md +++ b/docs/site/src/reference/cli/generate.md @@ -6,38 +6,36 @@ Generate artifacts from the IDL. sunscreen generate [FLAGS] ``` -`generate` is implicitly called by `chain build` and `chain serve`. Use it directly when you want to regenerate without rebuilding the program. +`generate clients` is implicitly called by `chain build` and `chain serve`. Use these subcommands directly when you want to regenerate without rebuilding the program. ## `clients` ``` -sunscreen generate clients [FLAGS] +sunscreen generate clients [--program ] ``` -Run Codama against the workspace IDL and write a JavaScript/TypeScript client into `app/src/clients/` (or the path configured in `sunscreen.yml`). +Run Codama against the workspace IDL and write a JavaScript/TypeScript client. | Flag | Default | Description | |------|---------|-------------| -| `--rebuild-config` | off | rewrite `codama.config.mjs` from scratch (use when IDL shape changes drastically) | -| `--out ` | from config | client output directory | -| `--json` | off | summary on stdout | +| `--program ` | first IDL | program to regenerate clients for | -**Requires:** `pnpm` on PATH (sunscreen uses `pnpm exec codama`). +**Requires:** `pnpm` on PATH (sunscreen drives Codama via `pnpm exec codama`). ## `idl` ``` -sunscreen generate idl [FLAGS] +sunscreen generate idl [--program ] [--out-dir ] ``` -Export a deterministic IDL into `idl/`. Useful for CI artifacts and clients consumed outside Codama. +Export a deterministic IDL into the workspace. | Flag | Default | Description | |------|---------|-------------| -| `--out ` | `idl/` | output directory | -| `--pretty` | on | format JSON with 2-space indent | +| `--program ` | all built IDLs in `target/idl` | program to export | +| `--out-dir ` | `clients/idl` | output directory relative to the workspace root | -The exported IDL is byte-identical between runs as long as the source hasn't changed (sorted fields, normalized numeric types). +The exported IDL is byte-stable between runs as long as the source hasn't changed. ## `frontend-hooks` @@ -45,26 +43,26 @@ The exported IDL is byte-identical between runs as long as the source hasn't cha sunscreen generate frontend-hooks [FLAGS] ``` -Generate React Query or Solid Query hooks from the IDL. +Generate TanStack Query hooks from exported IDLs. | Flag | Default | Description | |------|---------|-------------| -| `--framework ` | from `sunscreen.yml` | `react` or `solid` | -| `--out ` | `app/src/hooks/` | output directory | -| `--json` | off | summary on stdout | +| `--program ` | all built IDLs | program to generate hooks for | +| `--frontend-path ` | from `sunscreen.yml` | required when the workspace was scaffolded with `--frontend none` | +| `--target ` | from project config | `react`, `solid`, or `all` (React Query + Solid Query wrappers) | -For each instruction in the IDL, generates a hook (`useCreatePost`, `useReadPost`, …) wrapping the Codama client. The hook handles transaction building, signing, and refetching account queries. +For each instruction in the IDL, generates a hook (`useCreatePost`, `useReadPost`, …) wrapping the Codama client. Hooks handle transaction building, signing, and refetching account queries on mutation success. ## Exit codes | Code | When | |------|------| | `0` | success | -| `2` | `pnpm` or required dependency missing | -| `3` | `sunscreen.yml` does not declare a frontend | +| `2` | `pnpm` or another required dependency missing | +| `3` | `sunscreen.yml` invalid | | `5` | not in a workspace | ## Tips -- `chain build` calls `generate clients` automatically. Run `generate` directly when you've edited the IDL by hand or need clients without rebuilding the `.so`. -- Re-runs are idempotent. Codama overwrites only files it owns; your hand-written code in `app/src/` is untouched. +- `chain build` calls `generate clients` automatically. Run `generate` directly when you've touched the IDL by hand or need clients without rebuilding the `.so`. +- Re-runs are idempotent. Codama overwrites only files it owns. diff --git a/docs/site/src/reference/cli/index.md b/docs/site/src/reference/cli/index.md index 89856f1..24a70d2 100644 --- a/docs/site/src/reference/cli/index.md +++ b/docs/site/src/reference/cli/index.md @@ -28,8 +28,9 @@ sunscreen [GLOBAL_FLAGS] [ARGS] [FLAGS] | Flag | What it does | |------|-------------| | `--json` | machine-readable output on stdout; human messages on stderr | -| `--no-color` | disable ANSI colors | | `-v / -vv / -vvv` | verbosity (warn / info / debug) | +| `--workdir ` | override working directory | +| `--config ` | path to an alternative `sunscreen.yml` | | `--help` | per-command help | | `--version` | print sunscreen version | @@ -49,13 +50,7 @@ Full list with `next_step` strings in [Errors & exit codes](../errors.md). ## Environment variables -| Variable | Effect | -|----------|--------| -| `SUNSCREEN_CONFIG` | path to an alternative `sunscreen.yml` | -| `SUNSCREEN_WALLET` | path to a Solana keypair JSON file | -| `SUNSCREEN_NO_COLOR` | same as `--no-color` | -| `SUNSCREEN_LOG` | log filter (e.g. `info`, `debug,sunscreen::runtime=trace`) | -| `SUNSCREEN_FRAMEWORK` | override framework detection during command runs | +Sunscreen prefers explicit flags over magic environment variables. Pass `--config ` and `--workdir ` as needed. Standard Rust env vars (`RUST_LOG`, `RUST_BACKTRACE`) apply to the binary as usual. ## `--json` contract diff --git a/docs/site/src/reference/cli/onboarding.md b/docs/site/src/reference/cli/onboarding.md index 543508b..afa874c 100644 --- a/docs/site/src/reference/cli/onboarding.md +++ b/docs/site/src/reference/cli/onboarding.md @@ -1,101 +1,123 @@ # Onboarding commands -Beginner-friendly shortcuts that compose other sunscreen commands. +Beginner-friendly shortcuts that compose other sunscreen commands. All require the `onboarding` feature (enabled by default in release builds). ## `init` ``` -sunscreen init [--non-interactive] [--name ] [--framework ] [--frontend ] +sunscreen init [] [FLAGS] ``` -Interactive wizard that asks 3–5 questions and runs `chain new` under the hood. With `--non-interactive`, behaves like `chain new` with explicit flags. +Interactive wizard that asks 3–5 questions and runs `chain new` under the hood. + +| Flag | Default | Description | +|------|---------|-------------| +| `--non-interactive` | off | disable prompts and require flag-based input | +| `--from-preset ` | none | preset to apply when no prompts are available | +| `--frontend ` | `vite` | `none`, `vite`, `next` | +| `--path ` | `./` | output directory | +| `--dry-run` | off | print planned files without writing | ## `examples` ``` -sunscreen examples [list|show |init [--out ]] +sunscreen examples ``` -Browse embedded example projects. `init ` copies the example into a new directory. - -Available examples (embedded at compile time): - -- `counter` — minimal counter program. -- `token-faucet` — SPL token mint with a free-claim instruction. -- `nft-collection` — Metaplex NFT collection with mint. -- `dao-voting` — a stripped-down DAO voting program. +| Subcommand | What it does | +|------------|-------------| +| `list [--tag ]` | list embedded examples (optionally filtered) | +| `describe ` | print one example's README | +| `use [] [--non-interactive] [--dry-run]` | copy an example onto disk | ## `quickstart` ``` -sunscreen quickstart +sunscreen quickstart [FLAGS] ``` -Composite recipes for "I want a working X in 30 seconds": +Composite recipes for "I want a working X in 30 seconds". -| Kind | What it builds | -|------|---------------| -| `token` | Anchor workspace + SPL Token recipe + React frontend with mint UI | -| `nft` | Anchor workspace + Metaplex NFT recipe + React frontend with mint UI | +| Recipe | What it builds | +|--------|---------------| +| `token` | Anchor workspace + SPL Token recipe | +| `nft` | Anchor workspace + Metaplex NFT recipe | | `dao` | Anchor workspace + DAO voting scaffolds | -| `blog` | Anchor workspace + CRUD `Post` resource + React frontend | +| `blog` | Anchor workspace + CRUD `Post` resource | -Equivalent to running `chain new` + the matching `scaffold` recipe + `generate frontend-hooks`. +| Flag | Default | Description | +|------|---------|-------------| +| `--name ` | prompted | project name (required in `--non-interactive`) | +| `--cluster ` | `localnet` | `localnet`, `devnet`, `mainnet` — used for the generated next steps | +| `--non-interactive` | off | disable prompts | +| `--frontend ` | `vite` | `none`, `vite`, `next` | +| `--path ` | `./` | output directory | +| `--dry-run` | off | print planned operations without writing | ## `wallet` ``` -sunscreen wallet new [--out ] -sunscreen wallet airdrop --network --amount [--address ] -sunscreen wallet show [--network ] +sunscreen wallet ``` | Subcommand | What it does | |------------|-------------| -| `new` | Generate a new keypair at `~/.config/solana/id.json` (or `--out`) | -| `airdrop` | Request SOL from a network's faucet | -| `show` | Print the current wallet's pubkey and balance on a network | +| `new [] [--out ] [--no-bip39-passphrase] [--dry-run]` | Generate a keypair. When `--out` is omitted, lands under `.sunscreen/wallets/.json` | +| `list` | List wallets discovered under `.sunscreen/wallets/` | +| `airdrop [] [--cluster ] [--to ] [--dry-run]` | Request SOL. `AMOUNT` defaults to `1.0`. `--cluster` defaults to `devnet`. `--to` defaults to the Solana CLI default keypair | +| `balance [
] [--cluster ]` | Print a wallet balance | +| `set-default [--cluster ]` | Set the default wallet path in `sunscreen.yml` for a cluster | + +**Examples:** + +```bash +sunscreen wallet new dev +sunscreen wallet airdrop 2 --cluster devnet +sunscreen wallet balance --cluster devnet +sunscreen wallet set-default dev --cluster localnet +``` ## `deploy` ``` -sunscreen deploy [--network ] [--rpc-url ] [--with-idl] [--dry-run] [--json] +sunscreen deploy [FLAGS] ``` -Build and deploy programs in the workspace to a Solana network. +Build and deploy programs to a Solana cluster. -| Flag | Default | Description | -|------|---------|-------------| -| `--network` | `localnet` | `localnet`, `devnet`, `testnet`, `mainnet-beta` | -| `--rpc-url` | network default | override RPC endpoint | -| `--with-idl` | off | also publish IDL on chain | -| `--dry-run` | off | print plan only | -| `--json` | off | structured output | +| Arg / flag | Default | Description | +|------------|---------|-------------| +| `` | required | `localnet`, `devnet`, or `mainnet` (positional, value-enum) | +| `--program ` | all programs | pass through to Anchor for a single program | +| `--verify` | off | run `anchor verify` after deploy when supported | +| `--yes-i-understand-cost` | off | **required for `mainnet`** | +| `--dry-run` | off | print deployment plan without running Anchor | + +**Exit codes:** `0` ok · `2` toolchain · `4` invalid args / insufficient balance · `5` no workspace. -**Exit codes:** `0` ok · `2` toolchain · `4` insufficient balance / network unreachable · `5` no workspace. +**Examples:** + +```bash +sunscreen deploy devnet +sunscreen deploy devnet --program my_app --dry-run +sunscreen deploy mainnet --yes-i-understand-cost +``` ## `learn` ``` -sunscreen learn [list|] +sunscreen learn [] ``` -Open an embedded topic in the terminal pager. Topics: +Print an embedded topic in the terminal. Omit `` to list available topics. -- `markers` — the marker protocol in 1 page. -- `pdas` — PDA basics. -- `idl-flow` — IDL → Codama → clients. -- `rent` — Solana rent in 1 page. +## `next_step` contract -`learn` requires no network. It's the offline equivalent of pointing users at the docs site. +Every onboarding error includes a `next_step` field in JSON output and a final line in human output telling the user exactly what to do. The contract is tested in `tests/errors_contract.rs` and is part of sunscreen's stable surface. -## Exit code: `next_step` contract - -Every onboarding error includes a `next_step` field in JSON output and a final line in human output telling the user exactly what to do. Example: +Example error: ```text error: Network: rate-limited (exit 4) next_step: Try the web faucet at https://faucet.solana.com/ or wait 10 minutes. ``` - -This contract is tested in `tests/errors_contract.rs` and is part of sunscreen's stable API surface. diff --git a/docs/site/src/reference/config/schema.md b/docs/site/src/reference/config/schema.md index 80d37d7..3341708 100644 --- a/docs/site/src/reference/config/schema.md +++ b/docs/site/src/reference/config/schema.md @@ -2,104 +2,106 @@ Single source of truth for a workspace. Generated by `chain new`, read by every other command. +::: warning +This page summarizes the schema. The authoritative shape lives in [`src/config/schema.rs`](https://github.com/Pantani/sunscreen/blob/main/src/config/schema.rs) and is emitted as JSON Schema via `schemars`. When in doubt, the Rust types win. +::: + ## Top-level shape ```yaml version: 1 -name: my-app -framework: anchor # anchor | pinocchio -frontend: react # none | react | solid -wallet: ~/.config/solana/id.json + +project: + name: my-app + framework: anchor # anchor | pinocchio + frontend: vite # none | vite | next + +toolchain: + # tool version pins; see ToolchainCfg in schema.rs + anchor: "^0.30" + +scaffolding: + # marker + scaffolder preferences; see ScaffoldingCfg programs: - name: my_app path: programs/my_app - program_id: ~ # filled by deploy + program_id: ~ # filled by deploy + +workspace: + # workspace-level layout knobs -networks: +clusters: localnet: - rpc_url: http://127.0.0.1:8899 + url: http://127.0.0.1:8899 + wallet: ~/.config/solana/id.json devnet: - rpc_url: https://api.devnet.solana.com - mainnet-beta: - rpc_url: ~ # use --rpc-url at deploy time + url: https://api.devnet.solana.com + wallet: ~/.config/solana/id.json + mainnet: + url: https://api.mainnet-beta.solana.com + wallet: ~/.config/solana/id.json + +runtime: + engine: surfpool # surfpool | test-validator + port: 8899 + faucet_sol: 100 plugins: - source: ./plugins/my-plugin version: "0.2.0" - hooks: [after_build] - -runtime: - validator: surfpool # surfpool | test-validator | auto - codama: true - frontend_notify_path: app/.sunscreen/reload ``` -## Field reference - -### Top level +## Top-level keys -| Field | Type | Required | Default | Description | -|-------|------|----------|---------|-------------| -| `version` | int | yes | `1` | schema version. Sunscreen migrates older versions automatically. | -| `name` | string | yes | — | workspace name (kebab-case). | -| `framework` | enum | yes | — | `anchor` or `pinocchio`. | -| `frontend` | enum | no | `none` | `none`, `react`, `solid`. | -| `wallet` | path | no | solana-cli default | path to Solana keypair JSON. | -| `programs` | array | yes | — | one entry per program in the workspace. | -| `networks` | map | no | localnet+devnet | per-network RPC config. | -| `plugins` | array | no | `[]` | declared plugins. | -| `runtime` | object | no | — | dev-loop preferences. | +| Key | Type | Required | Description | +|-----|------|----------|-------------| +| `version` | int | yes | schema version; sunscreen migrates older values automatically | +| `project` | object | no | `name`, `framework` (`anchor`/`pinocchio`), `frontend` (`none`/`vite`/`next`) | +| `toolchain` | object | no | external tool version pins | +| `scaffolding` | object | no | scaffolder/marker behaviour knobs | +| `programs` | array | no | one entry per program in the workspace | +| `workspace` | object | no | workspace-level layout | +| `clusters` | object | no | per-cluster RPC + wallet (`localnet`/`devnet`/`mainnet`) | +| `runtime` | object | no | local dev runtime preferences | +| `plugins` | array | no | declared plugins | -### `programs[]` +## `programs[]` | Field | Type | Required | Description | |-------|------|----------|-------------| -| `name` | string | yes | snake_case program name. | -| `path` | path | yes | relative to workspace root. | -| `program_id` | string | no | filled by `deploy`; `null` until first deploy. | +| `name` | string | yes | snake_case program name | +| `path` | path | yes | relative to workspace root | +| `program_id` | string | no | filled by `deploy`; `null` until first deploy | -### `networks.` +## `clusters.` | Field | Type | Required | Description | |-------|------|----------|-------------| -| `rpc_url` | URL | no | overrides the network's default RPC endpoint. | +| `url` | string | yes | RPC endpoint | +| `wallet` | string | yes | path to the default keypair for this cluster | -### `plugins[]` +Sunscreen ships defaults for `localnet`, `devnet`, and `mainnet`. The CLI accepts `mainnet` (not `mainnet-beta`) as the cluster target name; the underlying URL remains `https://api.mainnet-beta.solana.com`. -| Field | Type | Required | Description | -|-------|------|----------|-------------| -| `source` | string | yes | local path or git URL. | -| `version` | semver | yes | with or without `v` prefix. | -| `hooks` | array | no | list of hook names (`before_build`, `after_build`, `after_serve`). | - -### `runtime` +## `runtime` | Field | Type | Default | Description | |-------|------|---------|-------------| -| `validator` | enum | `auto` | `surfpool`, `test-validator`, `auto` (Surfpool if found, else test-validator). | -| `codama` | bool | `true` if `frontend != none` | run Codama during builds. | -| `frontend_notify_path` | path | `app/.sunscreen/reload` | file sunscreen touches on successful client regen. | - -## Environment overrides - -Any string field can be overridden via environment variable, name-prefixed with `SUNSCREEN_`: +| `engine` | enum | `surfpool` | `surfpool` or `test-validator` (sunscreen falls back from Surfpool to test-validator when Surfpool isn't on PATH) | +| `port` | int | `8899` | validator RPC port | +| `faucet_sol` | int | `100` | seed balance for the local runtime faucet | -| Env var | Field overridden | -|---------|------------------| -| `SUNSCREEN_WALLET` | `wallet` | -| `SUNSCREEN_FRAMEWORK` | `framework` (per-invocation) | +## `plugins[]` -Env overrides take precedence over the YAML. +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `source` | string | yes | local path or git URL | +| `version` | semver | yes | with or without `v` prefix | ## Migrations -When sunscreen reads a `sunscreen.yml` with a lower `version` than the current binary supports, it migrates in-place (writes a backup `sunscreen.yml.bak`). Migrations are deterministic — same input, same output. +When sunscreen reads a `sunscreen.yml` with a lower `version` than the current binary supports, it migrates in-place. Migrations are deterministic. ## Validation -`sunscreen.yml` is validated on every command. Failures exit with code `3` and a message naming the field: - -``` -error: invalid_config: programs[0].path: directory does not exist (programs/missing) -``` +`sunscreen.yml` is validated on every command. Failures exit with code `3` (`invalid_config`) and a message naming the offending field path.