Short answer: Use a server-issued, short-lived display capability per participant, renew presence with a lease, and reject publish requests whose session epoch or scope no longer matches.
A screen-sharing session in a healthtech workspace should fail closed: a participant can publish only the track the server granted, and presence is derived from short-lived leases rather than assumed from an open socket. That combination keeps a clinician who loses authorization from publishing a sensitive display, while still letting the workspace show who is online. The practical design is a server-issued capability, a small state machine, and an alert on stale presence instead of an alert on every reconnect.
The page arrives before the symptom
The on-call page is usually phrased as "workspace presence below 92%". In the incident view, several clinicians appear offline while their browser connections are healthy. The first instinct is to blame WebRTC negotiation. I start elsewhere: compare the last presence lease, the authorization decision, and the publish state for the same participant.
A presence record should carry a participant id, workspace id, session id, last-seen time, and an authorization epoch. The client renews the lease on a short interval, and the server expires it without requiring a clean disconnect. A tab crash then becomes an ordinary timeout. A reconnect creates a new session id, so an old tab cannot keep a participant marked online.
The earlier signal is not a count of connected sockets. It is the ratio of leases renewed within the expiry window, split by workspace and browser version. Alerting on that ratio catches a broken renewal path before a support ticket reports missing people. Keep the threshold wide enough for mobile suspend and brief network loss; false positives train the on-call to ignore the page.
That distinction matters.
How should a screen sharing session API scope each participant?
Treat screen publishing as a capability, not a property of the user interface. The authorization service evaluates workspace membership, role, consent state, and the requested source, then returns a signed grant with an expiry and an epoch. The media gateway checks that grant when the publish request arrives and again when the grant is refreshed. A button being visible is never the enforcement point.
For a shared workspace, I use separate capabilities for camera, microphone, and display. Display is narrower: its scope names the workspace and session, its action is publish, and its expiry is short enough that a revoked participant cannot continue indefinitely. The participant may subscribe to others without receiving publish rights. That asymmetry is useful in a clinical review where observers need to watch but must not share a patient chart.
A minimal grant payload can be represented like this:
type PublishGrant struct {
ParticipantID string `json:"participant_id"`
WorkspaceID string `json:"workspace_id"`
SessionID string `json:"session_id"`
Source string `json:"source"`
Action string `json:"action"`
Epoch uint64 `json:"epoch"`
ExpiresAt int64 `json:"expires_at"`
}
The gateway should reject a mismatched workspace, source, epoch, or expiry with the same generic authorization result. Detailed reasons belong in protected logs, not in a browser response that could help an attacker enumerate policy.
What does a reconnect prove?
Nothing by itself. A WebSocket reconnect proves transport continuity, not identity freshness or publish permission. On reconnect, the client presents its session credential, obtains a new presence lease, and asks for capabilities again. The server increments the session epoch when membership or consent changes. Any grant carrying an older epoch is invalid, even if its timestamp has not expired.
The race to test is revoke versus publish. Store the epoch and grant decision in the same authorization boundary used by the publish handler. If the decision changes between a UI check and the media request, the media request loses. This is the small detail that prevents a revoked screen from surviving because a browser clicked at the wrong millisecond.
I also keep a bounded idempotency key on publish attempts. Retries after a 502 or a dropped response must not create two display tracks. That identifier is scoped to session and source, expires with the grant, and is recorded with the resulting track id. Duplicate delivery is a normal network outcome, not an exceptional branch.
Instrumentation that makes the failure actionable
Emit one structured event for each grant decision and one for each lease transition. Useful fields are workspace hash, session id, participant id hash, source, epoch, decision, and latency bucket. Do not put names, chart contents, or raw tokens in these events.
The dashboard needs three views: expired leases by client build, denied publish attempts by policy reason, and grants accepted after a reconnect. A rising denied count with stable lease renewals points to policy or epoch churn. A rising expired count with normal authorization latency points to the client timer, browser suspension, or the renewal route. Those are different runbooks, so they should not share one alert.
For tests, inject clock skew, suspend the renewal timer, revoke consent during negotiation, and deliver the same publish request twice. Verify that subscriptions continue when publishing is denied, that an expired lease removes presence, and that an old epoch cannot publish. Exercise the browser path with real RTCPeerConnection objects because a mock will not reveal negotiation timing. The WebRTC API exposes the peer connection and sender lifecycle; your policy layer still owns the decision.
Page on missing renewals only after a grace interval that covers the expected reconnect budget. Page separately on an unusual rise in denied display publishes. During an incident, operators can then distinguish a presence freshness problem from an authorization problem without opening patient data.
The design choice is deliberate. The limitation is operational: short capabilities and renewable presence add coordination and timer edge cases, so this approach is a poor fit for a toy room with no sensitive data or revocation requirement. In that case, a simpler room token can be easier to operate. For a clinical workspace, the extra state makes revocation, auditing, and recovery explicit. Keep the media protocol standards-based, keep policy at the server boundary, and make every retry idempotent. That is enough to show who is online while ensuring that being online never implies permission to publish a screen. It failed closed. That is the point.
Top comments (0)