feat(KB-136): add shared badge pub/sub for multi-instance deployments

- Add BadgePubSub interface with Redis and in-memory adapters

- Implement cross-instance badge snapshot delivery via Redis pub/sub

- Add message validation, echo loop prevention, and structured caching

- Support graceful fallback to in-memory mode when Redis unavailable

- Update ServerOptions for dependency injection and add documentation
This commit is contained in:
gsxdsm
2026-03-30 12:52:07 -07:00
parent fc0ce7cf95
commit 2dbcf2433e
14 changed files with 1341 additions and 23 deletions

View File

@@ -28,6 +28,22 @@ kb dashboard --paused # Start with automation paused (review before work
kb dashboard --dev # Start web UI only (no AI engine)
```
### Multi-Instance Deployments
When deploying the dashboard behind a load balancer with multiple instances, configure Redis pub/sub for real-time badge updates across instances:
```bash
# Set Redis URL for cross-instance badge synchronization
export KB_BADGE_PUBSUB_REDIS_URL="redis://redis.example.com:6379"
# Optional: customize the pub/sub channel (default: kb:badge-updates)
export KB_BADGE_PUBSUB_CHANNEL="my-app-badge-updates"
kb dashboard
```
With this configuration, PR/issue badge updates detected on one instance are delivered to subscribed WebSocket clients on other instances. Each instance maintains its own GitHub polling, only the badge snapshots are shared.
### Create a task
```bash

View File

@@ -37,6 +37,7 @@
"@mariozechner/pi-ai": "^0.62.0",
"@mariozechner/pi-coding-agent": "^0.62.0",
"express": "^5.1.0",
"ioredis": "^5.6.0",
"multer": "^2.1.1"
},
"peerDependencies": {

View File

@@ -60,10 +60,9 @@ describe("CLI package.json publishing config", () => {
expect(pkg.private).not.toBe(true);
});
it("does not have @kb/* workspace packages in dependencies", () => {
it("declares ioredis as a runtime dependency for badge pub/sub", () => {
const deps = Object.keys(pkg.dependencies || {});
const kbDeps = deps.filter((d) => d.startsWith("@kb/"));
expect(kbDeps).toEqual([]);
expect(deps).toContain("ioredis");
});
});