mirror of
https://github.com/logos-co/logos-scaffold.git
synced 2026-08-27 10:51:16 +00:00
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
20 KiB
20 KiB
Scaffold — FURPS+
FURPS+
Functionality
- One public DevNet vertical slice: generate wallet, fund wallet, deploy contract, execute one transaction type, verify result.
- Integrate wallet generation as part of the scaffold workflow for bootstrap and interaction flows.
- Support native token topup for wallet operations on local and DevNet environments.
- Deploy command auto-discovers program binaries from
methods/target/by matching program names, so non-template projects deploy without--program-path. - Build command auto-compiles
methods/Cargo.tomlwhen present, so projects whose parent workspace excludes the Risc0 guest crate produce guest binaries vialgs buildwithout a separatecargo build --manifest-path methods/Cargo.toml. - Deploy outputs the deployed program's on-chain ID (the risc0 image ID) for every successful submission, in both the human-readable output and the
--jsonoutput, so users can hand the value to a client without rerunning a separate inspection tool. The value is computed locally from the submitted ELF; on-chain inclusion verification is future work and depends on LEZ exposing deploy receipts. - Scaffold vendors the
spelCLI per project — cloneslogos-co/spelto a project-local path, pinned via[repos.spel]inscaffold.toml, and builds it duringsetup— mirroring the LEZ vendoring pattern.deployinvokes the project-local binary; no globalspelinstall is required. The default spel pin is selected so spel itself vendors the same LEZ commit scaffold pins;doctorenforces this alignment at runtime by inspectingspel-cli/Cargo.tomland warning if spel's vendored LEZ diverges fromDEFAULT_LEZ_PIN. logos-scaffold spel -- <args...>(and thelgs spel -- <args...>alias) proxies trailing arguments to the project-vendoredspelbinary, so any spel command (inspect,pda,generate-idl, etc.) runs against the project's pinned version without a global install. Exit codes are forwarded.logos-scaffold deploy --jsonoutput is a pure JSON object on stdout — no command-echo, no informational text — and absent values are omitted from the object rather than emitted asnull. Consumers can pipe directly tojqand test field presence withhas(...). Subprocess command-echoes and progress messages stay off the JSON channel. Two shapes by code path:--program-path … --jsonemits a bare object{"status":"submitted","program":...,"tx"?:...,"program_id"?:...}; auto-discovery--jsonemits{"deploys":[<entry>,...]}with one entry per attempted program. Failed entries replaceprogram_idwitherror. Auto-discovery exit code is non-zero when any entry failed; the JSON object is still emitted so consumers can inspect partial results.
Usability
- Single command bootstrap with no manual project wiring required.
- Generated layout clearly separates contract code, client code, config, and deploy scripts.
- Deterministic wallet generation and .env handling for repeatability.
- Clear happy-path docs, reproducible setup, discoverable commands.
- CLI prints underlying commands for each step so users can drop down to lower-level tooling.
Reliability
- The vertical slice must succeed 3 times in a row on a clean machine with deterministic wallets.
- Local network can be started and torn down in isolation without modifying host-global blockchain state.
Performance
- Each workshop step must complete within a demo-tolerable threshold (a few minutes).
Supportability
- Scaffold version and toolchain versions are explicit in generated output so projects remain buildable over time.
- Network configuration for local and DevNet deployment is .env based config.
- The scaffolded project includes command references for build, deploy, and interaction steps.
logos-scaffold doctorreports thespelrepo presence and pin status, mirroring the existing LEZ checks, so drift fromDEFAULT_SPEL_PINis surfaced before it bites at deploy time.scaffold.tomlfiles predating the[repos.spel]section produce a targeted error from the config loader pointing atlogos-scaffold initas the fix;initis safe to re-run and back-fills the missing section without overwriting customized fields.
+ (Privacy, Anonymity, Censorship-Resistance)
- Local workflow does not require uploading source code, artifacts, or private keys to third-party services.
- CLI interaction flow works with locally controlled wallet keys and does not require custodial key management.
- Local development and testing can run fully offline from public networks.
- DevNet interaction uses explicit wallet and RPC configuration so developers can avoid accidental cross-network key reuse.
Dependencies
Internal Dependencies
- Logos Core DevEx for overall developer journey alignment and terminology.
- Logos Blockchain and Logos Execution Environment for functionality.
- Wallet Module for interactions with Logos Execution Environment.
logos-co/spelCLI — vendored per project at a pinned commit (DEFAULT_SPEL_PIN, currently tagv0.2.0); supplies thespel inspectoutput thatdeployparses for the program ID.
Runtime Dependencies
- Local network runtime availability for local deploy and interaction workflows.
- DevNet RPC endpoint availability and stable chain configuration.
- Deterministic local/DevNet account and chain configuration via environment files.
Wallet Dependencies
- Wallet available for signing transactions initiated by CLI interaction commands.
- Network-aware wallet configuration to prevent cross-network key misuse.
FURPS+ — lgs run
Functionality
lgs runcollapses the inner-loop sequence — build → IDL build → ensure-localnet → wallet topup → deploy → optional post-deploy hooks — into one command. Every step's failure aborts the pipeline with a numbered step header ([3/N] …) so the failing phase is unambiguous in console output.- Source edits drive fresh on-chain program identity automatically: when the guest ELF changes, its risc0 image ID changes, and the new program's storage starts empty. Scaffold relies on this for the default cycle and adds no per-run reset.
- Post-deploy hooks:
[run].post_deployis a list of shell commands executed in order viash -cwithcwdset to the project root. Hooks see a documented env contract:SEQUENCER_URL,NSSA_WALLET_HOME_DIR,SCAFFOLD_PROJECT_ROOT,SCAFFOLD_IDL_DIR, plus single-program shortcutsSCAFFOLD_PROGRAM_ID/SCAFFOLD_GUEST_BIN(set only when exactly one program is deployable). - CLI overrides:
--post-deploy <cmd>(repeatable) replaces[run].post_deployfor one invocation.--no-post-deployskips hooks entirely. The two flags conflict and are rejected at clap parse time. - Localnet reuse: if a managed sequencer is already running, the run reuses it. If the configured port is held by an unrelated process, the run aborts with a diagnostic naming the foreign PID.
- Topup safety: a wallet-topup confirmation timeout aborts before deploy so the developer is never left wondering whether deploy used a half-funded wallet.
Usability
- The command produces a single human-readable output stream with numbered step headers and one-line summaries per phase. No JSON output flag —
--jsonis reserved fordeploy's programmatic consumers. - The single-program shortcuts (
SCAFFOLD_PROGRAM_ID/SCAFFOLD_GUEST_BIN) cover the most common dogfooding shape (one guest program per project) without leaking ambiguous values into multi-program projects — they're unset when the project has more than one deployable program. - Hook log markers (
===> post_deploy[i/n]:and<=== post_deploy[i/n] OK) frame each hook's stdout for grep-friendly log reading.
Reliability
- The conflicting flag pair (
--post-deploy --no-post-deploy) is rejected at parse time, not silently coerced. - The pipeline anchors itself at the discovered project root:
lgs runfrom a subdirectory builds and deploys from the project root, not from cwd.
Performance
- The run is bounded by the underlying tools (cargo build, IDL test harness, sequencer startup, wallet topup, wallet deploy-program); scaffold adds no waiting steps beyond what each underlying command already imposes.
- Single-program metadata (program ID, guest binary path) is resolved once per invocation and reused across every post-deploy hook, so multiple hooks don't multiply
spel inspectcost.
Supportability
[run]round-trips cleanly throughparse_config/serialize_config. Default values are omitted from the serialized output to keep diffs minimal.- The hook env contract is documented in
README.mdand validated by unit and integration tests insrc/commands/run.rs::testsandtests/cli.rs. - Flag-conflict rejection messages list the conflicting flags and exit non-zero, matching clap's standard error format.
+ (Privacy, Anonymity, Censorship-Resistance)
- Hooks run locally with the developer's own wallet; no network egress beyond what the deploy step already needs.
- Post-deploy hooks have direct access to the deployer's wallet home via
NSSA_WALLET_HOME_DIR. Hooks are user-authored and trusted — same threat model asscaffold.tomlitself.
Dependencies
Internal Dependencies
cmd_build_shortcutfor the build phase.build_idl_for_current_projectfor IDL generation (no-op for non-lez-framework projects).cmd_localnet(start) for localnet lifecycle when no managed sequencer is already running.cmd_wallet_topup_innerfor the topup phase.cmd_deployfor deploy submission andextract_program_idfor image-ID extraction.
FURPS+ — Basecamp
Functionality
- Fetch and build a pinned basecamp (
nix build '.#app') and pinnedlgpmas project-local artifacts, in the same pin-isolated cache layout used for LEZ. - Pre-seed two isolated basecamp profiles (
alice,bob) per project for p2p dogfooding. - Build and install the project's
.lgxmodule(s) into one or both profiles vialgpm, with source resolution that follows the.#lgxflake-output convention used by existing modules. - Launch basecamp for a named profile with clean-slate semantics: kill any prior process tree for that profile, scrub the profile directory, reinstall recorded
.lgxsources, andexecbasecamp with profile-scopedXDG_*environment. - Set per-profile values for each module's documented port-override env vars on
launch(names owned by each module), so multiple profiles can coexist without port collisions on the same machine. basecamp build-portablebuilds the project's.#lgx-portableflake outputs (the variant that loads cleanly into a release basecamp AppImage), orders them topologically bymetadata.jsondependencies so leaves load first, symlinks the results into<project>/.scaffold/basecamp/portable/with names carrying the load order, and prints those symlink paths. The wipe-and-recreate on every run keeps the staging dir idempotent.- Source resolution for
build-portablereuses the same auto-discovery +--path/--flakeescape hatches asinstall, but targets#lgx-portableinstead of#lgx. scaffold.tomlgains one[modules.<module_name>]sub-section per captured module, withflakeandrole(project|dependency) fields. The collection of these sub-sections is the sole source of truth for the captured module set;basecamp.stateholds only derived artefacts (pin outputs, binaries). Sub-section form fits scaffold's existing line-oriented TOML parser — no inline tables.basecamp moduleswrites[modules]during capture. For each captured source, the command derivesmodule_nameas follows:path:flake ref → read<flake-path>/metadata.json, use.name. Deterministic..lgxfile path → readmetadata.jsonfrom the sibling directory if present; otherwise fall back to the filename stem.github:flake ref → heuristic: striplogos-prefix from the repo stem, replace-with_. Printed at capture time with an assumption note (see Usability 7).
- Dep resolution walks each project source's
metadata.jsondependenciesarray and, for each declared name:- Already keyed in
[modules]→ no-op (already covered, irrespective of role). - In
BASECAMP_PREINSTALLED_MODULES→ no-op (basecamp ships it). - Not covered → resolve a flake ref via the declaring source's
flake.lock, then the scaffold-default pin table. On success, insert into[modules]withrole = "dependency". - Unresolved after all fallbacks → fail with a targeted error naming the two user-side fixes (capture as project source, or add an explicit dependency entry). No silent skip.
- Already keyed in
[basecamp.dependencies](the legacy override table) is removed. Its role is subsumed by explicitrole = "dependency"entries in[modules].
Usability
basecamp setupis opt-in — it is never triggered implicitly bynew, the top-levelsetup, orbuild.- When
installorlaunchrun without priorbasecamp setup, the CLI prints a single one-line hint pointing at the required command instead of erroring with a raw subprocess trace. - When only
.#lgx-portableis found on a project, the CLI fails explicitly, names the missing.#lgxoutput, and suggests--flake <ref>#lgx-portablefor explicit opt-in. - Commands follow the existing
logos-scaffoldCLI idioms (subcommand groups,--helpoutput, project-context errors). - Projects exposing only
.#lgx(no.#lgx-portable) receive a targeted hint naming the missing attribute and suggesting--flake <ref>#lgx-portablefor explicit opt-in — mirror of theinstallfailure mode, in reverse. build-portablestages a user-facing mirror of every built artefact as a symlink under<project>/.scaffold/basecamp/portable/<NN>-<module_name>.lgx. The two-digitNNis the load-order index so a file-browser lists the artefacts in the exact order basecamp needs to load them — the AppImage's "install lgx" picker sees human-named files in the right order rather than opaque/nix/store/…-source/…paths. Nix's own./result-lgx-portablesymlinks still land next to each flake; the scaffold-owned dir is a separate concern layered on top.- For each
github:flake where scaffold derivesmodule_namefrom the repo slug,basecamp modulesprints exactly one assumption note at capture time: the flake ref and the inferredmodule_name, with "edit[modules]in scaffold.toml if wrong." One-time UX cost, never repeats. scaffold.tomlis human-editable at all times.basecamp modulesis idempotent: if a key already exists in[modules], itsmodule_nameandroleare preserved (user intent wins over auto-derivation).- Unresolved dep diagnostics are a fail-fast error at
basecamp modulestime — the dep name must resolve to an entry in[modules], ametadata.jsonsource flake-input pin, the scaffold default pin table, or the basecamp preinstall list, otherwise the command exits non-zero before writing any state. No warn-and-skip path. - No migration path: the whole
basecampsubcommand is unreleased. Users on earlier iterations re-runbasecamp modulesagainst a fresh scaffold.toml.
Reliability
- Two
basecamp launchinvocations for different profiles on the same machine run concurrently without colliding on XDG paths, p2p identity keys, or module ports (subject to modules honoring the external port-override contract). basecamp setupis idempotent when the pinned commit is unchanged: no rebuild, no reseeding, no state mutation.basecamp launch <profile>produces a reproducible profile state — clean-slate on every invocation.rm -rfduring scrub targets only paths under<project>/.scaffold/basecamp/profiles/<name>/.build-portablewrites only under<project>/.scaffold/basecamp/portable/(a wiped-and-recreated staging dir of symlinks into the nix store), never invokeslgpm, and never touchesbasecamp.stateor thealice/bobprofile trees — so a failed portable build cannot corrupt install/launch state.- Dep resolution is deterministic given the same
scaffold.tomland sourcemetadata.jsonfiles — no reliance on github repo naming conventions, no string substring matches, no ordering dependencies. basecamp moduleswrites toscaffold.tomlatomically (write-temp-then-rename) so a crash mid-write cannot corrupt an otherwise-valid scaffold.toml.- Re-running
basecamp moduleswith an unchanged project set is a no-op againstscaffold.tomlcontents; hashes of the serialized section match byte-for-byte on re-entry.
Performance
basecamp installcompletes in the low-seconds range with a warm Nix cache; cold first-run wall-clock is bounded by upstreamnix build '.#lgx'time.basecamp setupfirst-run wall-clock is bounded by upstream basecamp +lgpmbuild time; re-runs on unchanged pin are effectively instant.
Supportability
logos-scaffold doctorgains a basecamp section when.scaffold/basecamp/exists, covering binary presence, profile integrity, and installed-module state.- Basecamp and
lgpmpinned commits are explicit inscaffold.toml. .scaffold/state/basecamp.stateis plain-text and line-oriented, matching existing scaffold state conventions.- Dogfooding scenarios (
B1–B4inDOGFOODING.md) cover setup, single-instance, multi-instance p2p, and clean-slate behaviors. build-portable's manual load-into-AppImage step is explicit: scaffold stages browsable symlinks under.scaffold/basecamp/portable/but does not know or auto-feed the AppImage's install dialog. The AppImage lifecycle is intentionally outside scaffold's scope — see ADR "AppImage Path is Outside Scaffold's Scope".- Known limitation: multi-sub-flake projects must unify transitive
logos-module-builderreferences viainputs.<dep>.inputs.logos-module-builder.follows = "logos-module-builder". Without it,installcan fail via the overridden sibling's lock even when a directnix buildsucceeds. Documented fully indocs/basecamp-module-requirements.md; expected to become obsolete once upstreamlogos-module-builderscaffolding emits thisfollowsautomatically. - Assumption notes from Usability 7 are printed to stderr (not the captured log), so pasting them into a bug report is straightforward.
scaffold.tomldiffs in version control surface module-identity changes as explicit, reviewable edits — same footing as any other project config change.
+ (Privacy, Anonymity, Censorship-Resistance)
- Per-profile isolation of p2p identity keys: fresh profile directory produces a fresh libp2p / Waku identity with no cross-profile leakage.
- No state mutation outside the project's
.scaffold/directory — the user's global Logos state is never touched. - No telemetry, no upload of module artifacts, identities, or profile state to third-party services.
Dependencies
Internal Dependencies
- Logos Basecamp (dev variant only).
- Logos Package Manager (
lgpm). - Module repositories (delivery, storage, etc.) exposing env-var overrides for every listening port, with env var names chosen and documented by each module; tracked via upstream issues (e.g., logos-delivery-module#18).
- Module
metadata.jsonschema:name(string),dependencies(array of strings). Already documented indocs/basecamp-module-requirements.md.
Runtime Dependencies
- Nix with flakes enabled on the developer machine.
- Qt build toolchain (supplied via the basecamp flake dev shell).
- Unix-like OS (Linux, macOS). Windows is out of scope.
Module Dependencies
.#lgxflake output on the project (or sub-flakes) —.#lgx-portable-only projects fail explicitly until they expose.#lgx..#lgx-portableflake output for any module the developer wants to test against a basecamp AppImage. Projects without it get a clear error frombuild-portable, not a silent miss.- Modules that bind sockets must honor external port override via env var (names chosen by each module) for multi-instance launch to be fully useful.