hecate_pubsub_server (macula v10.9.0)

View Source

PubSub gen_server wrapping hecate_pubsub state for one realm namespace.

Activates the dormant hecate_pubsub pure-state module by giving it a process identity. One server instance owns the topic-to-subscriber index for a single realm tag; the realm is an opaque 32-byte namespace key, not validated against any authority. Multi-tenancy comes from running multiple servers under different realm tags — the station does not arbitrate which realm tags are "real" (Sprint A: realm identity lives outside infrastructure).

Phase 1 scope (this commit)

State mutations + frame processing only. The publish path builds the signed EVENT frame and returns the matched LOCAL subscribers, but does NOT fan out across the cluster — that requires the Plumtree wire layer (hecate_plumtree) and the DHT topic-discovery integration which land in subsequent commits.

Sequencing

  • This commit: server in isolation, no integration with station listener or DHT.
  • Next: per-realm-namespace registry under hecate_overlay_sup so the listener can route inbound SUBSCRIBE / UNSUBSCRIBE / EVENT frames to the right server.
  • Then: Plumtree fan-out for cross-station delivery.
  • Then: DHT integration for topic-mesh discovery.

Summary

Functions

Process an inbound EVENT frame received from the wire. Returns the matched local subscribers; the caller delivers.

Generic frame dispatch — handles subscribe / unsubscribe / event uniformly. Returns the matched subscribers for event frames, empty list for subscribe / unsubscribe.

Build a signed EVENT frame for Topic/Payload and return it together with the set of LOCAL subscribers that match. The caller is responsible for handing the frame to the cross-station delivery layer (Plumtree, future commit) and for delivering to the matched local subscribers via the application channel.

Relay an inbound PUBLISH frame from a remote daemon. The server builds an EVENT frame signed by THIS server's identity (intermediate hop re-signing — Phase 1 simplification; Phase 2 tightens to publisher-end-to-end auth via UCAN), preserves the original publisher pubkey + seq inside the EVENT, and returns the matched local subscribers. The caller (typically the peer observer) is responsible for sending EventFrame on each subscriber's peering connection.

Types

opts/0

-type opts() :: #{realm := <<_:256>>, identity := macula_identity:key_pair()}.

Functions

deliver_event(Pid, Frame)

-spec deliver_event(pid(), macula_frame:frame()) -> [<<_:256>>].

Process an inbound EVENT frame received from the wire. Returns the matched local subscribers; the caller delivers.

handle_call(Request, From, S)

handle_cast(Msg, S)

handle_info(Info, S)

init(_)

is_subscribed(Pid, Topic, Sub)

-spec is_subscribed(pid(), binary(), <<_:256>>) -> boolean().

process_frame(Pid, From, Frame)

-spec process_frame(pid(), <<_:256>>, macula_frame:frame()) -> [<<_:256>>].

Generic frame dispatch — handles subscribe / unsubscribe / event uniformly. Returns the matched subscribers for event frames, empty list for subscribe / unsubscribe.

publish(Pid, Topic, Payload)

-spec publish(pid(), binary(), binary()) -> {macula_frame:frame(), [<<_:256>>]}.

Build a signed EVENT frame for Topic/Payload and return it together with the set of LOCAL subscribers that match. The caller is responsible for handing the frame to the cross-station delivery layer (Plumtree, future commit) and for delivering to the matched local subscribers via the application channel.

realm(Pid)

-spec realm(pid()) -> <<_:256>>.

relay_publish(Pid, Frame)

-spec relay_publish(pid(), macula_frame:frame()) ->
                       {macula_frame:frame(), [<<_:256>>]} | {error, realm_mismatch}.

Relay an inbound PUBLISH frame from a remote daemon. The server builds an EVENT frame signed by THIS server's identity (intermediate hop re-signing — Phase 1 simplification; Phase 2 tightens to publisher-end-to-end auth via UCAN), preserves the original publisher pubkey + seq inside the EVENT, and returns the matched local subscribers. The caller (typically the peer observer) is responsible for sending EventFrame on each subscriber's peering connection.

Returns {error, realm_mismatch} when the publish frame's realm does not match this server's realm. The registry routes by realm so this should never fire in practice — defensive check.

start_link(Opts)

-spec start_link(opts()) -> {ok, pid()} | {error, term()}.

stop(Pid)

-spec stop(pid()) -> ok.

subscribe(Pid, Topic, Sub)

-spec subscribe(pid(), binary(), <<_:256>>) -> ok.

subscriber_count(Pid)

-spec subscriber_count(pid()) -> non_neg_integer().

subscribers(Pid, Topic)

-spec subscribers(pid(), binary()) -> [<<_:256>>].

terminate(Reason, State)

topic_count(Pid)

-spec topic_count(pid()) -> non_neg_integer().

topics(Pid)

-spec topics(pid()) -> [binary()].

unsubscribe(Pid, Topic, Sub)

-spec unsubscribe(pid(), binary(), <<_:256>>) -> ok.