# Pipecircuit CLI
Install the released command from crates.io; no framework checkout is needed:
```sh
cargo install pipecircuit-cli --locked
pipec --version
```
If you use Binstall, `cargo binstall pipecircuit-cli` also works. This release
provides source packages, not first-party prebuilt binaries; Binstall may fall
back to compiling the published crate. For a reproducible tool version, append
`--version 0.0.12` to either installation command.
From the declared project root or an application's Cargo workspace, run `pipec arch check` directly. Cargo
and the workspace's resolved dependencies must be available; analysis is offline
and does not build the application or modify its lockfile.
Create and run a workspace with a portable core and a routing-only switchboard:
```sh
pipec new my-program
cd my-program
cargo run
cargo test --workspace
pipec arch visualize --machine Latch --manifest-path core/Cargo.toml
```
The finite example activates a core machine, reports its outcome, and exits via
`pipecircuit.conduct()`. `core/src` owns primitives and machines;
`apps/<app>/pipes` owns contracts, `apps/<app>/switchboard` owns routing, and
`apps/<app>/wiring` owns physical assembly. `apps/<app>/src/main.rs` starts the app. All semantic crates
are `no_std` and every package shares strict Rust/Clippy lints. Architecture and
behavior snapshots are derived from the actual source and compiled tests.
For a matching unpublished checkout:
```sh
pipec new my-program --framework-path /path/to/Pipecircuit/pipecircuit
```
Without this option, the template uses the exact registry version pinned in
`src/new.rs`. `--name` overrides the destination's package name. Existing destinations
are refused. Generation uses embedded assets and performs no network requests,
Cargo execution or Git initialization. On I/O failure it retains the partial new
directory for inspection.
Choose the existing Mio host instead of the portable startup reactor:
```sh
pipec new native-program --template native
cd native-program
cargo run
```
This uses the same core and named pipe roster, with a one-slot SPSC exchange and
real Mio wake. It is a finite, single-threaded example: one local request, one
reply, then exit. The generated application chooses bindings, queue bounds,
submission policy and completion; the framework supplies polling and dispatch.
Output errors preserve the confirmed reply. No worker thread or socket is created.
These variants are starting examples, not production service templates. Long-running
intake needs admission and lifecycle policy. With `--framework-path`, native mode
also uses `libraries/mio` from that checkout. Physical applications live under `apps/`, sharing portable core libraries.
Run the same semantic program in a dedicated browser worker:
```sh
pipec new browser-program --template browser
cd browser-program/apps/browser-program/wiring/web
npm install
npm run dev
```
Requires Node, Rust's `wasm32-unknown-unknown` target, and the wasm-bindgen CLI
matching the generated manifest's exact version. `npm run dev` builds the Rust
worker before starting Vite, then automatically rebuilds on Rust/Cargo edits.
Edits during compilation coalesce into one follow-up build. A failed build keeps
the previous worker running; a successful build reloads the page with fresh state. `npm run build` creates static output in
`web/dist`; `npm run preview` serves that output locally.
```text
web/src/main.ts page: events and presentation
⇅ generated codec / bounded transferable messages
apps/<app>/wiring/src/lib.rs worker: installed Rust program
⇅
apps/<app>/switchboard same routes as portable/native
⇅
core/src same authoritative Latch
```
The page has one pending request slot; no polling or retry loop. Disconnect closes
pending work, while initialization failures surface as unavailable confirmation.
A hung worker remains pending until explicit close; there is no automatic timeout
or retry. Reload starts a fresh worker. `web/vite.config.mjs` installs `rustWorker` from
`pipecircuit/vite`, using `web/worker.config.mjs` for both dev and manual generation.
It also rejects generated worker/WASM modules in production page chunks. This is
a build guard for that boundary, not a sandbox
against arbitrary application JavaScript.
The explicit request/reply fitting requires one immediate worker reply. It is not
a general streaming browser host. `flow_to_worker_reply` conducts the unchanged
switchboard; the worker export and generated codecs enforce the actual pipe types.
The small generator uses the plain pipe API because a unit command does not need
an artificial command/reply enum wrapper. Core serialization/binding derives are
optional; generation runs on the host. JavaScript owns DOM and browser bootstrap,
not domain state. Buffers transfer between realms; WASM boundary copies and codec
allocations remain—this is not an end-to-end zero-copy claim.
Run `npm test` after `npm run build` and installing Chromium with
`npx playwright install chromium`. It exercises real WASM workers in development
and production builds, repeat requests, capacity rejection, transferable buffer
detachment, pending shutdown, and initialization failure. Commit the generated
project's npm/Cargo lockfiles after installation. With `--framework-path`, the
browser variant uses the checkout's browser crate and binding generator; its npm
transport remains pinned to the published package.
The starter uses `generate_worker_pipe::<ActivationWorker>()` from
`pipecircuit-browser` 0.0.11. The framework derives the binding inventory and worker
entry from that export; the page connects through generated `startWorker`.
`tools/check-browser-scaffold` checks real development and production workers;
`--registry` verifies the published dependencies without checkout paths. The
check also edits a disposable starter to verify compile-error recovery and live
Rust rebuilds; it never edits a consumer application. See the [browser tooling
contract](../../libraries/browser/typescript/README.md#generated-output-ownership)
for generated-directory ownership, extra watch inputs and interrupted-build recovery.
## One behavior, three placements
Start with the behavior: a user asks to activate something, and repeated requests
must tell them it was already active. `Latch` is only this starter's example owner,
not a framework type. The core owns these steps:
```text
inactive + Activate → Activated, now active
active + Activate → AlreadyActive, still active
```
The `Client` pipe sends `Activate` out and accepts `Activation` in, from the
client's perspective. `Activator` exposes the opposite direction. The switchboard
routes the two closed families:
```rust
match from {
From::Client(request) => To::Activator(request),
From::Activator(activation) => To::Client(activation),
}
```
The application chooses the placement:
| Portable | One startup request; stdout reply | Inline in the executable | Startup reactor and `pipecircuit.conduct()` |
| Native | One-slot queues; local peer prints reply | Inline in the same native thread | Mio wake and hosted `pipecircuit.conduct()` |
| Browser | Button request; rendered reply | Inline inside a dedicated Rust/WASM worker | Browser message event; `flow_to_worker_reply` conducts the synchronous route |
```text
Browser main thread Worker thread
button → generated pipe ══ ArrayBuffer ══▶ decode → switchboard → Latch
status ← generated pipe ◀═ ArrayBuffer ═══ encode ← switchboard ← outcome
```
The worker retains the machine between messages. The page does not remember
whether activation happened; it renders the authority's reply. Moving between
variants leaves the core steps and switchboard route unchanged. The application
still chooses capacities, completion/shutdown and response presentation. Browser
serialization and worker boot are representation/placement work, not new domain
behavior.
Render other formats from the same source:
```sh
pipec arch visualize --machine Latch --manifest-path core/Cargo.toml --format mermaid
pipec arch visualize --machine Latch --manifest-path core/Cargo.toml --format graphviz
```
## Check and explore architecture
```sh
pipec arch check
pipec arch check --format json
pipec arch visualize
pipec arch visualize --format text
pipec arch visualize --format mermaid > architecture.mmd
pipec arch visualize --format html > architecture.html
pipec arch check --timings
```
The check is static and offline: it does not compile code, run build scripts,
fetch dependencies, or modify lockfiles. It examines all declared production
and build dependencies, including optional and target-specific edges. Development
dependencies are shown but do not establish production reachability.
Canonical `core/` and `apps/<app>/{pipes,switchboard,wiring}` paths establish roles.
Shared contracts belong under `apps/shared/pipes/`. Empty shared crates are not
created. Custom layouts declare the equivalent responsibility in Cargo:
```toml
[package.metadata.pipecircuit]
role = "switchboard"
app = "bakery"
```
Roles are `core`, `pipes`, `switchboard`, `wiring`, and `application`. Core has no
application owner. Shared pipes or wiring use `scope = "shared"` instead of `app`.
A declaration cannot contradict its canonical location. Every inspected workspace
package requires a role, including local helper crates. External dependencies are
not claimed to have been exhaustively audited; pipe and routing dependencies are
restricted to local semantic crates and approved no_std Pipecircuit capabilities.
A multi-workspace repository can select its project root and sibling workspaces:
```toml
[workspace.metadata.pipecircuit]
root = ".."
workspaces = ["../core/Cargo.toml", "vault/Cargo.toml"]
```
Paths are relative to the declaring workspace. List the same project from each
independently usable workspace entry point. From a directory without Cargo.toml,
the CLI checks `apps/Cargo.toml` and `core/Cargo.toml` for an explicit project-root
declaration. Linked declarations identify the entry point; separate, unlinked
workspace sets require `--manifest-path`. No recursive repository scan occurs.
Execution implementations can declare dependency isolation:
```toml
[package.metadata.pipecircuit]
role = "wiring"
app = "browser"
forbid_dependencies = ["client-worker", "audio-worklet"]
```
These names must identify inspected packages. The check rejects transitive
reachability through production/build dependencies; it does not prove where code
runs. Keep browser bundler placement guards and target compilation as separate
checks.
Text and JSON share findings, stable codes, manifest paths, and explanatory
next steps. JSON uses schema `pipecircuit.arch/1`. Exit codes are 0 for a completed
check without violations, 1 for violations, and 2 for an incomplete check or
invalid invocation. Timings go to stderr only when requested. They separate
Cargo discovery, validation, and rendering; work counts report metadata
invocations, parsed manifests, library roots, and inspected dependency edges.
`pipec arch visualize` opens a self-contained, offline HTML source map in your
default browser. Groups show app ownership and shared code. Select a package to
highlight dependencies, follow them, or open/copy its manifest location. Search
and app filters narrow the map; external/dev dependencies and check limitations
are available in the detail panel. The printed temporary HTML path remains usable
if a browser cannot open. Each invocation creates a snapshot, not a live server;
no renderer, JavaScript package installation, or CDN is required.
Explicit `--format` options write to stdout without opening a browser. Text gives
a grouped source guide; HTML saves the interactive view; Mermaid and Graphviz
export diagram source. JSON retains the full analysis and limitations.
These maps show source dependencies, not message flow. Semantic routes and actual
execution placement remain explicitly unknown in this static project overview.
`--machine` reuses source-derived machine diagrams. Existing compiled
`ProgramDiagram`/installation inspection remains available for typed pipe and
binding inventories; the CLI does not compile or execute consumer programs to
obtain them. `pipec diagram` remains an alias for the older machine surface.
Start an invariant change in the core owner. Start a new interaction at the app's
pipe contract, then follow its switchboard route to the machine that decides.
A machine's result is an outcome; once routed, that value is a message. Prefer
concrete domain names and bindings over mandatory `Command`/`Effect` suffixes.
Architecture checks establish boundaries, not application correctness: behavior,
coverage, mutations, proofs, portability builds, and performance remain explicit
forms of evidence.
The browser-view regression runs against an exported HTML snapshot, using the
Playwright installation of an existing browser starter:
```sh
node tools/pipec/tests/browser.mjs /path/to/starter/web/package.json /tmp/architecture.html
```