# Export / Compile Soleprint's source tree is not what runs. `build.py` compiles the framework plus a room's configuration into a self-contained instance under `gen//`, and that directory is what a container boots, what `deploy.sh` rsyncs, and what the cluster manifests point at. Everything below is a `make` target, and every target is one script in `ctrl/`. The logic lives in the scripts, never in the Makefile. ```bash make build # cfg/standalone -> gen/standalone make build sample # cfg/sample -> gen/sample make start # run it ``` ## The targets | Command | Runs | Does | | --- | --- | --- | | `make build [\|all\|models]` | `ctrl/build.sh` | compile a room into `gen/` | | `make dist []` | `ctrl/dist.sh` | compile just the plexus UIs to single files | | `make docs [serve\|graphs]` | `ctrl/docs.sh` | serve the docs, or re-render the diagrams | | `make start [] [-d]` | `ctrl/start.sh` | run a built room's compose stack | | `make stop []` | `ctrl/stop.sh` | stop it | | `make cluster [up\|down\|status]` | `ctrl/cluster.sh` | the shared kind cluster | | `make component [list\|publish\|sync\|watch\|diff]` | `ctrl/spr.py` | publish a distributable component | | `make deploy` | `ctrl/deploy.sh` | rsync `gen/standalone` to the server and restart | Bare words pass straight through, so `make build sample` becomes `ctrl/build.sh sample`. Anything starting with a dash would be eaten by make itself, so those go through `ARGS`: ```bash make deploy ARGS="--build" make component ARGS="publish soleprint-ui /tmp/out --dist" ``` ## What a build does `python build.py --cfg ` runs these in order: 1. **Clean** `gen//`. A build is not incremental — a stale file left behind is worse than a slow build. 2. **Copy the framework.** `main.py`, `run.py`, `index.html`, `Dockerfile`, `requirements.txt`, `dataloader/`, `common/`, and the three systems (`artery/`, `atlas/`, `station/`). 3. **Merge the room** (`copy_cfg`). `cfg//config.json` lands in `cfg/`, `data/*.json` in `data/`, and anything under `cfg//soleprint/artery|atlas|station/` is merged *over* the framework copy — which is how a room adds its own vein, shunt, tool or generator without forking the tree. 4. **Compose cabinets.** The dependency containers the room declared in `data/cabinets.json` are merged into its `docker-compose.yml`. See [Cabinets](#station-cabinets). 5. **Export plexuses.** Each plexus the room declared in `data/plexuses.json` is compiled to a single self-contained `index.html` — theme, data and diagrams inlined, so it opens with no server. See [Plexuses](#artery-plexuses). 6. **Generate models.** modelgen reads the room's `config.json` and writes `models/pydantic/__init__.py`. 7. **Render k8s** (optional). When the room's config enables it, `soleprint/ctrl/k8s/` writes manifests and lifecycle scripts. ## What comes out A standalone room is a flat instance: ``` gen/standalone/ run.py main.py Dockerfile docker-compose.yml artery/ atlas/ station/ common/ cfg/config.json data/*.json models/pydantic/ plexuses//index.html # one file each, opens with no server ``` A **managed** room — one that wraps an existing application — is three folders instead, because soleprint sits beside the app rather than containing it: ``` gen// / the application's repos, plus its ctrl scripts link/ bridge code between the two soleprint/ the instance, exactly as above ``` `build.py` picks between them on whether the room's `config.json` has a `managed` block. `gen/` is gitignored in full: it is an artifact, and the way to change it is to change `cfg//` and rebuild. ## Distributing components Rooms are compiled; *components* are published. `registry.json` lists what can be shipped out of this repo on its own: ```bash make component # list make component ARGS="publish soleprint-ui /tmp/out --dist" make component ARGS="watch soleprint-ui ../unt/ui/framework" ``` `--dist` copies only the built bundle — `dist/**` plus `package.json`, `README.md` and `LICENSE` — rather than the source. It refuses to publish an empty `dist/`, because a component whose bundle was never built is the failure that shows up later as a container that starts and renders nothing: ``` no build at soleprint/common/ui build it first: cd soleprint/common/ui && pnpm build ``` Each publish leaves a `.spr` stamp in the destination recording name, version, type, source and mode, so a copy can say where it came from. ### Slices: only what a consumer uses `publish` hands over the whole component. A **slice** hands over the part one consumer actually uses, as readable source, computed rather than picked: ```bash make ui # list: every export and part, and what it pulls in make ui scan ../mpr/ui/detection-app # what an app uses; its framework copy against spr ./ctrl/ui.sh slice path/to/ui.toml # write the slices a run file lists ./ctrl/ui.sh check path/to/ui.toml # the same, writing nothing — exit 1 if stale ``` A run file names the consumer, where the slice goes, and whether it is read from the consumer's own imports (`scan`) or named (`use`). `target = "vue"` takes files from `common/ui/src` — `Panel` alone is one file and `vue`; `TimeSeriesRenderer` brings uplot and nothing else does. `target = "plain"` takes parts from `common/theme/parts` as one `ui.css` and one `ui.js`. See `soleprint/common/slice/ui.example.toml`. Two deliveries: | `delivery` | for | what changes | | --- | --- | --- | | `dependency` | your own projects | nothing: spr's names stay, and a `.spr` stamp says where the copy came from | | `vendored` | a repository that is not spr's | names go through a mapping profile, headers are stripped, no stamp is written, a scrub fails on any spr word, and `notice` must be stated | Every run writes a manifest beside its run file recording the hash of each file it wrote. That is what makes drift visible both ways: when spr moves on, `check` says the slice is stale; when someone edits the copy, the next run names the file and refuses to overwrite it without `--force` — so an improvement made in a consumer can be brought back instead of lost. ## Deploying ```bash make deploy ARGS="--build" # rebuild, sync, restart make deploy ARGS="--sync-only" # sync, leave it running ``` `deploy.sh` rsyncs `gen/standalone/` and runs `docker compose up -d --build` on the far side. `.env` is excluded, so server secrets stay on the server. ## Running without building `python run.py` from `soleprint/` serves every subsystem on one port (12000 by default) straight from the source tree. It is for developing the framework itself; a room's `cfg/config.json` does not exist there, so the landing pages fall back to their defaults. Rooms use docker. ## Diagrams The `.dot` sources under `docs/graphs/` carry structure; the palette lives in `docs/graphs/themes/*.gvpr` and is applied at render time, so one source renders in every theme. ```bash make docs graphs # every graph, every theme make docs graphs lucid # one theme ``` `.svg` is the dark default the docs link to; other themes write `..svg`. See [Themes](#themes).