Releases: macula-io/macula
Releases · macula-io/macula
v3.13.0
v3.12.1
fix(station_link): gate {call, ...} on peer_node_id (matches publish)
Release 3.12.1.
The {call, ...} gen_server clause was gated on peer_pid, set the moment
macula_peering:connect/1 returns — before the peering worker has finished
the CONNECT/HELLO handshake. publish/4 was correctly gated on peer_node_id
(set by the {macula_peering, connected, ...} notification after HELLO).
Race: a caller (e.g. a freshly-spawned daemon stub) issues put_record/3
immediately after start_link/1. The link forwards the call frame via
gen_statem:cast(PeerPid, {send_frame, Frame}) while the peering worker
is still in handshaking. handshaking has no clause for cast({send_frame, _}),
so the cast falls into drop_unexpected and the frame is silently dropped.
The caller's deadline timer fires and surfaces {error, timeout}, even
though the underlying QUIC connection is healthy and any subsequent call
(after the timer's wake-up) would have succeeded.
Fix: gate {call, ...} on peer_node_id to match {publish, ...}. Callers
issuing a request before the handshake completes now get
{error, not_connected} immediately, matching the SDK's documented
contract for the disconnected case. Existing call sites already handle
{error, not_connected} with a short backoff, so no consumer change is
required.
Direct evidence: handshaking peering_conn workers on production relay
boxes carried buffers stuck on V1-protocol bytes from beam-daemon
realm-join (separate problem, fixed in hecate-daemon), but V2-protocol
stub workers also exhibited timeout-then-recycle cycles on every
put_record because of this race.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
v3.11.1
fix(record_cbor): accept atoms in encode/1 to round-trip wire-decoded…
v3.11.0
chore: Release v3.11.0 — Phase 1 V2 PARITY (pool + pubsub + docs)
v3.10.3
chore: Release v3.10.3 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
v3.9.0
feat(client)!: put_record + find_record on station-client — v3.9.0
Round out macula_station_client so it owns every DHT operation a
node needs against a V2 station, not just reads:
- put_record/2,3 wraps _dht.put_record. ok / {error,_} taxonomy.
- find_record/2,3 wraps _dht.find_record. {ok,Record} or
{error, not_found}.
Closes the wire-incompat gap that left node daemons unable to
publish node_record / domain-fact records into V2-only stations:
macula_mesh_client (V1) is rejected by hecate-station's V2 peering
listener, so before this release writes silently dropped. Consumers
(hecate-daemon, future SDK clients) now have a single read+write
path through macula_station_client.
Tests: 4 new EUnit cases. macula_station_client_tests count: 10.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
v3.8.0
feat(client)!: V2 station-client for outbound RPC over peering — v3.8.0
Adds macula_station_client, a high-level gen_server wrapping macula_peering
+ macula_frame to issue CALL frames against V2 stations and match
RESULT/ERROR replies via the 16-byte call_id.
Closes a real protocol gap: V1 macula_mesh_client cannot drive a V2
station because the station dispatches the QUIC connection straight
into macula_peering:accept/2 — V1 CONNECT frames never reach the V2
handler registry. Consumers like macula-realm's topology subscriber
were silently timing out on every find_records_by_type call.
API:
- macula_station_client:start_link/1 #{seed, identity, realm, ...}
- macula_station_client:call/4 (Pid, Procedure, Payload, Timeout)
- macula_station_client:find_records_by_type/2,3
- is_connected/1, peer_node_id/1, stop/1
Tests:
- 6 new EUnit cases for seed parsing, RESULT/ERROR matching, deadline,
disconnect drain. Live QUIC handshake covered in hecate-station CT
suites which already exercise the peering layer end-to-end.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
v3.7.0
feat(peering+diagnostics): vendor macula_peering + macula_diagnostics…
v3.6.0
feat(frame): vendor macula_frame + macula_bolt4 + macula_source_route…
v3.5.0
feat(record): expose envelope/4 + subject_id for domain-defined types…