Public: anyone can read this; the crew can change it.
A shared board cannot be used to fill the disk: bound the undo log, a board's size, and board creation #16
Sign in to join this conversation.
Public: anyone can read this; the crew can change it.
A board anyone can open and change has nothing standing between it and the disk: the
actionlog grows by a row for every change and is only ever shortened byundo, a board can hold unlimited columns and cards, andPOST /boardswill create boards as fast as it is asked. Give the app a floor it can defend before identity and an MCP agent make writing to it even easier.Done looks like
src/limits.ts(new) holds every limit as one named constant with a sentence saying why, and nothing else: undo depth, columns per board, cards per board, boards created per minute, and the byte cap for a request body.src/tty.tsalready handle afalse.src/db.ts, not only insrc/routes.ts, so the terminal and any later caller (MCP) get it too: the create returnsnull/falseand the route answers400with a sentence a person can act on ("this board is full — 500 cards"). The terminal shows the same sentence in its message line instead of doing nothing.src/limits.ts, in memory, for the whole process, refuses more than the per-minute cap with429and aretry-afterheader, and recovers on its own. It is keyed by nothing a caller controls, so it cannot itself become an unbounded map — say plainly in the README that this is a crude, app-wide brake, not per-person fairness.src/limits.test.tsnew, plus cases insrc/server.test.ts): the 51st column is refused with400and the board is unchanged; the undo log stops growing at N and the oldest action is the one dropped, while undo still works for the newest change; the bucket refuses after the cap and allows again once refilled (inject the clock, do not sleep); a refused create writes no row.bun testpasses.Files
src/limits.ts(new),src/limits.test.ts(new),src/db.ts,src/routes.ts,src/tty.ts,src/server.test.ts,README.md.Risks
A limiter keyed by anything a caller sends (ip, header, board id) is a memory leak wearing a safety vest — keep it a single bucket. Trimming the log in a different transaction from the write leaves
undoreading a row that is gone. A limit enforced only in the route silently leaves the terminal and the future/mcpunbounded. Numbers chosen too low turn a legitimate busy board into a broken one; say the number in the error, always.Blocked by the transactional write path, because the trim belongs inside that transaction.
Suggested brief: scout (small, tested, says what it left out); sentinel to review the pull — denial of service and unbounded state are its beat.