Documentation
¶
Overview ¶
Package demo holds the txco demo's curriculum — the tracks, steps, and starting ops that `txco demo` seeds into a chassis at boot. The SAME data feeds two consumers:
- chassis/cli/demo.runDemo → demo.Seed(adminURL) pre-seeds every stack against the loopback admin API before opening the browser, so the user never sees a "step failed to seed" error on first load.
- chassis/server/admin.handleDemoCurriculum exposes the same data at GET /v1/demo/curriculum so the admin-ui Runner can render the walkthrough (titles, prose, ops, request shape) without duplicating the curriculum in TypeScript.
Single source of truth: don't add a parallel definition in admin-ui/. The SPA fetches its rendering data from the endpoint above; the seeding is done already by the time the SPA mounts.
Index ¶
Constants ¶
const DefaultHostSuffix = "local.thanks.computer"
DefaultHostSuffix is the wildcard DNS suffix used for all demo stack hostnames.
Variables ¶
This section is empty.
Functions ¶
func Seed ¶
Seed walks the curriculum and ensures every step's stack exists on the chassis at `adminURL`, with the step's ops as the active version and `<stack>.<HostSuffix>` bound to it.
Called from `txco demo` after the chassis is ready but before the browser opens. By the time the SPA mounts, all stacks are already seeded — the Runner's onMount filter sees them via listStacks and does nothing, so the user never sees a "step failed to seed" error.
Sequencing: stacks are seeded SERIALLY (one chain at a time). The SPA used to run 3 concurrent chains for speed; the chassis's SQLite contention story under that load was lossy (~13% of chains hit a transient 500). Serial is ~3–4 s wall-clock for ~15 stacks, which is fine here — happens once at startup, in the background of the spinner.
Best-effort per step: if a step's chain fails (createDraft, etc.), Seed logs the failure and continues. The SPA's listStacks filter will catch any stack missing an active_version on first paint and (re-)seed just those. Worst case: one transient failure → the SPA quietly retries it on first load.
Types ¶
type Curriculum ¶
Curriculum is the top-level shape returned by GET /v1/demo/curriculum and consumed by Seed. HostSuffix is the wildcard DNS suffix every step's bound hostname uses (e.g. `<stack>.local.thanks.computer`); the wildcard resolves to 127.0.0.1 publicly so loopback "subdomain" routing works without per-machine DNS setup.
func Get ¶
func Get() Curriculum
Get returns the full demo curriculum. Stack names are derived (`<trackID>-<i+1>` for each step) and populated here so consumers don't have to know the convention. Returning a fresh value rather than a package-level pointer so callers (the seed loop, the HTTP handler) can mutate locally without affecting each other.
type Method ¶
type Method string
Method is the HTTP method a step's fire uses. The same restricted set the admin-ui's TypeScript declares; serialized as the bare verb string ("GET", "POST", "PUT", "DELETE").
type OpFile ¶
type OpFile struct {
Name string `json:"name"`
Scope int `json:"scope"`
Txcl string `json:"txcl"`
Js string `json:"js,omitempty"`
}
OpFile is one op in a step's ops list — its name, the scope it runs at (ops at the same scope run in parallel; later scopes run after), the TXCL source, and optionally a JavaScript "nano-op" compute source. When Js is non-empty the seed compiles it via the demo's /v1/demo/op/build endpoint (which bundles + javy-compiles + stores a `compute://sha256/<digest>` wasm artifact) and substitutes that ref into Txcl in place of `op://<name>` before activate. An empty-string Js (vs nil) opts the op into the SPA's JS textarea even when no source has been authored yet.
type Step ¶
type Step struct {
Title string `json:"title"`
Prose string `json:"prose"`
Ops []OpFile `json:"ops"`
Method Method `json:"method"`
Path string `json:"path"`
Body string `json:"body,omitempty"`
Stack string `json:"stack"`
}
Step is one stop on the walkthrough — a title + prose explaining what changed since the previous step, the cumulative ops list, and the request (method/path/body) to fire against it. Every step has its OWN stack (Stack), bound to `<stack>.local.thanks.computer` so tab switches are pure-navigation rather than re-applies.