![Bldr](./doc/img/bldr-logo.png) ## Introduction **Bldr** leverages the Aperture Stack to build modular peer-to-peer applications with real-time user interfaces that run anywhere. Deploys hot-loaded plugins via the p2p network. The bundler can deploy to many target environments: - **CLI**: client interfaces on the command line. - **Daemon/Cloud**: running as a native Go process. - **Native**: supports Electron and/or OS-provided web views. - **Firmware**: embedded devices (using TinyGo). - **Mobile**: using the gomobile tool. - **Web Browser**: using WebAssembly and WebWorkers. Uses the Aperture Robotics stack to implement a full peer-to-peer application: - [ControllerBus]: communicating controllers w/ declarative config. - [Bifrost]: p2p communications + pub-sub with pluggable transports. - [Hydra]: storage engine with advanced p2p block-graph structures. - [Identity] and [Auth]: public-key identity and key derivation. - [staRPC]: bi-directional streaming RPCs between TypeScript and Go. - [rGraphQL]: live-updating streaming GraphQL requests w/ lazy-loading. [ControllerBus]: https://github.com/aperturerobotics/controllerbus [Bifrost]: https://github.com/s4wave/spacewave/tree/master/net [Hydra]: https://github.com/s4wave/spacewave/tree/master/db [Identity]: https://github.com/s4wave/spacewave/identity [Auth]: https://github.com/s4wave/spacewave/auth [staRPC]: https://github.com/aperturerobotics/starpc [rGraphQL]: https://github.com/rgraphql/magellan Supports advanced data structures (even in the web browser) including: - **Blob**: split a large piece of data into deterministic chunks. - **File**: collection of written Ranges composed of Blobs of data. - **Git**: code revision tracking engine with go-git. - **Graph**: graph database w/ quads: `` - **Kvtx**: transactional key/value store (i.e. AVL tree). - **Sql**: SQL data store backed by GenjiDB or go-mysql-server. - **UnixFS**: directories, files, permissions, FUSE mounts. - **World**: key/value store coupled with a graph database + changelog. Each UI and application module is bundled separately and can use any linting, compilation, and frontend approaches. ## Overview Main concepts in the development workflow: - **Entrypoint**: manages executing plugins and starting the initial plugin. - **Plugin**: contains **controller** factories and a startup **ConfigSet**. - **Controller**: goroutine managing a portion of the application logic. - **ConfigSet**: list of controllers to start with configuration objects. The bldr developer tool has the following major command categories: - **start**: starts applications in development mode - **deploy**: pushes plugins to a plugin registry - **dist**: bundles distribution archives (release tarballs) The bldr developer tool uses Go and **esbuild** to bundle Go, JavaScript, TypeScript, and other assets into **Plugins**. When a **Plugin** is loaded, its startup **ConfigSet** is applied, executing any configured startup controllers. **Plugins** can communicate with the host and each other via RPC services. The **LoadPlugin** directive instructs the plugin host to load a plugin by ID. ### CLI The **CLI compiler** (`bldr/cli/compiler`) generates standalone command-line binaries. It scans Go packages for controller factories (`NewFactory`) and CLI command providers (`NewCliCommands`), embeds a ConfigSet, and compiles a self-contained binary that boots a DevtoolBus on startup. See [doc/CLI.md](./doc/CLI.md) for the full guide and [example/cli/](./example/cli/) for a working example. ### Web The **web** layer for bldr adds additional concepts: - **WebDocument**: browser page, tab, or Electron BrowserWindow. - **WebView**: location in the WebDocument where Go can load components. - **WebRuntime**: interface to access the Go runtime from JavaScript. It uses the following browser mechanics: - **BroadcastChannel**: communications channel between two Js components. - **SharedWorker**: parallel background worker shared between all tabs. - **ServiceWorker**: intercepts HTTP requests and forwards to Go runtime. When running as a native application the Go process is the initial entrypoint to the application, and will start the WebRuntime as a sub-process. For example: extracting & starting the Electron redistributable. The Web frontend communicates with the Go backend via [RPC streams]. The frontend and backend can be located in the same browser, as a native process bundled with an Electron app, or separated into client/server. [RPC streams]: https://github.com/aperturerobotics/starpc The **ServiceWorker** intercepts HTTP requests to the `/b/` and `/p/` paths. Plugins control the URL space below `/p/{plugin-id}/` and can serve any Go HTTP handler at that path, including static assets bundled with the plugin. ### Esbuild The plugin compiler scans Go code for comment directives, ex: ```go // Entrypoint is the component entrypoint. // //bldr:esbuild root.tsx var Entrypoint bldr_web_bundler.WebBundlerOutput // Alternatively you can use a string path to the .js entrypoint. // //bldr:esbuild root.tsx var EntrypointURL string ``` The available comment directives are documented here: ### Vite The plugin compiler can use Vite to bundle the contents of the given entrypoint to the plugin asset filesystem. ```go // ExampleVitePlugin is the Vite plugin configuration. // //bldr:vite vite.config.ts var ExampleVitePlugin bldr_web_bundler.WebBundlerOutput ``` All files output from Vite are served starting at the plugin asset path. The root index.html from a Vite build can be accessed by setting this variable. ### `bldr:asset` ```go // AppFavicon is the favicon .ico asset. // //bldr:asset favicon.ico favicon.ico var AppFavicon string ``` Recursively copies a file and/or directory to the asset filesystem. Paths are relative to the Go file containing the comment. The resulting URL is stored in the variable associated with the comment. ### `bldr:asset:href` ```go // AppFaviconHref is the URL to the .ico icon asset. // //bldr:asset:href favicon.ico var AppFaviconHref string ``` Determines the URL to the given path relative to the assets filesystem root. Conceptually similar to `path.Join(assetsFs, givenPath)`. The resulting URL is stored in the variable associated with the comment. ### `bldr:esbuild` ```go //bldr:esbuild --any-esbuild-flag component.tsx var Component bldr_web_bundler.WebBundlerOutput ``` Uses Esbuild to bundle the contents of the given entrypoint to the plugin asset filesystem. CLI arguments for esbuild are accepted in the comment. Multiple lines with the bldr:esbuild prefix are joined into a single esbuild command. Stores the URL to the root javascript and CSS file in the variable associated with the comment. An optional flag is the `--bundle-id=default` flag. All bldr:esbuild directives with the same bundle ID will be combined together into a single esbuild bundle request, with one esbuild entrypoint per bldr:esbuild directive. If you want to have a separate bundle, specify a different `--bundle-id=value` in the comment. Flags can also be specified in the plugin compiler config with "esbuildFlags". ## Build Tags bldr will set the following build tags: - `bldr_analyze`: set while analyzing the code for factories - `build_type_dev`: set during development build - `build_type_release`: set during release build ## Developing You need the following tools installed: - [Go](https://golang.org) >= 1.22 - [Bun](https://bun.sh) Initial setup: ```bash # download deps bun install ``` Here are some of the available ways to start bldr from the spacewave repo root: ``` # start web application with GoScript browser Go plugins (loads web plugin via --plugins) bun run bldr:start:web # start web application with standard Go/WASM browser Go plugins bun run bldr:start:web:wasm # explicit alias for the default GoScript mode bun run bldr:start:web:goscript # start native application (loads web plugin via --plugins) bun run bldr:start:desktop # start CLI application (loads bldr-demo-cli plugin via --plugins) bun run bldr:start:cli # build web (wasm) distribution bundle & serve bun run bldr:start:web:release # build release bundle for all platforms bun run bldr:build:release:cross ``` ### Terminal Status and Logs `bldr start` and `bldr build` show a live terminal dashboard when stdin and stderr are terminals. The dashboard projects the command state, manifest fetch and build progress, plugin health, controller activity, and recent errors from the devtool status model. Press `Ctrl-C` to cancel the command; press `o` from a web start dashboard to open the browser. Use `--no-tui` or `BLDR_NO_TUI=1` to keep plain terminal output. Diagnostic logs are written to a timestamped file under `.bldr/logs/` while the dashboard owns the terminal. Concurrent `bldr start`, `bldr build`, and `bldr publish` commands against the same `--state-path` serialize on `.bldr/state.lock`. When a second command would collide with an active state owner, Bldr prints the holder PID and state root before waiting for the lock. Use separate `--state-path` values only when CI or local proofs need independent web and desktop state trees to run in parallel. ### Start Plugins The `bldr start` command accepts a `--plugins` flag to load additional plugins on startup. These are merged with any plugins declared in `bldr.yaml` under `start.plugins`. ```bash # load specific plugins for the start session bldr start --plugins web native bldr start --plugins bldr-demo-cli native bldr start --plugins web --plugins extra-plugin web ``` The `start.plugins` list in `bldr.yaml` declares plugins that are always loaded. The `--plugins` flag adds to this list without modifying the config file, allowing different start configurations for different targets (native, web, CLI). ### Environment Variables - `BLDR_FROM_SOURCE=1`: Forces building saucer from vendored C++ sources instead of resolving a prebuilt binary from the `@aptre/bldr-saucer` npm package. Useful when developing saucer itself or when prebuilt binaries are not available for your platform. ```bash export BLDR_FROM_SOURCE=1 bun run bldr:start:desktop ``` ### Chromium SharedWorker Debugging In Chromium: to view the SharedWorker developer tools: - Open chrome://inspect - Select "Shared workers" - Click "inspect" on the SharedWorker ### Testing Run unit tests: ```bash bun run bldr:test ``` Run browser E2E tests (requires a web release build): ```bash # Build the web release bundle first bun run bldr:build:release:web # Run E2E tests in headless Chromium bun run bldr:test:e2e ``` The E2E tests use Vitest browser mode with Playwright to test the full web release build, including service worker registration, SharedWorker initialization, and WASM runtime loading. ### VSCode Debugging To debug the Electron `main` process: - Press Command + Shift + P - Select "Debug: Attach to Node Process" - Select the Electron instance. To debug the Electron `renderer` process: - Copy bldr/.bldr/src/.vscode/launch.json to `.vscode/launch.json` - Select "Run and Debug" in the left bar. - Click the green play button for "Debug Electron Renderer Process" This is only enabled in Debug builds for Electron, where the plugin compiler will automatically configure the web plugin to start Electron with `--inspect=5858` for the main process. This is enabled by the plugin compiler for Electron in debug builds only.