feat(gateways): store inbound media as HTTP reference, not inline RDF - #127
Open
retog wants to merge 1 commit into
Open
feat(gateways): store inbound media as HTTP reference, not inline RDF#127retog wants to merge 1 commit into
retog wants to merge 1 commit into
Conversation
Inbound messenger attachments (voice notes, images) are persisted as a durable on-disk blob and referenced from the message record by a kb:attachment IRI resolved over the gateway's own token-gated GET /media/<id> — never embedded inline in RDF (no data: URI), regardless of size. Consistency over data-in-graph: one uniform mechanism for every attachment type keeps the life store pure triples. - inbound_store.py: kb:attachment predicate (multi-valued), store_media/ load_media (hex-keyed, traversal-safe, no RDF extension so qlever-dir ignores the blobs), write_message/undelivered carry attachment_urls. - signal/whatsapp/telegram gateways: read media bytes once, persist the durable reference BEFORE transcription so a failed/garbled transcript never costs the recording; add the original audio to the forwarded files payload (size-capped) so it rides into the dashboard conversation alongside its transcript; serve GET /media/<id> (token-gated). Co-Authored-By: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Inbound messenger media (voice notes, images) is persisted as a durable
on-disk blob referenced by an IRI — never embedded inline in RDF as a
data:URI — regardless of size. The message record points at each blobthrough a
kb:attachmenttriple resolved over the gateway's own token-gatedGET /media/<id>.Rationale (owner's decision): consistency over data-in-graph. One uniform
mechanism for every attachment type — small or large, image or audio — keeps the
life store pure triples, instead of a size threshold that inlines some blobs and
references others.
This also fixes the original motivation: when an inbound voice note is surfaced
into a dashboard conversation, the original audio now rides along in the
forwarded
filespayload, not just its (often garbled) transcript.How
inbound_store.pykb:attachmentpredicate (multi-valued IRI object). The media type isdeliberately not stored in RDF — it is returned by the HTTP response's
Content-Typewhen the reference is resolved, which is where a media typebelongs once the payload lives behind a URL.
store_media()/load_media(): blobs keyed bytoken_hex(16)(never anuntrusted filename), with a
<id>.typesidecar.load_mediavalidates the idagainst a strict hex regex before touching the filesystem (path-traversal
safe). Blobs carry no RDF extension, so qlever-dir — which indexes only
.nt/.ttl/.n3plus declared converters — ignores them: the binaries sit onthe same volume as the message
.ntfiles without ever entering the graph.write_message()/undelivered()carryattachment_urls(deduped, emptiesdropped).
signal / whatsapp / telegram gateways
transcription, so a failed or garbled transcript never costs the recording.
base64-encoded
filespayload forwarded to triage honoursMAX_INBOUND_FILE_BYTES.filespayload (sizepermitting) so the conversation carries the recording alongside its
transcript.
GET /media/<id>(token-gated,_reply_raw).All new steps are best-effort and non-raising: any failure forwards/persists the
message without the media link rather than dropping the message.
Tests
tests/test_inbound_image_forward.pyupdated for the new(files, attachment_urls)/(voice, files, attachment_urls)return contracts and thereworded attachment note, including the oversized-image case (payload dropped,
durable reference kept).
tests/test_inbound_store.pyand the image-forwardsuite pass; all four scripts compile.
Branch policy
Tier 3 (framework
scripts/), so this goes through a PR.