# Getting started
This walks the full **parse → validate → compile → run** loop: you'll scaffold a
project, write a node, wire it into a network, and run a message through it. It
assumes you've skimmed [Core concepts](concepts.md) — node, port, effect, schema,
pure-core/effect-shell, network, edge.
> bloccs targets Elixir `~> 1.18` on the BEAM.
## 1. Start a project
The quickest start is the scaffolder — it writes a runnable project with bloccs
wired in, a sample node and its schemas, and a one-node network. Install the
generator as a Mix archive, then scaffold:
```bash
mix archive.install hex bloccs # makes the `mix bloccs.new` generator available
mix bloccs.new my_flow
cd my_flow
mix deps.get
```
```
my_flow/
├── nodes/hello.bloccs # a node manifest
└── networks/hello.bloccs # a network manifest wiring it up
```
You can validate and compile what it generated right away (below).
**Already have a project?** Add bloccs to its deps instead, and create the
`nodes/` and `networks/` directories yourself:
```elixir
# mix.exs
def deps do
[{:bloccs, "~> 0.1"}]
end
```
`:req` (for the real HTTP adapter) and Ecto (for the real DB adapter) are
*optional* — bloccs ships with mock adapters by default, so you add those only
when you want real backends. See [Effect adapters](effect-adapters.md).
The rest of this guide builds a node by hand so you see every part.
## 2. Write a node
A node is **one manifest + one implementation module**.
### The manifest (`nodes/greet.bloccs`)
```toml
[node]
id = "greet"
version = "0.1.0"
kind = "transform"
[doc]
intent = "Uppercase a name and greet it."
owner = "@me"
[ports.in]
name_received = { schema = "Name@1" }
[ports.out]
greeted = { schema = "Greeting@1" }
# No external world touched → no [effects] block needed.
[contract]
pure_core = "MyFlow.Nodes.Greet.transform/2"
effect_shell = "MyFlow.Nodes.Greet.execute/2"
```
### The implementation (`lib/nodes/greet.ex`)
`use Bloccs.Node` reads and validates the manifest at compile time. The two
functions named in `[contract]` are the **pure core** and **effect shell**:
```elixir
defmodule MyFlow.Nodes.Greet do
use Bloccs.Node, manifest: "../../nodes/greet.bloccs"
# pure core: no IO, just computation
@spec transform(map(), Bloccs.Context.t()) :: {:ok, map()} | {:error, term()}
def transform(%{name: name}, _ctx) when is_binary(name) do
{:ok, %{message: "Hello, #{String.upcase(name)}!"}}
end
def transform(_, _ctx), do: {:error, :invalid_name}
# effect shell: would touch the world here; this node just emits
@spec execute(map(), Bloccs.Context.t()) :: {:emit, atom(), map()}
def execute(intermediate, _ctx) do
{:emit, :greeted, intermediate}
end
end
```
A node that *does* touch the world declares the capability in `[effects]` and
reaches it through `ctx.effects` in the shell — for example
`Bloccs.Effects.HTTP.post(ctx.effects.http, url, body)`. Any effect you use but
didn't declare is refused at runtime (and warned at compile time).
### Register the schemas
Ports reference `Name@N` schemas; register them at app start:
```elixir
# lib/my_flow/schemas.ex
defmodule MyFlow.Schemas do
alias Bloccs.Schema
def register do
Schema.register("Name@1", name: :string)
Schema.register("Greeting@1", message: :string)
:ok
end
end
```
Call `MyFlow.Schemas.register/0` from your application's `start/2`.
## 3. Validate
```bash
mix bloccs.validate nodes/greet.bloccs
```
`bloccs.validate` auto-detects node vs network manifests. It checks ports are
declared, effects are well-formed, the `pure_core`/`effect_shell` refs are
well-shaped, and (for networks) that edges connect real ports with matching
schemas and form a DAG. Errors are printed with file + section pointers; the task
exits non-zero on failure.
## 4. Wire a network
```toml
# networks/greet.bloccs
[network]
id = "greet"
version = "0.1.0"
runtime = "beam"
[nodes]
greet = { use = "../nodes/greet.bloccs" }
[expose]
in = { input = "greet.name_received" }
out = { output = "greet.greeted" }
[supervision]
strategy = "one_for_one"
max_restarts = 5
max_seconds = 60
```
Validate it, then compile it:
```bash
mix bloccs.validate networks/greet.bloccs
mix bloccs.compile networks/greet.bloccs
```
`bloccs.compile` emits **real `.ex` source** to
`_build/<MIX_ENV>/bloccs_generated/greet/` — one Broadway pipeline module per
node plus a supervisor. Open them: legible output is a feature, not a
side effect.
## 5. Run a message through it
```bash
mix bloccs.run networks/greet.bloccs --message '{"name": "ada"}'
```
`bloccs.run` compiles the network, starts the generated supervisor, and feeds the
JSON message into the exposed input port (defaults to the first `[expose].in`
entry; override with `--port`). Without `--message` it starts the tree and waits
so you can drive it from IEx or a test.
To record what the run touched:
```bash
mix bloccs.run networks/greet.bloccs --message '{"name": "ada"}' --trace run.bloccs-trace
mix bloccs.coverage networks/greet.bloccs --trace run.bloccs-trace
```
`bloccs.coverage` reports every in-port, out-port, and edge against the set the
run actually reached.
## Where to go next
- [Manifest reference](manifest-reference.md) — every TOML field, typed.
- [Effect adapters](effect-adapters.md) — swap the mock HTTP/DB adapters for real
`Req`/`Ecto`.
- [Architecture](ARCHITECTURE.md) — how the compile pipeline and runtime are
built, module by module.
- `examples/` in the repo — a graded ladder: [`tour`](https://github.com/Bloccs/bloccs/tree/main/examples/tour)
(core concepts, `mix tour.hello`), [`events`](https://github.com/Bloccs/bloccs/tree/main/examples/events) (the flagship
webhook processor, the `events.demo` task), and [`real_backend`](https://github.com/Bloccs/bloccs/tree/main/examples/real_backend)
(a real-HTTP-to-SQLite demo, `mix price_watch.demo`).