feat: file blocks #10

Merged
dhkatz merged 1 commits from feat/file-blocks into main 2026-11-05 22:48:08 +00:00
Owner

Phase 3, part 2 (docs/design.md, "Next"): file blocks. Moderators block a file network-wide by its sha256, so a removed product can't come back under another name or catalog.

Blocking

  • A file's page (Console → Blocks → a file) shows whether it's blocked and the products using it, so a moderator sees what a block removes before confirming ("Block and remove 2 products").
  • Moderators reach it from a product's files (each has a Block link for them) or by pasting a sha256 into the Blocks tab, which also lists blocked files.
  • Blocking a file:
    • removes every product with a revision using it. Each removal is audited as product.remove with the file, so creators are notified and can appeal it as before.
    • puts a {type: "block", sha256} change in the changes feed of every catalog hosting it, meaning: delete it whatever uses it. (t5de-catalog doesn't read the feed yet.)
    • refuses uploads with it from then on (409 blocked). Each attempt is audited as file.blocked_upload with the person and catalog, so moderators see who tried.
  • Unblocking lets the file in again. The products it removed stay removed until a moderator restores them, one by one.
  • Who: product:remove hub-wide (moderators and admins). The Blocks tab shows only for them.

API: GET /api/v1/blocks, and GET/PUT/DELETE /api/v1/blocks/:sha256 (docs/products.md).

Migration 0013_file_blocks: the file_block table, and a GIN index on revisions' file lists, so finding the products using a file doesn't scan every revision.

Checks

  • 53 API tests pass. The new blocks.test.ts covers:
    • who may block
    • the preview of what a block removes
    • removal of every product using the file, across catalogs and creators
    • the changes feed, notifications and appeals
    • refused uploads and their audit entry
    • unblocking, and restoring a removed product afterwards
  • Type checks pass.
  • On a local hub, through the website's own forms:
    • the Block link on a product's files, for moderators only
    • the Blocks tab, for moderators only (404 for the creator)
    • the file's page, then blocking: the product is gone, its creator notified and offered Appeal
    • a re-upload through the catalog API: refused, and audited
    • unblocking, then Restore
    • Fixed along the way: an empty reason gave a generic "Some of that isn't right"; it now says "Give a reason."

🤖 Generated with Claude Code

Phase 3, part 2 (`docs/design.md`, "Next"): file blocks. Moderators block a file network-wide by its sha256, so a removed product can't come back under another name or catalog. **Blocking** - A file's page (Console → Blocks → a file) shows whether it's blocked and the products using it, so a moderator sees what a block removes before confirming ("Block and remove 2 products"). - Moderators reach it from a product's files (each has a Block link for them) or by pasting a sha256 into the Blocks tab, which also lists blocked files. - Blocking a file: - removes every product with a revision using it. Each removal is audited as `product.remove` with the file, so creators are notified and can appeal it as before. - puts a `{type: "block", sha256}` change in the changes feed of every catalog hosting it, meaning: delete it whatever uses it. (t5de-catalog doesn't read the feed yet.) - refuses uploads with it from then on (409 `blocked`). Each attempt is audited as `file.blocked_upload` with the person and catalog, so moderators see who tried. - Unblocking lets the file in again. The products it removed stay removed until a moderator restores them, one by one. - Who: `product:remove` hub-wide (moderators and admins). The Blocks tab shows only for them. **API**: `GET /api/v1/blocks`, and `GET`/`PUT`/`DELETE /api/v1/blocks/:sha256` (`docs/products.md`). **Migration** `0013_file_blocks`: the `file_block` table, and a GIN index on revisions' file lists, so finding the products using a file doesn't scan every revision. **Checks** - 53 API tests pass. The new `blocks.test.ts` covers: - who may block - the preview of what a block removes - removal of every product using the file, across catalogs and creators - the changes feed, notifications and appeals - refused uploads and their audit entry - unblocking, and restoring a removed product afterwards - Type checks pass. - On a local hub, through the website's own forms: - the Block link on a product's files, for moderators only - the Blocks tab, for moderators only (404 for the creator) - the file's page, then blocking: the product is gone, its creator notified and offered Appeal - a re-upload through the catalog API: refused, and audited - unblocking, then Restore - Fixed along the way: an empty reason gave a generic "Some of that isn't right"; it now says "Give a reason." 🤖 Generated with [Claude Code](https://claude.com/claude-code)
dhkatz added 1 commit 2026-11-05 22:33:12 +00:00
feat: file blocks
Pull request / title (pull_request) Successful in 1s
CI / test (pull_request) Successful in 1m8s
CI / image (apps/api/Dockerfile, t5de-hub-api) (pull_request) Skipped
CI / image (apps/web/Dockerfile, t5de-hub-web) (pull_request) Skipped
ba423f32d8
Moderators block a file network-wide by its sha256, from a product's files or the console's new Blocks tab, which
first shows the products using it. Blocking removes those products (their creators are told and may appeal), puts
a `block` change in the changes feed of every catalog hosting it, and refuses uploads with it; moderators see who
tried. Unblocking lets it in again; the products it removed stay removed until restored.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
dhkatz merged commit 58a3e70a63 into main 2026-11-05 22:48:08 +00:00
dhkatz deleted branch feat/file-blocks 2026-11-05 22:48:08 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: t5de/t5de-hub#10