abstract_chatshare
Login-gated chat + file-share + video backend for abstractendeavors.com (route /chat).
It is the server half of the contract in PROTOCOL.md; the React frontend
(@putkoff/abstract-chatshare) is the other half.
The service is a single asyncio process: websockets for the WS/HTTP surface and psycopg
(3, async) over the shared auth_v2 Postgres database. Presence, typing, call state and fan-out
live in memory; Postgres is the durable store. There is no web framework, no Redis, no connection
pooler — just a tiny internal asyncio.Queue pool of a few connections. The only third-party
dependencies are websockets>=17 and psycopg[binary]>=3.1, plus self-hosted coturn for TURN.
What it does
- Discord-like rooms: invite-only, owner/mod/member roles, bans, invite links (with optional
guest registration through abstract_logins'
invitestable), DMs, reactions, edits, read markers, typing, soft-deletes, attachments backed by the existing Secure Files system. - Calls: the server only relays WebRTC signaling (
rtc.signal) between members of the same room call and issues ephemeral coturn REST credentials; media is a P2P mesh. - Bots: a generic, Discord-style bot API (see below). The first bot, hugpy-ai, is built separately as a hugpy-side adapter against this API; this service knows nothing about it.
All of the wire protocol (auth handshake, objects, events, requests, rate limits) lives in
PROTOCOL.md and is implemented exactly.
Install / run
pip install abstract_chatshare # websockets + psycopg[binary]
abstract-chatshare migrate # create/upgrade the chat_* tables (advisory-locked)
abstract-chatshare serve # listen on CHAT_HOST:CHAT_PORT (default 127.0.0.1:6016)
serve runs migrations on startup as well. Configuration is entirely environment-driven; see the
Config table in PROTOCOL.md and deploy/chat.env.example. The default DSN is
postgresql:///auth_v2 (unix socket, peer auth as the abstractendeavors service user).
nginx proxies location ^~ /chat/ to 127.0.0.1:6016 with the path preserved, so the service
sees /chat/ws, /chat/bot, /chat/health, /chat/invite/... and /chat/files/.... See
deploy/ for the systemd unit, nginx snippet, env example and a hardened turnserver.conf.
Module map (src/abstract_chatshare/)
| module | responsibility |
|---|---|
config.py |
env → immutable Config; wire limits + reaction set |
db.py |
tiny async pool, migrations (advisory-locked), every query (parameterized) |
auth.py |
handshake Origin/cookie/session/access checks; periodic re-check |
ice.py |
STUN list + coturn REST ephemeral TURN creds |
ratelimit.py |
per-user and per-IP sliding windows |
serialize.py |
rows → wire objects (User/Room/Member/Invite/Ban/Message/Bot), attachment re-resolution |
hub.py |
connection registry, membership cache, presence, raw fan-out, typing throttle |
calls.py |
in-memory call state + mesh signaling bookkeeping |
app.py |
shared state + high-level fan-out + the single post_message path |
handlers.py |
user request dispatch + validation |
bots.py |
bot invocations, bot request surface, mentions, command validation |
http.py |
public invite endpoints + bot-file serving (CORS, per-IP limits) |
server.py |
websockets.serve + process_request routing + the connection loops |
cli.py |
serve, migrate, bot … |
Bot API (surface for the hugpy-ai adapter)
A bot is a global actor (available in every room and in DMs; never a chat_members row). It is
created by the operator and connects over a dedicated websocket. Build a hugpy-side adapter against
this surface; the service stays generic.
Admin (operator CLI, token shown once, only its sha256 is stored):
abstract-chatshare bot create <name> --display-name "hugpy-ai" [--description "..."]
# prints csb_<id>_<token> (store it; it is never shown again)
abstract-chatshare bot list
abstract-chatshare bot rotate-token <name> # prints a new token
abstract-chatshare bot enable|disable <name>
name matches ^[a-z0-9_-]{2,32}$ and is the @name used to mention the bot.
Connection: ws://127.0.0.1:6016/chat/bot with header Authorization: Bot <token>.
- A request carrying an
Originheader is refused 403 (bot tokens must never be used from a browser). A bad/disabled token is 401. One live connection per bot; a new one replaces the old (old closed 4409). Max inbound frame 24 MiB (forfile.put). Framing is identical to the user side ({t, rid, ...}requests →{t:"res", ...};{t:"ev", ...}events).
Privacy: a bot receives only (a) messages that @mention it, (b) every message in its DM
room, and (c) slash commands addressed to it (cmd.invoke). It never sees other room traffic.
A bot may post in a room only in reply to an invocation from that room at most 30 min old, and
may edit/delete only its own messages, within 30 min of posting.
Events to the bot: bot.ready, invoke (kind ∈ mention|dm|command), autocomplete.
Requests from the bot: commands.set, invocation.defer (→ bot.thinking), msg.send
(normal or ephemeral), msg.edit (fan-out coalesced to ≤4/s), msg.delete, typing,
file.put (≤16 MiB, served from GET /chat/files/{token}), autocomplete.result.
See the Bots section of PROTOCOL.md for exact payloads and CommandSpec/OptionSpec.
Note (clarification over PROTOCOL.md): every non-ephemeral bot reply tied to an invocation
stores an interaction object (not only command invocations), so the frontend can clear the
bot.thinking placeholder for mention/dm replies too. For mention/dm, interaction.command is
null. This is a superset of the spec's requirement and was recorded in PROTOCOL.md.
Metadata
Release files for abstract-chatshare 0.0.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| abstract_chatshare-0.0.3.tar.gz | 56.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| abstract_chatshare-0.0.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 106.4 kB