Skip to content

shellular-org/server

Repository files navigation

Shellular Server

WebSocket relay server for Shellular — relays messages between Shellular CLI host agent and the Shellular app client.

Requirements

  • Node.js 18+
  • pnpm

Development

Install

pnpm install

Environment

Create a local .env from .env.example. The server requires WS_TOKEN_SECRET with at least 32 characters for signing short-lived app WebSocket tickets.

OAuth provider settings live in .env. Apple Sign in with Apple uses APPLE_CLIENT_ID, APPLE_TEAM_ID, and APPLE_KEY_ID, and reads the private key from the ignored apple_key.p8 file in the server directory.

Architecture

The server is two independently-deployed processes under one monorepo:

  • central (central/) — auth, DB (SQLite), token minting, the relay/presence registry, and HTTP routes. Runs no WebSocket relay.
  • relay (relay/) — a DB-free regional relay that verifies signed tokens and just relays messages between the CLI and app. Deploy one per region. The relay registers itself with central and reports presence and stats.

Scripts are prefixed central: / relay: so it's always explicit which one you're running.

Dev

pnpm run dev

Prod - under PM2

Central

# start (named "shellular-server-central")
pnpm run central:pm2:start

# restart
pnpm run central:pm2:restart

Relay

# start (named "shellular-server-relay")
pnpm run relay:pm2:start

# restart
pnpm run relay:pm2:restart

OAuth Login

See docs/oauth-flow.md for provider setup, token lifecycle, and the app/WebSocket authentication flow.

App WebSocket Authentication

The app no longer sends access tokens or device metadata in the /app WebSocket URL. Instead:

  1. The app refreshes its access token.
  2. The app sends POST /auth/ws-app-token with Authorization: Bearer <accessToken> and client metadata in the JSON body.
  3. The server validates the user token, ClientInfoSchema, host existence, and known-client identity.
  4. The server returns a signed app WebSocket ticket that expires after 30 seconds.
  5. The app opens /app?wsToken=<ticket>.

The /cli WebSocket and /host/register flow are unchanged.

App Notices

Short, dismiss-once messages shown as a popup on the app home screen (e.g. maintenance heads-ups). Notices are served from a plain JSON file so they can be pushed to already-installed apps without an app update.

How it works

  • Notices live in content/notices.json.
  • The server reads that file into memory on boot and re-reads it about once an hour, so edits show up in the app without a redeploy or restart. A malformed edit keeps the previous cache rather than taking the endpoint down.
  • GET /notices returns the cached notices; the app fetches this and shows the first notice the user hasn't dismissed.
  • Each notice has a stable id. The app remembers which ids a user dismissed, so a dismissed notice never reappears — but adding a new notice (new id) pops up for everyone. To push a fresh message, add a new notice.

Managing notices

Use the notices CLI rather than hand-editing the JSON — it owns id generation so ids are never typed by hand.

# List all notices
pnpm notices list

# Add a notice interactively — prompts for title and body (id is generated automatically)
pnpm notices add

# Add a notice non-interactively with flags
pnpm notices add --title "Heads up" --body "Some message"

# Edit an existing notice's title and/or body
pnpm notices edit <id> --title "New title" --body "New body"

# Remove a notice
pnpm notices rm <id>

Changes take effect the next time the server re-reads the file (within ~1 hour), or immediately on restart.

License

AGPL-3.0-only

Links

About

Shellular relay server.

Resources

License

Stars

12 stars

Watchers

0 watching

Forks

Contributors