feat(daemon): bound a session's unwritten input with a byte budget #71
No reviewers
Labels
No labels
priority/P0
priority/P1
priority/P2
release/v0.1.0
status/blocked
status/planned
type/bug
type/design
type/test-gap
type/tracker
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
natsukium/felis!71
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/bounded-io-queues-15-v2"
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?
Fixes #15.
What this changes
Both ends of the input path were unbounded, so a peer could turn a
same-UID
felis sessions sendloop — or a stalled carrier — intounbounded memory growth.
Daemon side.
PtyWriterqueued into an unbounded channel andanswered every write with
Ready(Ok(len)), so nothing above it couldtell "queued" from "written". The writer now carries a queued/written
gauge, and each session admits at most 16 MiB of unwritten input against
a per-session semaphore. The connection's inbound pump reserves before
the bytes enter the command channel, so an exhausted budget stops the
peer that is typing — backpressure reaching its socket — while the
session actor stays free to serve every other subscriber.
felis daemon statusreports it as the newpty_input_bytesrow.Window side. The window's outgoing frame queue is bounded at 4 MiB
and coalesces the kinds whose queued value is state (resize, focus,
color scheme, viewport, buttonless motion) in place; keystrokes, pastes
and every other kind keep their order. Past the cap the window treats
the carrier as lost and reconnects, since megabytes a socket has not
accepted describe a dead transport rather than a slow one.
CLI side.
felis sessions sendnow returns only once the daemon hasadmitted the payload against that budget, so it no longer exits
0onbytes the child never received;
--timeoutbounds the admission wait aswell as the mark watch.
Wire: protocol minor 7 —
ResourceKind::PTY_INPUT_BYTES. It degrades tono other row, so a daemon answering a client below minor 7 omits it.
Doc cascade
docs/reference/ipc.md(the minor ledger, the backpressure section),docs/reference/cli.md(send,daemon status, the doctor example),docs/reference/spec.md(REQ-1011a),docs/explanation/security-model.md,docs/explanation/architecture/session-lifecycle.md,docs/explanation/architecture/control-surfaces.md,CHANGELOG.md, andskills/felis/SKILL.md.Review follow-ups
A sol review of the rebased branch found three defects, all fixed here:
MAX_PASTE_BYTESconstants disagreeing by 64bytes, so payloads in the gap passed a sender's preflight and were
then refused by the daemon's admission, which closes the connection.
There is now one constant, and
send_inputcarries the validationthe coalescing queue had stopped inheriting from
send_control;felis bridge'ssessions.sendanswered at the write rather than atadmission, reporting success for bytes a stalled child never received;
could take an earlier one's place and reflow the child before it read
coordinates computed against the old grid.
sealsis now a set.Rebase note
This branch was built on the issue #14 branch, which landed on main by
rebase with review follow-ups, and main moved again while this was in
flight.
fix(cli): settle the first-operation deadline on the bridge anchorwas dropped: main solved the same problem differently, byexempting
Opsconnections from the first-operation deadline outright.The branch is
feat/bounded-io-queues-15-v2because the first push nolonger rebased cleanly onto main and force-pushing was not on the table.
Verified: just check green (fmt, clippy, nextest 3163 passed, deny,
proto-compat), pi review request-changes on the first round with all
three findings accepted and fixed.