stapel-chat
Conversations and messaging over WebSocket: direct (1:1, idempotent by participant pair), group and support threads on one model; realtime send/edit/delete with a monotonic per-conversation seq for order and a separate revision sequence for resume, so an edit or a deletion made while a client was offline reaches it on reconnect; deletion is a tombstone the id keeps arriving under, so client caches and offline databases learn what to purge; attachments carry the render metadata a bubble needs on first paint (aspect, byte size, a 16px base64 thumbnail, voice duration and waveform, document mime and extension) behind an OPEN type registry; typing and activity states, read and delivery receipts as ephemeral signals; a live inbox stream so the conversation list does not poll either; anchor-paginated history and lists; and a support layer (queue, first-come assignment, open/pending/resolved with reopen).
Part of the Stapel framework — composable Django apps that deploy as a monolith or as microservices without changing module code.
Install
pip install stapel-chat
At a glance
| Fact | Value |
|---|---|
| Version | 0.7.2 |
| Python | >=3.11 (3.11, 3.12, 3.13, 3.14) |
| HTTP operations | 13 |
| Config axes | 15 |
| Usage surface | 21 |
| Extension points | 9 |
| Error codes | 65 |
| Fleet dependencies | stapel-auth (optional) · stapel-cdn (optional) · stapel-core · stapel-realtime |
Documentation
OpenAPI · capabilities.json · llms.txt (for agents)
One model backs three kinds of thread: direct (1:1, idempotent by participant pair), group, and support (a customer↔operator thread with a queue and assignment lifecycle).
Realtime is the module, not a mode of it. Messages are sent and received
over a WebSocket; REST serves history, hydration and the support lifecycle.
A deployment that cannot serve the socket fails manage.py check rather than
degrading into a product that refreshes on a timer — because a polling fallback
a product can end up in silently is exactly how "websockets are done" became a
false claim once already.
Quick start
INSTALLED_APPS = [
# ...
"stapel_core.django.apps.CommonDjangoConfig",
"stapel_core.django.users",
"rest_framework",
"stapel_realtime",
"stapel_chat",
]
CHANNEL_LAYERS = {"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {"hosts": [REDIS_URL]},
}}
STAPEL_COMM = {"SIGNAL_TRANSPORT": "channels"}
STAPEL_REALTIME = {"ALLOWED_ORIGINS": ["https://app.example.com"]} # with the port
# urls.py
urlpatterns = [path("chat/", include("stapel_chat.urls"))]
# asgi.py — the whole file
from django.core.asgi import get_asgi_application
from stapel_realtime.asgi import build_websocket_application
application = build_websocket_application(http_application=get_asgi_application())
pip install 'stapel-chat[realtime]'
What you get
- Two sockets.
ws/chat/<conversation_id>is the resumable journal —hello{last_seq}→ replay → live, withsend/edit/delete/read/delivered/activityframes going through the same service layer the REST views call.ws/chat/inboxkeeps the conversation list live, because a list with no socket refreshes on a timer forever however live the open thread is. - Two sequences.
seqis a message's immutable place in the thread — the sort key and the history anchor.rev_seqis its place in the revision journal, re-allocated on every edit and delete, and it is what realtime replay is anchored on: an edit made while a client was offline arrives in the catch-up. A client upserts by id, sorts byseq, and remembersrev_seqas its cursor. - Edit and delete. An edit sets
edited/edited_at. A delete leaves a tombstone: the id keeps being delivered withbody: "",attachments: []anddeleted: true, so a client cache learns which id to purge. An id that stops arriving is an id nobody can purge. Retention is permanent. - Attachments that render on first paint — aspect, byte size and a ~16px
base64 thumbnail for images and GIFs; duration and a waveform image for audio;
mime and extension for documents; poster and duration for video. The type set
is an open registry — stickers are a settings line — and the metadata comes
from
stapel-cdnby comm, once, at send time. - Receipts and activity. Separate delivery and read markers, both durable
and both fanned out live;
typing/recording_audio/sending_video/uploading_fileas ephemeral signals with a TTL, from another open registry. - Conversations —
POST /chat/api/v1/conversations(direct/group/support); direct is get-or-create by participant pair.GETlists yours (anchor-paginated) withunread_count, and every row carries its ownstream_keyandsocket_path. - Messages —
GET/POST /chat/api/v1/conversations/{id}/messages,PATCH/DELETE .../messages/{message_id}. History is anchored onseq, newest-first, both directions. - Support —
GET /chat/api/v1/support/queue,POST .../support/conversations/{id}/{assign,resolve,reopen}.
Configuration (STAPEL_CHAT)
| Key | Default | Meaning |
|---|---|---|
CHAT_KINDS |
["direct","group","support"] |
Enabled thread kinds |
ATTACHMENTS |
True |
Allow attachments on messages |
MAX_BODY_LENGTH |
4000 |
Hard cap on a text body |
ATTACHMENT_TYPES |
{} |
Open registry, merged over image/gif/video/audio/file — the same names stapel-cdn uses |
ACTIVITY_STATES |
{} |
Open registry, merged over typing/recording_audio/… |
ATTACHMENT_METADATA |
"cdn" |
Ask cdn.describe, or trust the client |
MAX_ATTACHMENTS |
10 |
Attachments per message |
MAX_PREVIEW_B64_BYTES |
8192 |
Ceiling on an inline data: preview |
EDIT_WINDOW_S |
0 |
Seconds a message stays editable (0 = forever) |
SCOPE_PROVIDER |
stapel_chat.scope.DefaultScopeProvider |
Resolve/enforce the opaque scope_key |
There is no key that turns realtime off. See MODULE.md for the full wire contract, the extension seams and the anti-patterns.
License
MIT — see LICENSE.
This page is assembled by stapel-readme from docs/readme.md plus the contract artifacts in docs/. Edit the prose in docs/readme.md; the badges, facts and links above and below it are generated — do not hand-edit README.md.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file stapel_chat-0.7.2.tar.gz.
File metadata
- Download URL: stapel_chat-0.7.2.tar.gz
- Upload date:
- Size: 167.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
57e813fdb407e1d54d78b1d53968e5daf2c06c2b9d44312637a58a62c16c3b23
|
|
| MD5 |
ce5e4cc8f9336f107db4028a92fffe44
|
|
| BLAKE2b-256 |
75b96b11b88aff08d1fa4c00e5a3a463c2f27ad42ec3712e3edaaa6824189201
|
Provenance
The following attestation bundles were made for stapel_chat-0.7.2.tar.gz:
Publisher:
publish.yml on usestapel/stapel-chat
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
stapel_chat-0.7.2.tar.gz -
Subject digest:
57e813fdb407e1d54d78b1d53968e5daf2c06c2b9d44312637a58a62c16c3b23 - Sigstore transparency entry: 2646044369
- Sigstore integration time:
-
Permalink:
usestapel/stapel-chat@bb6c88ff258f60937dacf0ea7d9de6533bd02474 -
Branch / Tag:
refs/tags/v0.7.2 - Owner: https://github.com/usestapel
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@bb6c88ff258f60937dacf0ea7d9de6533bd02474 -
Trigger Event:
push
-
Statement type:
File details
Details for the file stapel_chat-0.7.2-py3-none-any.whl.
File metadata
- Download URL: stapel_chat-0.7.2-py3-none-any.whl
- Upload date:
- Size: 140.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1a88b38b93bb5923f3c80d7bf876db575ace0b7ae094ef14d6785f4708fefc4e
|
|
| MD5 |
c4a5a7c77e19b05cf6b7b03e89fb223b
|
|
| BLAKE2b-256 |
644d535dc7b0a6a7f031ea24481ed6dce27f72fae5db83a0cbfcc32e8ad398ec
|
Provenance
The following attestation bundles were made for stapel_chat-0.7.2-py3-none-any.whl:
Publisher:
publish.yml on usestapel/stapel-chat
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
stapel_chat-0.7.2-py3-none-any.whl -
Subject digest:
1a88b38b93bb5923f3c80d7bf876db575ace0b7ae094ef14d6785f4708fefc4e - Sigstore transparency entry: 2646044519
- Sigstore integration time:
-
Permalink:
usestapel/stapel-chat@bb6c88ff258f60937dacf0ea7d9de6533bd02474 -
Branch / Tag:
refs/tags/v0.7.2 - Owner: https://github.com/usestapel
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@bb6c88ff258f60937dacf0ea7d9de6533bd02474 -
Trigger Event:
push
-
Statement type: