@privateclaw/privateclaw-relay 0.1.7 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.env.example CHANGED
@@ -5,6 +5,8 @@ PRIVATECLAW_FRAME_CACHE_SIZE=25
5
5
  # PRIVATECLAW_MAX_MESSAGE_BYTES=25165824
6
6
  # PRIVATECLAW_APP_MESSAGES_PER_MINUTE=120
7
7
  # PRIVATECLAW_PROVIDER_MESSAGES_PER_MINUTE=600
8
+ # Optional: enable the built-in relay admin UI/API at /admin and /api/admin/*
9
+ # PRIVATECLAW_ADMIN_TOKEN=replace-with-a-long-random-token
8
10
  # Optional: enable background wake through Firebase Cloud Messaging.
9
11
  # Use either the full service-account JSON...
10
12
  # PRIVATECLAW_FCM_SERVICE_ACCOUNT_JSON={"type":"service_account","project_id":"your-project","client_email":"...","private_key":"-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"}
package/README.md CHANGED
@@ -44,6 +44,25 @@ privateclaw-relay --host 0.0.0.0 --port 8787
44
44
  privateclaw-relay --redis-url redis://127.0.0.1:6379
45
45
  ```
46
46
 
47
+ Enable the built-in relay admin UI/API with a fixed bearer token:
48
+
49
+ ```bash
50
+ export PRIVATECLAW_ADMIN_TOKEN=replace-with-a-long-random-token
51
+ privateclaw-relay --redis-url redis://127.0.0.1:6379
52
+ ```
53
+
54
+ When `PRIVATECLAW_ADMIN_TOKEN` is configured, the relay serves a simple admin dashboard at `/admin/` and protects the JSON APIs under `/api/admin/*` with `Authorization: Bearer <token>`.
55
+
56
+ The admin dashboard shows:
57
+
58
+ - current active sessions and participants
59
+ - session history and per-session details
60
+ - participant online time and message counts
61
+ - relay request/error statistics
62
+ - live relay instance heartbeats
63
+
64
+ For multi-instance deployments, point every relay instance at the same Redis so the admin view can aggregate shared session history and instance status.
65
+
47
66
  Expose the local relay to the internet through Tailscale Funnel:
48
67
 
49
68
  ```bash
@@ -94,6 +113,7 @@ The relay still reads its runtime config from the process environment:
94
113
  - `PRIVATECLAW_RELAY_INSTANCE_ID`
95
114
  - `PRIVATECLAW_REDIS_URL`
96
115
  - `REDIS_URL`
116
+ - `PRIVATECLAW_ADMIN_TOKEN`
97
117
  - `PRIVATECLAW_FCM_SERVICE_ACCOUNT_JSON`
98
118
  - `PRIVATECLAW_FCM_PROJECT_ID`
99
119
  - `PRIVATECLAW_FCM_CLIENT_EMAIL`
@@ -0,0 +1,11 @@
1
+ import type { IncomingMessage, ServerResponse } from "node:http";
2
+ import type { RelayAdminMetricsStore } from "./admin-metrics-store.js";
3
+ export interface HandleRelayAdminRequestOptions {
4
+ request: IncomingMessage;
5
+ response: ServerResponse;
6
+ url: URL;
7
+ adminToken?: string;
8
+ metricsStore: RelayAdminMetricsStore;
9
+ now?: () => number;
10
+ }
11
+ export declare function handleRelayAdminRequest(options: HandleRelayAdminRequestOptions): Promise<boolean>;
@@ -0,0 +1,111 @@
1
+ function writeJson(response, statusCode, payload) {
2
+ response.writeHead(statusCode, { "content-type": "application/json" });
3
+ response.end(JSON.stringify(payload));
4
+ }
5
+ function normalizeBearerToken(headerValue) {
6
+ if (!headerValue) {
7
+ return undefined;
8
+ }
9
+ const match = headerValue.match(/^Bearer\s+(.+)$/i);
10
+ if (!match) {
11
+ return undefined;
12
+ }
13
+ return match[1]?.trim() || undefined;
14
+ }
15
+ function isAuthorized(request, expectedToken) {
16
+ const headerValue = request.headers.authorization;
17
+ if (typeof headerValue !== "string") {
18
+ return false;
19
+ }
20
+ return normalizeBearerToken(headerValue) === expectedToken;
21
+ }
22
+ function parsePositiveInteger(rawValue, fallback) {
23
+ if (!rawValue) {
24
+ return fallback;
25
+ }
26
+ const parsed = Number.parseInt(rawValue, 10);
27
+ if (!Number.isInteger(parsed) || parsed <= 0) {
28
+ return fallback;
29
+ }
30
+ return parsed;
31
+ }
32
+ function parseStatus(rawValue) {
33
+ if (!rawValue || rawValue === "all") {
34
+ return "all";
35
+ }
36
+ if (rawValue === "active" ||
37
+ rawValue === "closed" ||
38
+ rawValue === "expired") {
39
+ return rawValue;
40
+ }
41
+ return "all";
42
+ }
43
+ export async function handleRelayAdminRequest(options) {
44
+ if (!options.url.pathname.startsWith("/api/admin")) {
45
+ return false;
46
+ }
47
+ if (!options.adminToken) {
48
+ writeJson(options.response, 404, { error: "not_found" });
49
+ return true;
50
+ }
51
+ if (options.request.method !== "GET") {
52
+ options.response.writeHead(405, {
53
+ "content-type": "application/json",
54
+ allow: "GET",
55
+ });
56
+ options.response.end(JSON.stringify({ error: "method_not_allowed" }));
57
+ return true;
58
+ }
59
+ if (!isAuthorized(options.request, options.adminToken)) {
60
+ options.response.writeHead(401, {
61
+ "content-type": "application/json",
62
+ "www-authenticate": 'Bearer realm="PrivateClaw Relay Admin"',
63
+ });
64
+ options.response.end(JSON.stringify({ error: "unauthorized" }));
65
+ return true;
66
+ }
67
+ const now = options.now?.() ?? Date.now();
68
+ if (options.url.pathname === "/api/admin" ||
69
+ options.url.pathname === "/api/admin/overview") {
70
+ writeJson(options.response, 200, await options.metricsStore.getOverview(now));
71
+ return true;
72
+ }
73
+ if (options.url.pathname === "/api/admin/sessions") {
74
+ const page = parsePositiveInteger(options.url.searchParams.get("page"), 1);
75
+ const pageSize = parsePositiveInteger(options.url.searchParams.get("pageSize"), 50);
76
+ const query = options.url.searchParams.get("query") ?? undefined;
77
+ const status = parseStatus(options.url.searchParams.get("status"));
78
+ writeJson(options.response, 200, await options.metricsStore.listSessions({
79
+ page,
80
+ pageSize,
81
+ ...(query ? { query } : {}),
82
+ ...(status ? { status } : {}),
83
+ now,
84
+ }));
85
+ return true;
86
+ }
87
+ if (options.url.pathname.startsWith("/api/admin/sessions/")) {
88
+ const sessionId = decodeURIComponent(options.url.pathname.slice("/api/admin/sessions/".length));
89
+ if (!sessionId) {
90
+ writeJson(options.response, 404, { error: "not_found" });
91
+ return true;
92
+ }
93
+ const detail = await options.metricsStore.getSessionDetail(sessionId, now);
94
+ if (!detail) {
95
+ writeJson(options.response, 404, { error: "unknown_session" });
96
+ return true;
97
+ }
98
+ writeJson(options.response, 200, detail);
99
+ return true;
100
+ }
101
+ if (options.url.pathname === "/api/admin/instances") {
102
+ writeJson(options.response, 200, {
103
+ generatedAt: now,
104
+ instances: await options.metricsStore.listInstances(now),
105
+ });
106
+ return true;
107
+ }
108
+ writeJson(options.response, 404, { error: "not_found" });
109
+ return true;
110
+ }
111
+ //# sourceMappingURL=admin-api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-api.js","sourceRoot":"","sources":["../src/admin-api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,245 @@
1
+ import type { RelaySessionRecord } from "./session-store.js";
2
+ export type RelayAdminSessionStatus = "active" | "closed" | "expired";
3
+ export type RelayAdminRequestActor = "app" | "provider";
4
+ export interface RelayAdminRequestTypeCount {
5
+ actor: RelayAdminRequestActor;
6
+ type: string;
7
+ ok: number;
8
+ error: number;
9
+ }
10
+ export interface RelayAdminErrorCodeCount {
11
+ code: string;
12
+ count: number;
13
+ }
14
+ export interface RelayAdminRequestStats {
15
+ appRequests: number;
16
+ providerRequests: number;
17
+ appSuccesses: number;
18
+ providerSuccesses: number;
19
+ appErrors: number;
20
+ providerErrors: number;
21
+ appFrames: number;
22
+ providerFrames: number;
23
+ errorCodes: RelayAdminErrorCodeCount[];
24
+ requestTypes: RelayAdminRequestTypeCount[];
25
+ }
26
+ export interface RelayAdminOverviewTotals {
27
+ sessions: number;
28
+ activeSessions: number;
29
+ closedSessions: number;
30
+ expiredSessions: number;
31
+ knownParticipants: number;
32
+ activeParticipants: number;
33
+ instances: number;
34
+ }
35
+ export interface RelayAdminOverview {
36
+ generatedAt: number;
37
+ totals: RelayAdminOverviewTotals;
38
+ requestStats: RelayAdminRequestStats;
39
+ }
40
+ export interface RelayAdminSessionSummary {
41
+ sessionId: string;
42
+ providerId: string;
43
+ groupMode: boolean;
44
+ createdAt: number;
45
+ updatedAt: number;
46
+ expiresAt: number;
47
+ closedAt?: number;
48
+ closeReason?: string;
49
+ status: RelayAdminSessionStatus;
50
+ appMessageCount: number;
51
+ providerMessageCount: number;
52
+ distinctParticipantCount: number;
53
+ activeParticipantCount: number;
54
+ providerOnline: boolean;
55
+ lastAppMessageAt?: number;
56
+ lastProviderMessageAt?: number;
57
+ }
58
+ export interface RelayAdminSessionParticipant {
59
+ appId: string;
60
+ firstSeenAt: number;
61
+ lastSeenAt: number;
62
+ lastConnectedAt?: number;
63
+ lastDisconnectedAt?: number;
64
+ lastDisconnectReason?: string;
65
+ connectionCount: number;
66
+ messageCount: number;
67
+ totalConnectedMs: number;
68
+ currentConnectedMs: number;
69
+ isOnline: boolean;
70
+ }
71
+ export interface RelayAdminSessionDetail {
72
+ session: RelayAdminSessionSummary;
73
+ participants: RelayAdminSessionParticipant[];
74
+ }
75
+ export interface RelayAdminSessionListOptions {
76
+ status?: RelayAdminSessionStatus | "all";
77
+ page?: number;
78
+ pageSize?: number;
79
+ query?: string;
80
+ now?: number;
81
+ }
82
+ export interface RelayAdminSessionListResult {
83
+ total: number;
84
+ page: number;
85
+ pageSize: number;
86
+ sessions: RelayAdminSessionSummary[];
87
+ }
88
+ export interface RelayAdminInstanceBinding {
89
+ sessionId: string;
90
+ appId: string;
91
+ }
92
+ export interface RelayAdminInstanceStatus {
93
+ instanceId: string;
94
+ startedAt: number;
95
+ lastSeenAt: number;
96
+ activeProviders: number;
97
+ activeApps: number;
98
+ localSessions: number;
99
+ providerIds: string[];
100
+ sessionIds: string[];
101
+ participantBindings: RelayAdminInstanceBinding[];
102
+ memoryUsage: {
103
+ rss: number;
104
+ heapTotal: number;
105
+ heapUsed: number;
106
+ external: number;
107
+ arrayBuffers: number;
108
+ };
109
+ }
110
+ export interface RelayAdminLocalSnapshot {
111
+ activeProviders: number;
112
+ activeApps: number;
113
+ localSessions: number;
114
+ providerIds: string[];
115
+ sessionIds: string[];
116
+ participantBindings: RelayAdminInstanceBinding[];
117
+ memoryUsage: NodeJS.MemoryUsage;
118
+ }
119
+ export interface RelayAdminInstanceHeartbeat {
120
+ instanceId: string;
121
+ startedAt: number;
122
+ recordedAt: number;
123
+ snapshot: RelayAdminLocalSnapshot;
124
+ }
125
+ export interface RelayAdminMetricsStore {
126
+ readonly persistent: boolean;
127
+ recordSessionCreated(session: RelaySessionRecord, recordedAt: number): Promise<void>;
128
+ recordSessionRenewed(session: RelaySessionRecord, recordedAt: number): Promise<void>;
129
+ recordSessionClosed(sessionId: string, reason: string, recordedAt: number): Promise<void>;
130
+ recordAppAttached(session: RelaySessionRecord, appId: string, recordedAt: number): Promise<void>;
131
+ recordAppDetached(sessionId: string, appId: string, recordedAt: number, reason: string): Promise<void>;
132
+ recordAppFrame(sessionId: string, appId: string, recordedAt: number): Promise<void>;
133
+ recordProviderFrame(sessionId: string, recordedAt: number): Promise<void>;
134
+ recordRequest(params: {
135
+ actor: RelayAdminRequestActor;
136
+ type: string;
137
+ ok: boolean;
138
+ errorCode?: string;
139
+ }): Promise<void>;
140
+ recordInstanceHeartbeat(heartbeat: RelayAdminInstanceHeartbeat): Promise<void>;
141
+ unregisterInstance(instanceId: string): Promise<void>;
142
+ listSessions(options?: RelayAdminSessionListOptions): Promise<RelayAdminSessionListResult>;
143
+ getSessionDetail(sessionId: string, now?: number): Promise<RelayAdminSessionDetail | undefined>;
144
+ getOverview(now?: number): Promise<RelayAdminOverview>;
145
+ listInstances(now?: number): Promise<RelayAdminInstanceStatus[]>;
146
+ close(): Promise<void>;
147
+ }
148
+ declare abstract class BaseRelayAdminMetricsStore implements RelayAdminMetricsStore {
149
+ abstract readonly persistent: boolean;
150
+ protected buildOverview(params: {
151
+ sessions: RelayAdminSessionSummary[];
152
+ instances: RelayAdminInstanceStatus[];
153
+ requestStats: RelayAdminRequestStats;
154
+ generatedAt: number;
155
+ }): RelayAdminOverview;
156
+ abstract recordSessionCreated(session: RelaySessionRecord, recordedAt: number): Promise<void>;
157
+ abstract recordSessionRenewed(session: RelaySessionRecord, recordedAt: number): Promise<void>;
158
+ abstract recordSessionClosed(sessionId: string, reason: string, recordedAt: number): Promise<void>;
159
+ abstract recordAppAttached(session: RelaySessionRecord, appId: string, recordedAt: number): Promise<void>;
160
+ abstract recordAppDetached(sessionId: string, appId: string, recordedAt: number, reason: string): Promise<void>;
161
+ abstract recordAppFrame(sessionId: string, appId: string, recordedAt: number): Promise<void>;
162
+ abstract recordProviderFrame(sessionId: string, recordedAt: number): Promise<void>;
163
+ abstract recordRequest(params: {
164
+ actor: RelayAdminRequestActor;
165
+ type: string;
166
+ ok: boolean;
167
+ errorCode?: string;
168
+ }): Promise<void>;
169
+ abstract recordInstanceHeartbeat(heartbeat: RelayAdminInstanceHeartbeat): Promise<void>;
170
+ abstract unregisterInstance(instanceId: string): Promise<void>;
171
+ abstract listSessions(options?: RelayAdminSessionListOptions): Promise<RelayAdminSessionListResult>;
172
+ abstract getSessionDetail(sessionId: string, now?: number): Promise<RelayAdminSessionDetail | undefined>;
173
+ abstract getOverview(now?: number): Promise<RelayAdminOverview>;
174
+ abstract listInstances(now?: number): Promise<RelayAdminInstanceStatus[]>;
175
+ abstract close(): Promise<void>;
176
+ }
177
+ export declare class InMemoryRelayAdminMetricsStore extends BaseRelayAdminMetricsStore {
178
+ readonly persistent = false;
179
+ private readonly sessions;
180
+ private readonly participants;
181
+ private readonly instances;
182
+ private readonly errorCodeCounts;
183
+ private readonly requestTypeCounts;
184
+ private readonly globalStats;
185
+ private ensureSession;
186
+ private ensureParticipant;
187
+ private activeInstances;
188
+ private liveState;
189
+ private requestStats;
190
+ recordSessionCreated(session: RelaySessionRecord, recordedAt: number): Promise<void>;
191
+ recordSessionRenewed(session: RelaySessionRecord, recordedAt: number): Promise<void>;
192
+ recordSessionClosed(sessionId: string, reason: string, recordedAt: number): Promise<void>;
193
+ recordAppAttached(session: RelaySessionRecord, appId: string, recordedAt: number): Promise<void>;
194
+ recordAppDetached(sessionId: string, appId: string, recordedAt: number, reason: string): Promise<void>;
195
+ recordAppFrame(sessionId: string, appId: string, recordedAt: number): Promise<void>;
196
+ recordProviderFrame(sessionId: string, recordedAt: number): Promise<void>;
197
+ recordRequest(params: {
198
+ actor: RelayAdminRequestActor;
199
+ type: string;
200
+ ok: boolean;
201
+ errorCode?: string;
202
+ }): Promise<void>;
203
+ recordInstanceHeartbeat(heartbeat: RelayAdminInstanceHeartbeat): Promise<void>;
204
+ unregisterInstance(instanceId: string): Promise<void>;
205
+ listSessions(options?: RelayAdminSessionListOptions): Promise<RelayAdminSessionListResult>;
206
+ getSessionDetail(sessionId: string, now?: number): Promise<RelayAdminSessionDetail | undefined>;
207
+ getOverview(now?: number): Promise<RelayAdminOverview>;
208
+ listInstances(now?: number): Promise<RelayAdminInstanceStatus[]>;
209
+ close(): Promise<void>;
210
+ }
211
+ export declare class RedisRelayAdminMetricsStore extends BaseRelayAdminMetricsStore {
212
+ readonly persistent = true;
213
+ private readonly redis;
214
+ constructor(redisUrl: string);
215
+ private ensureSession;
216
+ private loadSession;
217
+ private loadParticipant;
218
+ private pruneStaleInstances;
219
+ private loadLiveState;
220
+ private loadRequestStats;
221
+ recordSessionCreated(session: RelaySessionRecord, recordedAt: number): Promise<void>;
222
+ recordSessionRenewed(session: RelaySessionRecord, recordedAt: number): Promise<void>;
223
+ recordSessionClosed(sessionId: string, reason: string, recordedAt: number): Promise<void>;
224
+ recordAppAttached(session: RelaySessionRecord, appId: string, recordedAt: number): Promise<void>;
225
+ recordAppDetached(sessionId: string, appId: string, recordedAt: number, reason: string): Promise<void>;
226
+ recordAppFrame(sessionId: string, appId: string, recordedAt: number): Promise<void>;
227
+ recordProviderFrame(sessionId: string, recordedAt: number): Promise<void>;
228
+ recordRequest(params: {
229
+ actor: RelayAdminRequestActor;
230
+ type: string;
231
+ ok: boolean;
232
+ errorCode?: string;
233
+ }): Promise<void>;
234
+ recordInstanceHeartbeat(heartbeat: RelayAdminInstanceHeartbeat): Promise<void>;
235
+ unregisterInstance(instanceId: string): Promise<void>;
236
+ listSessions(options?: RelayAdminSessionListOptions): Promise<RelayAdminSessionListResult>;
237
+ getSessionDetail(sessionId: string, now?: number): Promise<RelayAdminSessionDetail | undefined>;
238
+ getOverview(now?: number): Promise<RelayAdminOverview>;
239
+ listInstances(now?: number): Promise<RelayAdminInstanceStatus[]>;
240
+ close(): Promise<void>;
241
+ }
242
+ export declare function createRelayAdminMetricsStore(params: {
243
+ redisUrl?: string;
244
+ }): RelayAdminMetricsStore;
245
+ export {};