Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Felix asked (2026-11-01 12:41Z) for image/file uploads from the web chat, "using our internal
file sharing/storage system". Filed as the human-side twin of #18. The good news, verified from
source at main
8d5964ba7e6: the storage half is already built. This is a wiring job.What already exists (read today from
services/files/)POST /filestakes anUploadFile, enforces a100 MB cap ("file too large (100 MB cap in v1)"), validates the name against
paths.SEGMENT_RE, defaults the target to the caller's home/home/<username>, and refuseswrites outside the caller's subtree (
_can_create: own home, or a non-personal tree whose topfolder is theirs or unclaimed;
/home/*is a reserved personal namespace).fpub()returns{id, name, size, content_type, owner, shared_with, created_at, updated_at}— that is the"file reference, not payload" shape, already the service's public form.
storage.py: S3 backend in prod (config fromTG_FILES_S3_*, R41 user-provided host), in-memory dict underTG_DEV=1; Mongo holdsmetadata only. So the retention/cost question is already delegated to an object store, and
it is a different bucket from the cm8b corpus bucket — no risk of colliding with ops#20.
presign_get(key, expires_s=900)returns atime-boxed URL (dev mode deliberately returns a non-URL
tg-memory://sentinel).shares_colwithpath_prefix, folders first-class,listing = own-scope R40 where superiors are not implicitly included).
services/web/imports/ui/FilesPane.jsxexists, and there is a deprecated flat listing kept"for old web clients until they browse by path" — so the web tier already speaks to this API.
The actual gap
One hop: the chat composer has no path into this. Per #18,
services/chat/app/routers/messages.pytreats the body as a plain
strfield (20k cap) with no image content type — so an upload hasnowhere to ride. (That line reference is #18's, filed 10-26; I re-read the files service today,
not the chat router, so treat the message-schema claim as #18's record.)
Proposed shape, as a ladder — each rung ships alone
POST /files. No new storage tier, no newbucket, no second permission model. Web forwards with the same caller-derived auth the service
already requires (
CLAIM_CALLERis explicitly ignored — identity is derived, never claimed, soa browser cannot upload into someone else's home).
fpub()(id/name/size/content_type) on the message doc. Keeps #6's length rule intact and prevents the 20k cap being
reinterpreted as a place to shove base64.
presign_getserves the bytes on demand.linking into the existing FilesPane view.
mail.save_attachmentalready lands bytes in an agent workspace; one mechanism, two front doors. That is what makes
"here is the screenshot" actionable for me instead of decorative.
Ordering constraint, load-bearing rather than cosmetic
#19 before #18, and #18 no later than rung 5. #19 is measured, not theoretical: gateways that
never send
architecture.input_modalitiesfall back to[text]silently — I found 130/130opencode rows stored text-only while models.dev carries real modality data for 87 of them
(comment 2102). Its warning is precisely this feature: once real multipart image traffic exists,
misclassified models produce all-candidates-failed proxy errors that look like provider outages
but are catalog defects. Uploads are harmless; image traffic against a text-only catalog is the
misleading-failure mode. Fix classification first.
Decisions I am deliberately not making (Felix's, per my role)
is not. Whether chat inherits the service ceiling or sets a lower one is a product call.
/home/<username>and supportspath=+ shares. Options: per-conversation folder under a shared tree, or the sender's home withan explicit share. The mechanism exists either way; the ownership rule does not.
just learned that lesson from the cm8b buckets (ops#20). A chat that accepts files needs a TTL.
prompt-injection bar should be stated before rung 5, not discovered after.
content_typebut I saw no allow/deny list in the uploadpath. Whether chat restricts to images/documents is a policy choice to make deliberately.
Cross-refs
#18 (image inputs — model-side twin, unassigned, blocked on #19) · #19 (modality classification —
prerequisite) · #14 (chat UX umbrella, where attachments was one bullet; this expands it) ·
#7 (mobile — a file card must survive a narrow screen) · #6 (message length — reference-not-payload
preserves it) · ops#20 (bucket economics, the retention precedent).
— rosa (filing, not deciding)