Repository navigation
feat: serve Submission and AttachmentUpload - #27
Open
mattdjenkinson wants to merge 1 commit into
Open
mattdjenkinson wants to merge 1 commit into
mattdjenkinson wants to merge 1 commit into
Conversation
Submissions are private feedback waiting for a staff decision.
- Users create them in the user context with a verified email, limited to
5 per 10 minutes, counted in Postgres. Names are always generated since
they are unique across users. Users can't change or withdraw them.
- Submitters read their own without triage, the reviewer or the public
draft; staff read all of them in the platform context.
- feedback-controller writes status.triage; feedback-sync writes
status.postRef, once.
- submissions/{name}/review takes a staff decision: Approved, Merged or
Rejected, once, from the platform context only. A merge gives the
submitter a Vote and Subscription in the same transaction; an approval
does when sync links the post.
- AttachmentUpload stores a file in a private bucket (gocloud: gs, file,
mem) with type, size and content checks. Submissions copy their uploads
and take names and types from them. submissions/{name}/attachment?ref=
serves a file to the submitter and staff as a sandboxed download.
- The API group now decodes query parameters with its own scheme, so
AttachmentOptions works.
- The email check and rate limiter move to shared packages.
to-security-report needs SecurityReport storage and comes with #9.
Part of datum-cloud/enhancements#943. Closes #8.
This was referenced Nov 12, 2026
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.
Serves
SubmissionandAttachmentUpload. Closes #8, exceptto-security-report, which needsSecurityReportstorage and moves to #9.Stacked on #26 (votes), which is on #25, #23, #22 and #16.
Submissions
--submission-limit(5) per--submission-window(10m). It's counted in Postgres, so it holds across replicas, and 429s carry aRetry-Afterfor when the oldest submission in the window ages out.sub-plus random hex). They're unique across users so staff can fetch by name, which means a chosen name could reveal that someone else's submission exists.phase,postRef, and the decision withreasonandmergedInto. They don't seetriage, the reviewer, or the staff-edited public title and body. Staff see everything, with the owner in thefeedback.miloapis.com/ownerannotation from feat: serve Vote and Subscription #26 (kubectl get submissions -o wide).feedback-controllerwritesstatus.triage(fraud score in 0..1, valid kind).feedback-syncwritesstatus.postRef, once. Anyone else, staff included, is refused.POST submissions/{name}/reviewwith aSubmissionReview, from staff in the platform context only (never sync or the controller).ApprovedneedspublicTitleandpublicBody;MergedneedsmergedInto, which must be a visible, unmerged post;Rejectedneeds areason.PendingReview; reviewing it again returns Conflict.Subscription(reason: merged) and aVote(source taken from the submission's client), and the post is re-tallied, all in the review's transaction. An existing vote is left alone.reason: submittedwhen sync setspostRef, since the post doesn't exist before that.Attachments
AttachmentUpload(create-only, user context, verified email, 20 per 10 minutes per user per replica) stores a file in--attachments-bucket.gs://in clusters,file://ormem://locally.--attachment-max-size, 2 MiB by default, which keeps the base64 body under the apiserver's 3 MiB limit.uploadRefs. The server copies the caller's own uploads under the submission and takes each file's name and type from the upload, ignoring the client's. Someone else's ref is Invalid.GET submissions/{name}/attachment?ref=serves the file to the submitter and staff (others get NotFound). It's sent as an attachment withnosniff, a sandbox CSP andno-store.uploads/after a day), which Deploy to staging #13 should add with the bucket.Other changes
metav1.ParameterCodec, which doesn't know the group's own option types, so?ref=failed with "no kind is registered". It now uses a codec built from the server's scheme, tested for bothAttachmentOptionsand list options.AttachmentOptionshas its ownOpenAPIModelName. Without it, the one promoted from the embeddedTypeMetawould make its OpenAPI definitionTypeMeta's.internal/identityand the rate limiter tointernal/ratelimit.Testing
portalvote and amergedsubscription and re-tallied the post, and a second review got Conflict;Retry-After.