Skip to content

misospace/miso-chat

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

959 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Miso Chat

GHCR Build Release License

Chat with your OpenClaw AI assistant from anywhere, protected by authentication.

Features

  • 🔐 Authentication: OIDC (Authentik, Okta, Google) + local username/password fallback
  • 💬 Real-time chat: WebSocket connection to OpenClaw gateway
  • 📱 Mobile-friendly: PWA-style responsive UI with native app feel
  • 🔔 Background alerts: Optional browser notifications + sounds for assistant replies when tab is hidden
  • 🐳 Containerized: Docker + Kubernetes deployment ready
  • 🔒 Security hardened: Non-root user, rate limiting, XSS protection
  • 🤖 Automated: CI/CD with linting, testing, and multi-platform builds
  • 📲 OTA Updates: Automatic over-the-air updates for native mobile apps (no external service required)

Quick Start

Docker Compose

git clone https://github.com/misospace/miso-chat.git
cd miso-chat
cp .env.example .env
docker-compose up -d

Docker

docker run -d --name miso-chat \
  -p 3000:3000 \
  -e GATEWAY_URL=ws://your-gateway:18789 \
  -e SESSION_SECRET=your-secret \
  ghcr.io/misospace/miso-chat:latest

Configuration

Variable Required Default Description
GATEWAY_URL Yes - WebSocket URL to OpenClaw gateway
PORT No 3000 Server port
SESSION_SECRET Yes - Secret for sessions
SESSION_COOKIE_SAMESITE No strict (or lax when OIDC enabled) Session cookie SameSite policy (`strict
SESSION_COOKIE_SECURE No true in production Override session cookie Secure flag
CSRF_TRUSTED_ORIGINS No - Comma-separated extra origins allowed for state-changing requests
OIDC_ENABLED No false Enable OIDC auth
LOCAL_USERS If local admin:password123 Users (user:pass)
REDIS_URL No - Optional Redis/Dragonfly session store
CAPACITOR_COOKIES_ENABLED No true Enable Capacitor cookie bridge for native app builds
PUSH_NOTIFICATIONS_ENABLED No false Reserved for future browser push support (production web-push NOT implemented)
PUSH_VAPID_PUBLIC_KEY If push enabled - Public VAPID key (reserved for future implementation)
PUSH_VAPID_PRIVATE_KEY If push enabled - Private VAPID key (reserved for future implementation)
PUSH_VAPID_SUBJECT If push enabled - Contact URI for VAPID claims (reserved for future implementation)

Gateway Scope Model

The miso-chat gateway WebSocket client requests OAuth scopes from the OpenClaw gateway on connect.

  • Default scopes (least-privilege): operator.read, operator.write — sufficient for normal chat, session list, history, send, and abort operations.
  • Admin/pairing scopes: operator.admin, operator.pairing — only requested when GATEWAY_ADMIN_SCOPES=true is set in the environment.

This reduces blast radius: if a web/session bug exposes gateway capabilities, the default configuration cannot perform admin or pairing actions.

Note: Earlier versions of miso-chat included chat.send, sessions.send, sessions.list, and sessions.history in REQUESTED_GATEWAY_SCOPES. These are gateway method names, not valid OAuth scopes, and were removed as non-scope entries. The OpenClaw gateway rejects invalid scope names; normal chat/session operations only require operator.read + operator.write.

Migration path

Deployments that need admin or pairing features (e.g., device pairing flows, admin tooling) should set:

-e GATEWAY_ADMIN_SCOPES=true \

No code changes are required — the scope list is built at startup from the environment variable.

Security

  • Adds baseline HTTP hardening headers (X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy).
  • Enforces origin checks on POST/PUT/PATCH/DELETE requests to reduce CSRF risk (configure extra trusted origins with CSRF_TRUSTED_ORIGINS).
securityContext:
  runAsNonRoot: true
  runAsUser: 1000
  readOnlyRootFilesystem: true
  allowPrivilegeEscalation: false
  capabilities:
    drop:
      - ALL

Development

npm install
npm run dev    # Development
npm run test   # Run tests
npm run lint   # Lint

Mobile Session Persistence (Capacitor)

For native APK builds, session persistence depends on cookie handling between the WebView and backend:

  • capacitor.config.js now enables CapacitorCookies by default (CAPACITOR_COOKIES_ENABLED=true).
  • If the app runs from an app origin (capacitor:// or custom scheme) and talks to a remote HTTPS API, set:
    • SESSION_COOKIE_SAMESITE=none
    • SESSION_COOKIE_SECURE=true
  • 401 responses already trigger a login redirect in the client as a fallback when a session has expired.

Background Notifications

Foreground browser notifications (tab-visible only; production web-push is NOT implemented yet).

  • Open the top-right menu () and toggle Alerts on.
  • The app will ask for browser notification permission once.
  • If notifications are blocked, allow them in site settings and re-enable Alerts.

Note: This uses the native Browser Notification API while the page is loaded. Production background web-push (via VAPID/SW) is reserved for future implementation.

Testing

Post-Deploy Smoke Check

Verify deploy health, login flow, and send-message API path:

SMOKE_BASE_URL="https://miso-chat.example.com" \
SMOKE_USERNAME="admin" \
SMOKE_PASSWORD="your-password" \
npm run smoke:deploy

Optional overrides:

  • SMOKE_SESSION_KEY (default: default)
  • SMOKE_MESSAGE (default: timestamped smoke ping)
  • SMOKE_HEALTH_URL (if health endpoint is not /api/health)
  • SMOKE_TIMEOUT_SECONDS (default: 20)

WebSocket Reconnection

The GatewayWsManager includes automatic reconnection with exponential backoff:

  • Tested: Connection loss recovery (server restart, network interruption)
  • Tested: Exponential backoff delays (1s, 2s, 4s, 8s...)
  • Tested: Max reconnection attempts limit (default: 5)
  • Tested: Origin preservation across reconnections
  • Tested: Event emission for reconnecting, reconnect-error, reconnect-failed

Manual testing performed by:

  1. Starting miso-chat with active gateway connection
  2. Stopping the OpenClaw gateway service
  3. Observing reconnection attempts in logs
  4. Restarting gateway - connection automatically restored

See: lib/gateway-ws.js for implementation details (issue #111, parent #110)

WebSocket Persistence

Issue #115 - WebSocket connection persistence and behavior testing.

The Gateway connection maintains persistent state across disruptions:

Scenario Behavior Status
Gateway restart Auto-reconnect with exponential backoff ✅ Tested
Network interruption (brief) Reconnection after 1s → 2s → 4s... ✅ Tested
Network interruption (extended) Max 5 attempts, then reconnect-failed ✅ Tested
Browser sleep/wake Connection resumes if within retry window ✅ Tested
Server-side close (1000/1001) Clean close, no reconnect ✅ Tested
Server-side error (1011) Triggers reconnect sequence ✅ Tested

Persistence Configuration:

const manager = new GatewayWsManager({
  maxReconnectAttempts: 5,    // Max retries before giving up
  reconnectDelay: 1000,       // Initial delay (ms)
  reconnectBackoff: 2,        // Exponential multiplier
  // Max delay = 1000 * 2^4 = 16000ms (16s) on final attempt
});

Events for Monitoring:

manager.on('reconnecting', (attempt, delay) => {
  console.log(`Reconnecting in ${delay}ms (attempt ${attempt})`);
});

manager.on('reconnect-failed', (err) => {
  console.log('Giving up - manual intervention needed');
});

Pending Request Handling:

  • In-flight requests during disconnect → timeout after 30s (configurable)
  • Requests queued while disconnected → immediate error
  • Successful reconnect does not retry failed requests (client responsibility)

API Endpoints

Sessions

  • GET /api/sessions - List all sessions
  • GET /api/sessions/:sessionKey/history - Get session message history
  • POST /api/sessions/:sessionKey/send - Send a message

Reactions

  • GET /api/reactions/:sessionKey - Get all reactions for a session
  • GET /api/messages/:messageId/reactions - Get reactions for a message (optionally scoped with ?sessionKey=...)
  • POST /api/messages/:messageId/reactions - Toggle a reaction (add/remove)
  • Investigation notes for gateway reaction notifications: docs/reaction-events-investigation.md

Contributing & Releases

Before Release Checklist

  • Update CHANGELOG.md with changes
  • Update version in package.json
  • Ensure all tests pass (npm test)
  • Run linting (npm run lint)
  • Check for security vulnerabilities (npm audit)
  • Verify README.md is current
  • Test WebSocket reconnection manually

Issues & PRs

Changelog

v0.3.0 (2026-03-01)

  • #125: Add reaction counts like Discord - reactions now show count badges
  • #126: Add dark/light theme toggle with localStorage persistence
  • Improved WebSocket reconnection on errors
  • Fixed emoji picker background and positioning
  • Typing indicator now responds to gateway events only

v0.2.0 (2026-02-28)

  • Initial release
  • OIDC authentication support
  • Real-time WebSocket chat
  • Mobile-friendly PWA UI

License

MIT License - see LICENSE.

Notes

  • When running behind Envoy/Ingress over HTTPS, this app trusts one proxy hop for secure session cookies.

  • WebSocket upgrades require an authenticated session (unauthenticated upgrades are rejected with 401).

  • With OIDC enabled, /login redirects directly to /auth/oidc.

  • Startup validation fails fast when OIDC is enabled but required env vars are missing.

Home-ops tip (Dragonfly)

If you enable the dragonfly component in home-ops, set:

env:
  REDIS_URL: "redis://{{ .Release.Name }}-dragonfly:6379"

This is optional. If REDIS_URL is not set, miso-chat falls back to in-memory sessions.

OTA Updates

Miso Chat supports automatic over-the-air (OTA) updates for native mobile apps using the self-hosted Capgo Capacitor Updater. No external service or API key required.

How It Works

  1. When a new release is published on GitHub, the update manager automatically checks for updates
  2. If an update is available, you'll see a notification in the app
  3. Tap "Update Now" to download and install the update
  4. The app will restart with the new version

Requirements

  • Native mobile app build (Android APK or iOS app)
  • Capacitor platform with @capgo/capacitor-updater plugin installed
  • GitHub release with update-manifest.json asset

Manual Update Check

To manually check for updates, call:

await MobileUpdateManager.checkForUpdate();

Update Notification

The update notification appears when:

  • A new version is available
  • The app is running on a native mobile platform
  • showNotification is enabled in the config

Configuration

The update manager can be configured with:

await MobileUpdateManager.init({
  autoCheck: true,        // Automatically check for updates on startup
  showNotification: true, // Show update notification when available
  checkInterval: 3600000, // Check every hour (default)
  debug: false            // Enable debug logging
});

Release Workflow

To publish an update, use the Manual Release GitHub Actions workflow and enter a version like 0.4.6. It normalizes v0.4.6 to 0.4.6, bumps package.json, commits the bump to main, creates the plain-semver tag, and creates the GitHub release with generated notes.

  1. Run the Manual Release workflow with the target version
  2. The workflow updates package.json on main
  3. The workflow creates the plain-semver git tag (for example 0.4.6)
  4. Attach the APK/IPA and update-manifest.json to the created GitHub release if needed
  5. The update manager will automatically detect the new version

Troubleshooting

Updates not appearing?

  • Check that the GitHub release includes update-manifest.json
  • Verify the version number in package.json is higher than the current version
  • Check browser console for update manager errors

Update failed?

  • Ensure the APK/IPA is properly signed
  • Check network connectivity
  • Verify the update-manifest.json contains valid bundleUrl

About

Chat container w/ auth

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors