Repository navigation
DeMoD Mixer (touch kiosk), gated DCF bridges, fixed control ops - #4
Merged
Merged
Conversation
…ders only
Both DCF relays used to act on whatever arrived. Measured against the bridge
before this change (gate_probe.py, 4/11): one spoofed 17-byte datagram from a
fresh socket made demod-remote-bridge stream ~230 meters datagrams back to it
in 0.4 s, because the telemetry peer was learned from any 17 bytes before any
check; a false HydraModem frame, a bad CRC, version 2 and a valid PING padded
to 64 bytes (cut to 17 by recvfrom) were all answered; and a DCF-Text message
carrying a newline put a second op (here load_fx) on the control socket behind
one reply.
The gate is Exsecutor's examples/custos, emitted as C (exsc --emitte c) and
held to the entry-23 codec certified against the 246 golden vectors.
- web/bridge: re-vendored from Punctim b206dcb ("dcf-ws-bridge: gate every
datagram with Exsecutor's custos; check Origin"). custos/custos.gen.c is
byte-identical to Punctim's copy and to what exsc emits from exsecutor
d1edfd5 (sha256 35a0cd84...acaa, emitted twice, identical). Punctim's
certify_gate.rs needs Punctim's codec/ and vectors, so the dev-dependency
is dropped (lock delta vs the old lock: cc + 2 deps) and tests/gate.rs
holds the linked unit to vector-free anchors. Four gates re-emitted from
mutated custos.exsc (core B version unchecked, sflags type unchecked,
17-byte frames refused, joint polynomial) each fail it.
- demod-remote-bridge links the same custos.gen.c. Admission, in order:
private source (loopback, RFC 1918, CGNAT 100.64/10, link-local; the
Oligarchy p2p rule), then the gate at the datagram's true length
(MSG_TRUNC), then 17 bytes only. Only an admitted frame can become the
telemetry peer. A text message containing LF/CR/NUL is refused with new
reply status CTL_REFUSED (3); dm_dcf.c names it "refused by bridge".
Refusals are logged at powers of two per reason: loud, bounded.
- MCP demod_engine_op: params may not set op/v/id. They overrode the
allowlisted op after the check. Latent: every op the orchestrator
dispatches today is on the list.
- dcf-ws-bridge checks Origin; docker/entrypoint.sh passes WS_ALLOW_ORIGIN.
- flake: dcf-ws-bridge doCheck = true (the tests are the gate).
Run, at this commit:
bash audio-stack/bridge/test/gate.sh: 11/11 on loopback; with iproute2 on
PATH, the namespace leg 13/13 (a valid PING from 203.0.113.7 ignored,
the same from 10.9.9.9 answered). Same probe vs the old bridge: 4/11.
cd web/bridge && cargo test: origin 5/5, gate 7/7.
nix build .#dcf-ws-bridge .#demod-remote-bridge (with git-protocol
--override-input for flake-utils/systems/nix-appimage/hydramesh, which
this sandbox cannot fetch as tarballs): both built, checkPhase 7/7 + 5/5.
examples/dcf_loopback.lua and remote_client.lua (DEMOD_RC_TEST=1) against
the new bridge + stub: PASS. ws_client.py -> dcf-ws-bridge -> bridge ->
stub: PONG + telemetry + op delivered. Origin https://evil.example -> 403,
http://localhost:8080 and no Origin -> 101.
make DCF=1 (nixpkgs SDL2 + Lua): dm_dcf.c compiles with no warnings.
Not run: ./dev check, loopback.sh and ws_loopback.sh as scripts (they call
nix develop / nix build without the overrides this sandbox needs; their
steps were run by hand above); engine_e2e.sh (no JACK here); CI.
Still open: dcf-ws-bridge's addpeer accepts any host:port (as upstream says);
the orchestrator's load_fx/synth.load dlopen any existing path the control
socket names; dm_dcf.c's UDP socket is unconnected and accepts any source.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YEapehGd6iN1d5vu53PEN7
…s fixed
dm.ctl_set_param / ctl_bypass / ctl_bpm / ctl_gain sent {"cmd":...}. The
orchestrator dispatches on "op" (Control.hs) and answered every one with
"missing op", so all four failed against the real engine; TERMINUS worked
around it with raw dm.ctl(). They send "op" now.
Framework (MPL)
- dm.ctl_request(json) -> ok, reply: the orchestrator's reply line, for ops
whose answer matters (get_health, list_slots). demod_control_request() in
C; send_raw is the same exchange without the copy.
- dm.meters_read(): demod-rt's segment on its own. dm.params_read() returns
nil whenever the orchestrator's params segment is absent, which also hid
the per-slot gain/pan/mute/solo readback.
- dm.mouse_down(): the primary button, or the first finger through SDL's
touch-to-mouse emulation. The shell's zone overlay reports taps only, and a
fader needs press-drag-release.
- DEMOD_KIOSK=1: fullscreen, no cursor, no minimise on focus loss.
DEMOD_FULLSCREEN=1: fullscreen alone. dm.kiosk() reports it.
- Fullscreen windows take the display's size. The framebuffer and the pointer
scaling were sized from the requested window (1280x720) until a resize
event happened to arrive, so taps on an 800x480 panel landed in the wrong
place.
apps/mixer (DeMoD Mixer, MPL-2.0, on the companion-shell SDK)
- Channels 1-8 / 9-16 and the master: fader (unity at 3/4, +3.5 dB top,
silence at the bottom), pan with detents at the centre and both ends,
mute, solo, stereo meters. The ENGINE page shows link, health, the 16
slots with bypass, and tempo.
- Engine local (control socket), remote (demod-remote-bridge over dm.dcf,
DEMOD_MIXER_ENGINE=remote:HOST) or none (a labelled SIMULATOR that keeps
retrying, so a kiosk that boots first attaches when the engine comes up).
- Strips show the engine's state, except for the control under a finger.
- Encoder-first as well as touch.
- Its own graphite/amber palette; shares no code with TERMINUS.
- nix run .#mixer; packages.demod-mixer wraps it for ArchibaldOS.
Gates (in ./dev check and ./dev test)
- tests/ctl_wire.sh runs the helpers against tests/fake_orchestrator.py,
which applies Control.hs's op rules and publishes through the real
DemodRtMeters layout. All helpers pass; with "cmd" restored, the
four helper checks and five wire checks fail.
- apps/mixer/test/mixer_e2e.sh replays a gesture script as the pointer:
- 13 checks: ops on the wire, all accepted; a drag streams 14 gains; the
readback matches what the engine applied;
- one slot is preset in the engine and never touched by the UI, so its
value can only come from readback. That check found the
dm.params_read() gap above.
- apps/mixer/test/taper_test.lua checks the fader law.
- ./dev check: all 14 gates pass. The real-engine e2e self-skipped (no
JACK here).
Not measured: SDL turning a real finger into dm.mouse_down (the replay
bypasses it), two faders at once (emulation follows the first finger), and
a real engine.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YEapehGd6iN1d5vu53PEN7
ArchibaldOS's companion images run DeMoD Mixer as the touchscreen kiosk on StarFive JH7110 boards. flake-utils' default systems leave riscv64-linux out, so no package existed there. The UI (C + SDL2 + Lua) evaluates for it; packages.riscv64-linux.demod-mixer instantiates. Not built here: there is no riscv64 builder in this environment. The Haskell orchestrator may not build on riscv64, and ArchibaldOS does not ask for it on that platform. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YEapehGd6iN1d5vu53PEN7
The one new source file in this branch without one. custos.gen.c stays without a header on purpose: it is compiler output that regen.sh compares byte for byte with a fresh emit (see PROVENANCE.md). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YEapehGd6iN1d5vu53PEN7
3 of 4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
Part of making ArchibaldOS a companion OS commanded by Oligarchy: rack units and boards show a DeMoD front panel on a touchscreen and drive either their own engine or the Oligarchy DSP VM's. ArchibaldOS's branch pins this one (
demodinput), so this merges first.demod-remote-bridgeand the web WS↔UDP bridge pass every datagram through Exsecutor'scustosgate (compiler output,web/bridge/custos/) and admit private senders only. Tests:audio-stack/bridge/test/gate.sh,web/bridge/tests/gate.rs.dm.ctl_*sent"cmd"; the orchestrator dispatches on"op", so none of them worked. Newdm.ctl_request(returns the reply),dm.meters_read,dm.mouse_down,dm.kiosk.DEMOD_KIOSK=1(fullscreen, no cursor, no minimise on focus loss),DEMOD_FULLSCREEN.apps/mixer/, MPL-2.0). Touch-first channel strips against the orchestrator: faders with a unity detent at 0.75 and a stated dead zone, pan with centre and end detents, meters from the real shm layout.DEMOD_MIXER_ENGINE=local|remote:HOST|sim.Measured:
./dev checkpasses, all 14 gates. The mixer's end-to-end test (13 checks) runs againsttests/fake_orchestrator.py, which follows Control.hs's dispatch rules and the real meters shm layout.tests/ctl_wire.shfails 9 checks when the"op"key is reverted to"cmd", so it is not vacuous.Not measured: a real touchscreen, two fingers at once (SDL's touch-to-mouse emulation follows the first), and the real orchestrator (the fake follows its rules).
After merging: repoint ArchibaldOS's
demodinput atmainand relock before deleting this branch. Its lock usesref=ccr-08f057a2-l4wyo1, and that fetch fails once the branch is gone.Checklist
git commit -s). Not done: a sign-off certifies the DCO in your name, so I left it to you (git rebase --signoff main, then force-push this branch).SPDX-License-Identifierheader: MPL-2.0 forapps/mixer/andtests/, LGPL-3.0-only for the bridges (audio-stack/bridge/**is LGPL perLICENSING.md). One exception:web/bridge/custos/custos.gen.c, see the last item.makeandmake testpass (./dev check), and the DCF loopbacks run clean.custos.gen.cis Exsecutor compiler output, committed so the bridges build without the compiler. It ships under LGPL-3.0-only on the reading that Exsecutor'sLICENSE.EXCEPTIONException A covers compiler output (PROVENANCE.md,LICENSING.md).regen.shre-emits it and compares byte for byte, which is why it carries no header. No Exsecutor source is copied.🤖 Generated with Claude Code
https://claude.ai/code/session_01YEapehGd6iN1d5vu53PEN7
Generated by Claude Code