aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/src
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-node/src')
-rw-r--r--packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py28
1 files changed, 20 insertions, 8 deletions
diff --git a/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py b/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py
index 298b3cd..99ba3c9 100644
--- a/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py
+++ b/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py
@@ -1178,11 +1178,23 @@ class WebRTCPeerSession:
# `user_id` in the body would let any member of this group read or
# overwrite any other member's blob, which is finding C5 one size down.
+ def _user_blob_refuse(self, detail: str, kind: str = "") -> None:
+ """
+ Refuse, and say so in the audit log.
+
+ A refusal used to be invisible here: the audit line was written only
+ after a store *succeeded*, so a client whose writes were all being
+ turned away looked exactly like a client that never wrote — which is
+ how a wedged sync went unnoticed for two hours.
+ """
+ self._audit("user_blob_refused", f"{kind} {detail}".strip())
+ self._send({"type": "error", "detail": detail})
+
def _user_blob_kind(self, msg: dict) -> str | None:
"""The validated `kind`, or None having already refused."""
kind = msg.get("kind")
if not isinstance(kind, str) or not _USER_BLOB_KIND_RE.match(kind):
- self._send({"type": "error", "detail": "Unknown blob kind"})
+ self._user_blob_refuse("Unknown blob kind", str(kind)[:40])
return None
return kind
@@ -1206,13 +1218,13 @@ class WebRTCPeerSession:
blob = msg.get("blob_enc")
if not isinstance(blob, (bytes, bytearray)) or not blob:
- self._send({"type": "error", "detail": "Missing blob_enc"})
+ self._user_blob_refuse("Missing blob_enc", kind)
return
blob = bytes(blob)
rev = msg.get("rev")
if not isinstance(rev, int) or rev < 0:
- self._send({"type": "error", "detail": "Missing rev"})
+ self._user_blob_refuse("Missing rev", kind)
return
limit = (USER_BLOB_MANIFEST_MAX if kind == "playlists"
@@ -1221,8 +1233,7 @@ class WebRTCPeerSession:
# A stated reason, not a bare error: the client turns this into a
# sentence the reader can act on ("this playlist is too large"),
# and a refusal nobody can read is a support case.
- self._send({"type": "error",
- "detail": f"Blob too large ({len(blob)} > {limit})"})
+ self._user_blob_refuse(f"Blob too large ({len(blob)} > {limit})", kind)
return
# What the account already uses, minus whatever this call replaces.
@@ -1231,9 +1242,9 @@ class WebRTCPeerSession:
if existing:
used -= len(existing["blob_enc"])
if used + len(blob) > USER_BLOB_ACCOUNT_MAX:
- self._send({"type": "error",
- "detail": f"Account blob quota exceeded "
- f"({used + len(blob)} > {USER_BLOB_ACCOUNT_MAX})"})
+ self._user_blob_refuse(
+ f"Account blob quota exceeded "
+ f"({used + len(blob)} > {USER_BLOB_ACCOUNT_MAX})", kind)
return
await store.store_user_blob(self._user_id, kind, rev, blob)
@@ -1263,6 +1274,7 @@ class WebRTCPeerSession:
if not store:
return
blobs = await store.list_user_blobs(self._user_id)
+ self._audit("user_blob_list", f"{len(blobs)} blobs")
self._send({"type": MNP.USER_BLOB_LIST_RESP, "v": MNP_VERSION,
"blobs": blobs})