OverviewFilesCommitsIssuesDocsReleasesPeers

hyperswarm-peer / daemon / session_daemon.h

97 lines · c ref:
// A session channel, exposed to a local process over a Unix socket.
//
// blender-bridge/docs/P2P_BRIDGE_SPEC.md §4, §6.2.
//
//   local app  <--UDS, length-prefixed-->  daemon  <--Protomux/swarm-->  peers
//
// ⛔ IT MOVES FRAMES AND HOLDS NO LOGIC. It never parses a frame. Grab
// arbitration lives in blender-bridge/session/grab.py with its own gates; a
// second implementation here would be another place for the rule to drift.
//
// ⛔ NOT BLENDER-SPECIFIC ON PURPOSE. "Expose a Protomux channel over a swarm
// topic to a local socket" has nothing to do with Blender, and it lives beside
// the transport because that is where its #includes are — the rule liboverlay
// states for itself ("no #include reaches outside liboverlay/") and the reason a
// cross-repo include would turn extraction into a refactor.

#ifndef HYPERSWARM_SESSION_DAEMON_H
#define HYPERSWARM_SESSION_DAEMON_H

#include <cstdint>
#include <functional>
#include <map>
#include <mutex>
#include <string>
#include <vector>

#include "protomux.h"
#include "udx_transport.h"

namespace session {

// The protocol name carries its version, as keet-call-presence/v3 does: a peer on
// another version never pairs, and no message negotiates one.
extern const char *PROTOCOL;

class SessionDaemon {
public:
	SessionDaemon();
	~SessionDaemon();

	// topic: 32 bytes. Every peer in a session joins the same one.
	bool start(const uint8_t *topic, const char *bootstrap, const char *socket_path);
	void stop();

	// Accept/serve local clients and pump the swarm. Call in a loop.
	void poll(int timeout_ms);

	int peer_count();
	int local_client_count();

	// ⛔ THE DAEMON WAS UNOBSERVABLE AND THAT IS HOW A DEAD DHT LOOKED HEALTHY.
	// Both peers reported joined "<topic>" while neither had ever sent a packet:
	// the socket bound, the local client attached, the UI went green. Nothing in
	// the process could say whether the swarm existed. These two are the
	// difference between "joined" (a local fact) and "reachable" (the one that
	// matters).
	bool dht_active() { return _t.is_dht_active(); }
	bool dht_bootstrapped() { return _t.is_dht_bootstrapped(); }

	// Diagnostics only — the daemon does not interpret frames.
	uint64_t frames_to_peers() { return _to_peers; }
	uint64_t frames_to_local() { return _to_local; }

private:
	void _on_mux_ready(int32_t id, Protomux *mux);
	void _on_mux_gone(int32_t id);
	void _broadcast_to_peers(const uint8_t *data, size_t len);
	void _broadcast_to_local(const uint8_t *data, size_t len, int except_fd);
	void _accept_local();
	void _read_local(int fd);
	void _drop_local(int fd);

	UdxTransport _t;
	std::vector<uint8_t> _topic;
	std::string _socket_path;
	int _listen_fd = -1;

	std::mutex _m;
	std::map<int32_t, ProtomuxChannel *> _channels;

	// Local clients, each with its own read buffer: a stream socket coalesces
	// writes, so a frame boundary only exists because the length prefix says so.
	struct Local {
		std::vector<uint8_t> buf;
	};
	std::map<int, Local> _locals;

	uint64_t _to_peers = 0;
	uint64_t _to_local = 0;
	bool _started = false;
	bool _joined = false;   // the topic join is DEFERRED — see poll()
};

} // namespace session

#endif