4: Socket monitoring

Because every connection passes through your hooks, you can see and manage sockets with ordinary Phlo models, no special tooling required.

4.1: Presence

Track who is connected by writing a row in wsConnect and deleting it in wsClose:

function wsConnect($wsHost, $wsToken, $wsSocket){
	type_presence::create(socket: $wsSocket, token: substr((string)$wsToken, 0, 8), since: time())
	return true
}
function wsClose($wsHost, $wsToken, $wsSocket){
	type_presence::delete('socket=?', $wsSocket)
}

The live count is then just count(type_presence::records()), which you can broadcast to everyone on each change.

4.2: A connect/disconnect feed

Append every lifecycle event to a log model and you have an audit trail and a live feed. The demo's /monitor page renders the log and updates the online count and the feed through wsCast as sockets come and go.

Want Use
Online count count(type_presence::records())
Per-user sockets filter presence by token
Event history a wslog model, newest first
Targeted push wsCast(wsTarget: $socketOrToken, ...)

4.3: Health

A GET /health on the daemon (e.g. 127.0.0.1:3001) reports both the connection side and the worker pools in one place, since they are one process:

{
	"status": "ok",
	"workers": 5,
	"cap": 7,
	"pools": {
		"/var/www/app/release/www/app.php": { "workers": 4, "busy": 2, "queued": 0 }
	},
	"sockets": {
		"app.example.com": {
			"tokens": 5,
			"sockets": 12,
			"perToken": { "9c11922f8c7fff2d...": 2 }
		}
	},
	"registered": ["app.example.com", "dev.example.com"]
}

sockets counts the live connections per host (tokens and sockets); pools is the per-app worker stats, where busy is the workers currently handling an event and queued the events waiting for a free worker; workers/cap is the live total against the ceiling. The pool grows itself toward the cap and reaps idle workers, so a busy that sits at the cap with a growing queued means the node is at capacity.

Presence

perToken breaks the count down per connected token, which is what you need to answer "is that user connected", as opposed to how many are. The keys are sha256 of the token, never the token itself: the endpoint is readable by any process on the box and a token is a credential. Your app knows its own tokens, so it hashes each one and looks it up.

Read presence here rather than counting it yourself in the hooks. A tally kept in your own database cannot stay right: a hard restart of the daemon never fires the closes, and the late closes of the old connections then cancel out the reconnects, so live clients end up looking offline. The daemon holds the sockets, so it is the only source that cannot drift. Store the connect and close moments if you want a timeline, and ask the daemon for the current state.

4.4: Limitations

Phlo Realtime is the daemon's built-in WebSocket layer: one process owns the sockets and runs the PHP. In one-shot mode (a build: true app) the daemon boots a handler per event, so there is a small per-event cost compared to a process that holds state in memory; run the host as a release build and the daemon serves it from its resident pool, removing that cost and scaling throughput. Either way you keep the same stateless model as the rest of Phlo and no extra language. For very high-frequency streaming (many events per second per socket) a dedicated streaming server may still fit better.

Last updated on 23 August 2026

We use essential cookies to make this site work. With your permission we also use analytics to improve the site.