Repository navigation
Add arbitrary file uploads (agentdraft file) - #33
Merged
Merged
Conversation
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.
Summary
Adds an arbitrary-file lane to agentdraft so
agentdraft file <path>uploads any localfile (screenshot, recording, log, PDF, zip, build artifact) and returns a stable public URL
— our in-house replacement for third-party file hosts. Reuses the existing workers, R2
bucket, D1, auth, and CLI (Option A: shared origin).
Documents already worked via
agentdraft upload(HTML/Markdown, validated + rendered).This adds the binary lane alongside it, served as-is.
Security model (the core of this change)
Arbitrary files share the content origin with rendered documents, so a naively-served file
could execute as a page there. The gate that prevents it:
Content-Disposition: attachmentUNLESS itsMIME is on a strict inline allowlist (
image/png|jpeg|gif|webp|avif,video/mp4|webm,audio/mpeg|mp4,text/plain,application/pdf).text/htmlandimage/svg+xmlare never inline — the two script-carrying types.X-Content-Type-Options: nosniffalways, so a file lying about its type can't bereinterpreted as markup.
default-src 'none'; sandbox,noindex,CORP: same-origin.keeping them in sync, and is asserted by unit tests.
What's included
0005_files.sql—filestable (unversioned; soft-delete + disable columns).POST /api/files(streams the raw body straight to R2, never buffered;100 MB cap; idempotency-key dedupe),
GET /api/files(list, read scope),POST /api/files/:id/disable(takedown, ownership in the query).GET /f/:id— attachment-default serve with the inline allowlist,nosniff, hard-immutable cache,
304revalidation, andRange/206for media seeking.agentdraft file <path>with--content-type/--idempotency-key, streamingupload with no client timeout, 100 MB local pre-check.
agentdraft-file-uploadreplacing the external file-host skill.api.md+cli.mdupdated.What was tested (real prod, deployed workers)
Unit tests: 78 passed (60 existing + 18 new for the inline-allowlist safety gate).
Worker + content-worker typecheck clean; dashboard
svelte-check0 errors; CLIgo buildgo vetclean.Size cap rationale
100 MB = the Cloudflare Free-plan request-body limit (verified against
developers.cloudflare.com/workers/platform/limits), enforced at the edge before the Worker
runs, plus a
Content-Lengthpre-check and a local CLI pre-check. Bytes stream to R2 soWorker memory (128 MB) is never the constraint.