feat(observer): M-Obs 4-7 — widgets, dashboard, docs, integration example
Some checks failed
Test / test (push) Has been cancelled
Some checks failed
Test / test (push) Has been cancelled
M-Obs 4: @shade/widgets React library - ShadeProvider context with observer URL + token + theme - useShadeState (polling) + useShadeEvents (SSE) hooks - 7 widgets: IdentityCard, SessionList, PrekeyStock, RecentActivity, ServerStatus, FingerprintCompare, WidgetCatalog (meta-widget for user-selectable layout with localStorage persistence) - Self-contained CSS via inline styles, no external CSS conflicts - Light/dark/auto theme via tokens M-Obs 5: @shade/dashboard standalone SPA - Vite + React app composing all widgets into a full debugger layout - Login screen with token persistence to localStorage - Build script copies dist/ to @shade/observer/dist/ for embedded serving - 211 KB JS bundle (66 KB gzipped) M-Obs 6: Documentation + integration example - READMEs for @shade/observer and @shade/widgets - examples/06-observer-dashboard runnable demo: spins up prekey server + observer, runs Alice ↔ Bob conversation loop, dashboard at :3901 - Updated root README and docker-compose.yml with observer integration M-Obs 7: End-to-end verification - StateAggregator now drains buffered events on subscription, so identity.initialized fires before observer construction are still seen - Verified live: snapshot endpoint returns full state, dashboard serves, 401 without auth, sessions/messages/ratchet steps all tracked 220 tests passing, 0 failures. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
64
packages/shade-observer/README.md
Normal file
64
packages/shade-observer/README.md
Normal file
@@ -0,0 +1,64 @@
|
||||
# @shade/observer
|
||||
|
||||
Live observability backend for Shade — exposes a snapshot endpoint, an SSE event stream, and serves the bundled dashboard SPA.
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
bun add @shade/observer @shade/server @shade/core
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```ts
|
||||
import { createObserver } from '@shade/observer';
|
||||
import { ShadeEventEmitter, ShadeSessionManager } from '@shade/core';
|
||||
import { PrekeyServerEvents, createPrekeyServer } from '@shade/server';
|
||||
|
||||
// 1. Create event emitters
|
||||
const clientEvents = new ShadeEventEmitter();
|
||||
const serverEvents = new PrekeyServerEvents();
|
||||
|
||||
// 2. Wire them into your session manager and prekey server
|
||||
const manager = new ShadeSessionManager(crypto, storage, { events: clientEvents });
|
||||
const prekeyServer = createPrekeyServer({ crypto, events: serverEvents });
|
||||
|
||||
// 3. Create the observer
|
||||
const observer = createObserver({
|
||||
token: process.env.SHADE_OBSERVER_TOKEN!,
|
||||
clientEvents,
|
||||
serverEvents,
|
||||
});
|
||||
|
||||
// 4. Mount or serve standalone
|
||||
import { Hono } from 'hono';
|
||||
const app = new Hono();
|
||||
app.route('/shade-observer', observer);
|
||||
|
||||
Bun.serve({ port: 3900, fetch: app.fetch });
|
||||
```
|
||||
|
||||
After this, visit `http://localhost:3900/shade-observer/dashboard/` and enter your bearer token to see the dashboard.
|
||||
|
||||
## Endpoints
|
||||
|
||||
| Method | Path | Auth | Description |
|
||||
|--------|------|------|-------------|
|
||||
| GET | `/api/state` | Bearer | Current snapshot (identity, sessions, prekeys, server stats) |
|
||||
| GET | `/api/events` | Bearer (or `?token=`) | SSE stream of live events |
|
||||
| GET | `/dashboard/` | None | Bundled web UI |
|
||||
| GET | `/health` | None | Liveness check |
|
||||
|
||||
## Configuration
|
||||
|
||||
| Env var | Required | Description |
|
||||
|---------|----------|-------------|
|
||||
| `SHADE_OBSERVER_TOKEN` | Yes | Bearer token (min 16 chars). Refuses to start if shorter. |
|
||||
|
||||
The token is checked with constant-time comparison.
|
||||
|
||||
## Security notes
|
||||
|
||||
- Event payloads contain NO key material, plaintext, or signatures — only structural facts (counters, addresses, short hashes for display).
|
||||
- The observer is intended for internal/debugging use. Put it behind a reverse proxy and authenticate access.
|
||||
- The dashboard stores the bearer token in `localStorage` for convenience. Don't load the dashboard on shared computers.
|
||||
@@ -82,9 +82,16 @@ export class StateAggregator {
|
||||
private readonly store?: PrekeyStore,
|
||||
) {
|
||||
if (clientEvents) {
|
||||
// Replay any events that fired before subscription, then subscribe
|
||||
for (const e of clientEvents.getBufferedSince(0)) {
|
||||
this.handleClientEvent(e);
|
||||
}
|
||||
clientEvents.on((e) => this.handleClientEvent(e));
|
||||
}
|
||||
if (serverEvents) {
|
||||
for (const e of serverEvents.getBufferedSince(0)) {
|
||||
this.handleServerEvent(e);
|
||||
}
|
||||
serverEvents.on((e) => this.handleServerEvent(e));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user