Build your own on the platform.
An extension is a new face built right next to the data — a new UI, a new ingestor, a new domain — that runs in-process on the engine and reads the same one copy every other face reads. It owns no storage of its own, so there is nothing to sync and no second store to keep in step. This is the same Tier-3 pattern Semurg's own faces are built in.
A Tier-3 extension, defined.
An extension is a normal application in the engine's umbrella — it lives at
apps/<your_name> — that depends on one thing: the engine. It is built in
the same Elixir-over-Rust ("Octopus") pattern as Semurg itself, and it exposes its face with
Phoenix LiveView. Four properties define it, and all four are how Semurg's own faces already work:
apps/<name> Live
A first-class app in the umbrella. Its only in-tree dependency is the engine
app (substrate_core). It compiles, tests, and ships alongside the engine.
The substrate, and only the substrate Live
The extension holds handles the engine vends and calls the engine's data functions. It never opens a raw file or keeps a side store — handle, not open. One copy, read by every face.
Inward only Live
It builds on the engine and never reaches sideways into another
extension. The engine holds itself to this same strictly-inward rule, checked in CI
(mix semurg.check_rings).
Phoenix LiveView Live
The UI is a LiveView, the same stack the console renders through
(phoenix_live_view ~> 1.0). Server-rendered, live-updating, no separate
front-end build to keep in step with the data.
The guardrails every extension follows.
Whether you hand-build it or Helix builds it for you, an extension is only a valid Semurg extension if it holds to these. They are the same rules Semurg's own faces obey.
What the code looks like.
An extension is a small umbrella app. Its mix.exs declares exactly one in-tree
dependency — the engine — which is what makes "the substrate is my only data layer" true at the build
level, not just by intention:
# apps/my_extension/mix.exs defp deps do [{:substrate_core, in_umbrella: true}] # the engine — the ONLY data dependency end
The extension's own module is a share-nothing OTP process that owns the handles the engine vends and answers calls by delegating to the engine's data functions. It never touches a file itself:
# apps/my_extension/lib/my_extension/store.ex — sketch, real engine functions defmodule MyExtension.Store do use GenServer alias SubstrateCore.{Shard, Container} # the engine's public data surface # this process OWNS the handles — share-nothing, no shared-mutable state def handle_call({:neighbors, id}, _from, s), do: {:reply, Shard.neighbors(s.nav, id), s} def handle_call({:put_vectors, pairs}, _from, s) do batch = Enum.map(pairs, fn {node_id, code} -> Container.vec(vec_id(node_id), code, 1) # build 64-byte containers, hand the engine a BATCH end) {:reply, Shard.append(s.of_record, batch), s} end end
SubstrateCore.Shard (graph reads, k-hop, vectors,
append) and SubstrateCore.Container (the 64-byte content-addressed record) are the engine's
public data surface — the same functions the shipping template calls. Your LiveView calls your store; your
store calls the engine. Nothing else touches the data.
A working extension already ships in the tree. Live
You do not start from a blank page. The two-way database ingestor —
apps/db_ingest_substrate — is a real, tested extension that imports relational
and graph databases (Postgres, MySQL, SQLite, Neo4j) into the one substrate and reads them back out. It is
the canonical pattern to copy:
- Its
mix.exsdepends only on the engine — nothing sideways. - Its store is a share-nothing process that owns the engine's handles and calls
Shard.neighbors,Container.vec, k-hop and append — it keeps no second copy of what it imports. - It ships with golden tests (including a "no sidecar" test that proves it adds no side store).
Inward-only, held by the toolchain.
Semurg's own faces are arranged in strictly-inward rings — the UI ring depends on the intelligence ring, which depends on the engine, which depends on a shared base, and nothing points outward. That is not a convention people remember to follow; it is a property the build checks:
# runs first in CI, before compile — a violation is a RED build, not a code review mix semurg.check_rings ✓ semurg.check_rings: inward DAG intact
Your extension holds to the same discipline: it depends only on the engine app, so it can never entangle itself with another extension. The engine app declares no web framework at all — Phoenix lives only at the face — so the data layer stays clean of the UI.
What you can do today, and what's coming.
| Capability | Status | Detail |
|---|---|---|
| The Tier-3 extension pattern | Live | apps/<name>, engine-only data layer, handle-not-open, inward-only, no side store — real and in production use by Semurg's own faces. |
| A working template to copy | Live | The two-way database ingestor ships in the tree, tested, with a no-sidecar proof. |
| Phoenix LiveView UI | Live | The console and demos render through LiveView 1.0; your extension's face uses the same stack. |
| Octopus Elixir/Rust pattern | Live | The engine is built this way; the Rust NIF toolchain and share-nothing OTP model are the same you build in. |
| In-process extension development | Live | Offered to design partners and licensees today — talk to us and we get you the kit. |
| Public self-serve kit + one-command scaffold | Coming | A downloadable kit and a generator that stamps out apps/<name> with the store, LiveView, and tests wired. |
| Per-extension automated boundary gate | Coming | The same ring-check, pointed at your app, as a one-command guard. |
| Helix — the swarm builds it for you | Preview | In development; see below for the honest status. |
Describe it. A swarm builds the extension.
Helix is the shortcut to everything above. You write a short spec — what the extension should do — and a small team of role-agents takes it from there: one develops it, one reviews it, a manager coordinates the work through an approval gate, and a security reviewer checks it. They iterate, run the tests, and deploy the finished extension onto the platform.
Developer
Turns the spec into an apps/<name> extension:
the store over the engine, the LiveView face, the tests.
Reviewer
Reads the diff for correctness and for the five rules — engine-only data, handle-not-open, inward-only — and sends it back until it holds.
Manager
Coordinates the loop and carries the work through an approval gate. It coordinates; it does not silently override the others.
Security
Checks the extension against the boundary and safety rules before anything is deployed. Nothing ships that fails this gate.
The cap — what Helix is, and is not
Helix is deliberately narrow. It is not a general coding tool. It builds one kind of thing: extensions and apps that run on the platform, and every one it produces must clear the same guardrails as a hand-built extension.
- It only builds extensions and apps on the platform — not arbitrary software.
- Every extension it builds must use the substrate as its data layer — no side store.
- Every UI it builds must be Phoenix LiveView.
- Every extension must follow the Octopus Elixir/Rust pattern.
- Nothing deploys until the review and security gates pass.