@6digit/satellite-convex
v0.3.2
Published
Server-side Convex integration for 6digit-satellite with transparent tool proxying and session management
Maintainers
Readme
@6digit/satellite-convex
Server-side Convex integration for 6digit-satellite with transparent tool proxying and session management.
Overview
The 6digit Satellite Convex package provides server-side utilities for managing satellite connections, pairing, tool invocations, and session persistence in Convex backends. It handles the server-side logic that coordinates with @6digit/satellite-core clients.
Features
- 🔗 Satellite Registration - Register satellites and generate pairing codes
- 🔑 Pairing Management - Handle satellite pairing with expiring codes
- 📡 Tool Invocation Routing - Route tool calls to connected satellites
- 💓 Session Management - Track satellite connections and heartbeats
- 🆔 Persistent Identity - Maintain satellite relationships across restarts
- 🔍 Status Monitoring - Query satellite availability and status
Installation
npm install @6digit/satellite-convexQuick Start
import { SatelliteManager } from '@6digit/satellite-convex';
// In your Convex mutation
export const registerSatellite = mutation({
args: {
satelliteId: v.string(),
toolDefinitions: v.array(v.any()),
metadata: v.optional(v.any())
},
handler: async (ctx, args) => {
return await SatelliteManager.registerSatellite(
ctx,
args.satelliteId,
args.toolDefinitions,
args.metadata
);
}
});
// Check if satellite is paired
export const isSatellitePaired = query({
args: { satelliteId: v.string() },
handler: async (ctx, args) => {
return await SatelliteManager.isSatellitePaired(ctx, args.satelliteId);
}
});Core Components
SatelliteManager
Main class for managing satellite lifecycle:
class SatelliteManager {
// Register satellite and return pairing code (or null if already paired)
static async registerSatellite(ctx, satelliteId, toolDefinitions, metadata?)
// Check if satellite is paired
static async isSatellitePaired(ctx, satelliteId)
// Get satellite status
static async getSatelliteStatus(ctx, satelliteId)
// Update satellite heartbeat
static async updateHeartbeat(ctx, satelliteId)
// Disconnect satellite
static async disconnectSatellite(ctx, satelliteId)
}Pairing System
Secure pairing with expiring codes:
// Generate human-readable pairing codes
const pairingCode = createPairingCode(satelliteId);
// Validate pairing codes
const isValid = validatePairingCode(code, satelliteId);
// Check expiration
const isExpired = isPairingCodeExpired(pairingCode);Database Schema
The package expects these Convex tables:
// satellites table
{
satelliteId: string,
toolDefinitions: any[],
status: "waiting_for_pair" | "paired" | "connected" | "disconnected",
registeredAt: number,
lastRegisteredAt?: number,
type?: string,
name?: string,
workingDirectory?: string,
platform?: string,
nodeVersion?: string
}
// satelliteSessions table
{
satelliteId: string,
pairingCode: string,
connectedAt: number,
lastSeen: number,
status: "connected" | "disconnected",
name?: string,
toolDefinitions: any[]
}
// pairingCodes table
{
code: string,
satelliteId: string,
createdAt: number,
expiresAt: number,
used: boolean
}
// satelliteToolInvocations table
{
satelliteId: string,
toolName: string,
parameters: any,
status: "pending" | "completed" | "failed",
createdAt: number,
completedAt?: number,
result?: any,
error?: string
}Example Convex Functions
// mutations/satellite.ts
import { mutation, query } from "./_generated/server";
import { v } from "convex/values";
import { SatelliteManager } from "@6digit/satellite-convex";
export const registerSatellite = mutation({
args: {
satelliteId: v.string(),
toolDefinitions: v.array(v.any()),
metadata: v.optional(v.any())
},
handler: async (ctx, args) => {
return await SatelliteManager.registerSatellite(
ctx,
args.satelliteId,
args.toolDefinitions,
args.metadata
);
}
});
export const updateHeartbeat = mutation({
args: { satelliteId: v.string() },
handler: async (ctx, args) => {
await SatelliteManager.updateHeartbeat(ctx, args.satelliteId);
}
});
export const getPendingToolInvocations = query({
args: { satelliteId: v.string() },
handler: async (ctx, args) => {
return await ctx.db
.query("satelliteToolInvocations")
.withIndex("by_satellite_id", (q) => q.eq("satelliteId", args.satelliteId))
.filter((q) => q.eq(q.field("status"), "pending"))
.collect();
}
});
export const sendToolResult = mutation({
args: {
invocationId: v.string(),
result: v.optional(v.any()),
error: v.optional(v.string())
},
handler: async (ctx, args) => {
await ctx.db.patch(args.invocationId, {
status: args.error ? "failed" : "completed",
result: args.result,
error: args.error,
completedAt: Date.now()
});
}
});Pairing Flow
- Satellite Registration: Satellite calls
registerSatellitewith tools - Pairing Code Generation: Server generates human-readable code (e.g., "BLUE-MOON-42")
- User Pairing: User enters code in 6digit Studio interface
- Session Creation: Server creates persistent session
- Automatic Reconnection: Subsequent registrations return
null(already paired)
Session Management
- Heartbeats: Satellites send heartbeats every 30 seconds
- Status Tracking: Server tracks
lastSeentimestamps - Automatic Cleanup: Expired sessions can be cleaned up
- Reconnection: Paired satellites automatically reconnect
License
MIT
Related Packages
@6digit/satellite-core- Client-side satellite framework
Contributing
See CONTRIBUTING.md for development guidelines.
Support
- 🌐 Website: 6digit.studio
- 📧 Issues: GitHub Issues
