Week of 2026-05-15 — AETHER orchestration-substrate bootstrap

Started 2026-05-15 · active · SMC17/aether

The orchestration-substrate week. AETHER is the Elixir/Phoenix row of the six-layer substrate stack described in the [portfolio design-system spec][1] §4 — the load-bearing piece that sits above the NATS bus and below the user-facing surfaces. Before this week the row was a blank line in the diagram. By the end of the day it was running.

[1]: /codex/methods/stax-dev-portfolio-design-system

2026-05-15 — bootstrap, end to end

What landed

LiveView 1.1 + Bandit. Initial commit 4182deb.

connection, registered as Aether.Nats.Conn, owned by a supervised GenServer. pub/2 JSON-encodes maps and forwards to gnat; sub/2 hands the calling pid through to gnat so messages arrive as {:msg, %{topic, body}}.

subscribes to lab.sonos.zone.<name>.state and registers in the unique Aether.ZoneRegistry. On every state push it merges the payload, broadcasts to a Phoenix.PubSub "zones" topic, and serves current_state/1 to the LiveView. Commands cast back the other way via lab.sonos.zone.<name>.cmd.

reads config :aether, :zones and starts one Zone per row. Idempotent on {:already_started, pid}.

columns: zone, state pill, volume slider, current track, controls. Volume slider is phx-change="volume" debounced at 180 ms; Play/Pause/Stop buttons cast via the Zone GenServer.

Total AETHER-specific code in this commit: 537 LOC across five modules. Phoenix scaffolding accounts for the rest of the repo.

Composition with the rest of the stack

AETHER is a new consumer of the existing the lab substrate. It does not modify the running NATS server, the Sonos bridge daemon, or the unified hctl CLI surface. The contract is:

hctl status (CLI)        ──┐
                           ├──► same lab.sonos.zone.*.state stream
AETHER dashboard (LV)    ──┘
                           
LiveView phx-click       ──► lab.sonos.zone.<name>.cmd
                                ↓
                          sonos_bridge.py (UPnP)
                                ↓
                          lab.event.audit (durable)

The bridge daemon owns the UPnP contract. AETHER never touches port 1400 directly. This is the substrate-discipline cut.

The two friction points

(1) Gnat subscription semantics. Gnat's sub/3 takes a subscriber pid and delivers {:msg, %{topic, body, ...}} directly to it. The natural pattern is to subscribe from inside each Zone's init/1 so the Zone's own mailbox receives the messages. Worked first try; the API is cleaner than the older gnat_consumer_supervisor pattern.

(2) The Aether.Zone GenServer is named via a {:via, Registry, ...} tuple. The LiveView looks up the pid via the Registry before casting. This avoids the global atom-name explosion (one named process per zone is fine at 10 zones; at 1000 it would not be).

Hot-reload demo — the design-system §4 contract

Per design-system §4, "hot-reload means edit-saves-takes-effect-mid-scene." The proof:

  1. elixir --sname aether --cookie aether-cookie -S mix phx.server
  2. Browse to localhost:4000. All 10 zones render with current state.
  3. Edit lib/aether/zone.ex's transport_pill/1 — change #2e7d32

(green) to #00bcd4 (cyan).

  1. From a sibling node:
   elixir --sname aether-rpc --cookie aether-cookie -e '
     true = Node.connect(:"aether@staxx")
     :rpc.call(:"aether@staxx", IEx.Helpers, :r, [Aether.Zone])
   '

Returns {:reloaded, [Aether.Zone]}.

  1. The 10 Zone GenServers keep running. Their state is untouched. The

next state push from any zone renders that row's pill with the new color. No browser refresh.

This is not "reload the page and it looks different." This is "edit the code while the state keeps flowing." The Erlang module hot-swap is the substrate; AETHER inherits it for free.

See <internal-lab>/aether/HOT_RELOAD_DEMO.md for the full sequence.

Verification record

vol=43, bonus_room STOPPED vol=20, office PAUSED vol=32, family_room STOPPED vol=61, master_bedroom STOPPED vol=51, patio PAUSED vol=64 The Next Episode, gym PAUSED vol=80 This Is It, bonus_rm_balcony STOPPED vol=30, pool STOPPED vol=64.

state — same transports, same volumes, same track strings. Confirmed via a grep against the rendered HTML.

HOMELAB_AUDIT stream sequence advanced from 3 to 4. The bridge logged the cmd; the Sonos returned an HTTP 500 (no queue loaded — expected for a play with no preceding play_uri); the audit record was durably stored regardless. The pipeline is complete.

Doctrine cross-references

substrate; Elixir orchestrates; Stax is meta." AETHER is the Elixir orchestrates row, materialized.

composition diagram is now matched by code.

[2]: https://github.com/SMC17/stax-blog (codex memory; not yet public)

What's deliberately not in v0.1

pipeline elements exist yet. That is the v0.2 Lineage-Mode audio companion's job.

host that LiveView, but the surface lives in the blog-builder until the integration phase.

AETHER body content, even though the Phoenix generator pulled those in. Substrate-discipline: no framework where a <style> block is enough.

Spec amendments needed

One. The design-system §4 entry says:

Live state — Phoenix LiveView server at <internal-lab>/aether/ (new) — only the

/workshop surface uses it.

That phrasing implies AETHER is only the live-state companion of the blog. In practice AETHER is also the standalone control plane for the lab — the Sonos dashboard at localhost:4000 has nothing to do with /workshop. Recommended amendment: phrase §4 as "AETHER is the orchestration substrate; one of its surfaces is the live-state injection into /workshop, but it also stands alone as the control plane."

The atlas is updated accordingly: repo/aether is its own node, with edges to method/portfolio-design-system (governed-by) and to edition/i-phonograph (companion-of — the Phonograph capsule's audio component will eventually be a Membrane pipeline running inside AETHER).


Next milestones:

audio. Lineage Mode companion.

abstraction extended for AirPlay 2 group controls.

blog-builder; integration cut.