Documentation Index
Fetch the complete documentation index at: https://mintlify.com/dyoburon/jarvis/llms.txt
Use this file to discover all available pages before exploring further.
The presence system tracks which Jarvis users are online, their current status, and activity. It connects to Supabase Realtime via Phoenix Channels and maintains real-time user state across all connected instances.
Architecture
PresenceClient (Rust)
|
+-- RealtimeClient (WebSocket)
| |
| +-- wss://<project>.supabase.co/realtime/v1/websocket
|
+-- event_translator task
|
+-- PresenceEvent stream --> UI
The Rust-side PresenceClient (jarvis-rs/crates/jarvis-social/src/presence/) handles all communication with Supabase Realtime. A background event translator task converts low-level WebSocket events into high-level presence events consumed by the UI.
Source files:
jarvis-rs/crates/jarvis-social/src/presence/client.rs
jarvis-rs/crates/jarvis-social/src/presence/types.rs
presence/client.py (Python implementation)
Connection Flow
Initialize Client
Create a PresenceClient with your identity and configuration:let config = PresenceConfig {
project_ref: "your-project-ref".to_string(),
api_key: "your-anon-key".to_string(),
access_token: None,
heartbeat_interval: 25,
reconnect_delay: 1,
max_reconnect_delay: 30,
};
let identity = Identity {
user_id: uuid::Uuid::new_v4().to_string(),
display_name: "alice".to_string(),
access_token: None,
};
let mut client = PresenceClient::new(identity, config);
Connect and Subscribe
Start the connection and receive presence events:let mut event_rx = client.start();
// Process events in your main loop
while let Some(event) = event_rx.recv().await {
match event {
PresenceEvent::Connected { online_count } => {
println!("Connected! {} users online", online_count);
}
PresenceEvent::UserOnline(user) => {
println!("{} is now online", user.display_name);
}
// ... handle other events
}
}
Track Your Presence
The client automatically tracks your presence on the jarvis-presence channel with this payload:{
"user_id": "<uuid>",
"display_name": "alice",
"status": "online",
"activity": null,
"online_at": "2026-03-05T12:00:00Z"
}
User Status Values
The presence system supports six status values:
pub enum UserStatus {
Online, // Default - user is active
Idle, // User is away from keyboard
InGame, // User is playing a game
InSkill, // User is running a skill
DoNotDisturb, // User doesn't want notifications
Away, // User is away
}
Presence Events
| Event | Description | Payload |
|---|
Connected | Channel joined successfully | online_count: u32 |
Disconnected | Connection lost | (none) |
UserOnline | Another user joined | OnlineUser struct |
UserOffline | Another user left | user_id, display_name |
ActivityChanged | User’s status or activity changed | OnlineUser struct |
GameInvite | User broadcast a game invitation | user_id, display_name, game, code |
Poked | Someone poked this user | user_id, display_name |
ChatMessage | Chat message on presence channel | user_id, display_name, channel, content |
Error | Connection or protocol error | String (error message) |
Protocol Messages
Activity Update
Broadcast when a user’s status or activity changes:
{
"type": "activity_update",
"user_id": "<uuid>",
"display_name": "alice",
"status": "in_game",
"activity": "Playing Snake"
}
Game Invite
Broadcast a game invitation to all online users:
{
"type": "game_invite",
"user_id": "<uuid>",
"display_name": "alice",
"game": "snake",
"code": "ROOM123"
}
Poke
Send a targeted notification to a specific user:
{
"type": "poke",
"user_id": "<sender-uuid>",
"display_name": "alice",
"target_user_id": "<recipient-uuid>"
}
Python Client
Jarvis also includes a Python presence client (presence/client.py) with similar functionality:
from presence.client import PresenceClient
client = PresenceClient(
server_url="wss://project.supabase.co/realtime/v1/websocket",
user_id="alice-123",
display_name="alice"
)
# Set callback for notifications
def handle_notification(event_type, data):
if event_type == "user_online":
print(f"{data['display_name']} is online")
elif event_type == "poke":
print(f"Poked by {data['display_name']}!")
client.on_notification = handle_notification
# Run the client
await client.run()
Python Protocol Messages
Connect:
await ws.send(json.dumps({
"type": "connect",
"user_id": "alice-123",
"display_name": "alice",
"version": "1"
}))
Heartbeat:
# Sent every 30 seconds
await ws.send(json.dumps({"type": "ping"}))
Response:
{"type": "pong", "online_count": 5}
Heartbeat and Reconnection
The Supabase Realtime connection uses Phoenix heartbeat messages to maintain the connection.
Configuration
| Parameter | Default | Description |
|---|
heartbeat_interval | 25s | How often to send heartbeat messages |
reconnect_delay | 1s | Base delay before reconnecting |
max_reconnect_delay | 30s | Maximum reconnection delay |
Reconnection Strategy
The client uses exponential backoff for reconnection:
delay = min(delay * 2, max_reconnect_delay)
On reconnect, the client automatically:
- Re-joins the
jarvis-presence channel
- Re-tracks presence with current status
- Receives full presence state from the server
OnlineUser Structure
pub struct OnlineUser {
pub user_id: String,
pub display_name: String,
pub status: UserStatus,
pub activity: Option<String>,
pub online_at: String,
}
Integration Example
use jarvis_social::presence::{PresenceClient, PresenceConfig, PresenceEvent};
use jarvis_social::identity::Identity;
use jarvis_social::protocol::UserStatus;
#[tokio::main]
async fn main() {
// Create identity
let identity = Identity {
user_id: uuid::Uuid::new_v4().to_string(),
display_name: std::env::var("USER").unwrap_or("jarvis".to_string()),
access_token: None,
};
// Configure presence
let config = PresenceConfig {
project_ref: "your-project".to_string(),
api_key: "your-key".to_string(),
..Default::default()
};
// Start client
let mut client = PresenceClient::new(identity, config);
let mut events = client.start();
// Process events
tokio::spawn(async move {
while let Some(event) = events.recv().await {
match event {
PresenceEvent::Connected { online_count } => {
println!("🟢 Connected ({} online)", online_count);
}
PresenceEvent::UserOnline(user) => {
println!("👋 {} joined", user.display_name);
}
PresenceEvent::Poked { display_name, .. } => {
println!("👉 Poked by {}!", display_name);
// Show desktop notification
}
_ => {}
}
}
});
// Update activity
client.update_activity(
UserStatus::InSkill,
Some("Running a terminal command".to_string())
).await;
// Send a poke
client.send_poke("another-user-id").await;
}
Configuration Reference
From jarvis-rs/crates/jarvis-config/src/schema/social.rs:
[presence]
enabled = true
server_url = "" # Supabase project ref
heartbeat_interval = 30 # seconds
Channel Name
All presence events use the channel name jarvis-presence. This is a constant defined in client.rs:23.