Repository navigation
Commit de5c326
`keel/web/render.py` is gone: 987 lines of server-side HTML, the seven page
handlers that called it, and its inline stylesheet. The client moved from
`/static/` to `/` and is the application now. Net -896 lines.
── THE WRITE SURFACE MOVED, AND THAT IS WHAT MADE A LAYER REAL ────────────────
`/setup/*` was an HTML form. It is `POST /api/setup/<key>` now, and the action
SET is a 0-line diff: `keel.commands.setup.ACTIONS`, still only idempotent,
non-destructive, `MECHANICAL` steps, still asserted disjoint from the eleven
capability-increasing actions. A browser can set a deployment up; it still
cannot arm a rule, attest an asset or enable autonomy.
What changed is that `X-Keel-Client` now covers the write path.
`_api_client_header_ok` recorded that widening as "#536's call to make, once
(and only once) the forms it replaces are gone" -- because a `<form>` cannot set
a header, and gating it would have refused every legitimate submission with no
terminal to fall back to on the desktop bundle. The forms are gone.
The CSRF token moved into `X-Keel-CSRF` for the same reason: in a body it would
prove only that the sender could READ it; in a header it also proves the sender
could set one, which a cross-origin form cannot do at all.
── A REVERSAL, RECORDED RATHER THAN QUIETLY APPLIED ───────────────────────────
`payload.setup_payload` used to argue against ever putting the token in a GET
response: "minting a live write credential into a GET would put it into every
cached copy, every proxy log and every paste". Two of those were already
answered -- `/api/*` is `no-store` and #538's worker refuses to cache it, and the
server binds loopback. The third does not survive the observation that settles
it: this token authorises NOTHING without the session cookie, and anyone holding
that cookie can mint it themselves. The old argument is quoted in place.
── TWO BUGS THE SUITE COULD NOT HAVE FOUND ────────────────────────────────────
**A refused write poisoned the next request on the same connection.** HTTP/1.1
keep-alive; a POST refused before its body was read left those bytes in the
socket, and the stdlib parsed them as the next request line -- 501, on the
request AFTER the one that was correctly refused. It became possible in this
commit: moving the token to a header put the refusal in front of the body read.
Every test in the suite opens a fresh connection, so none could see it. Found by
driving a browser through five refusals in a row. `_drain_request_body` fixes it
and disconnects rather than draining an oversized body, and a test now drives two
requests down one socket.
**An action's outcome was never visible.** The first version wrote the server's
message into the card and then repainted the view, destroying it. The second
restored it after the action's own repaint -- and the 15-second poll wiped it
mid-sentence. Outcomes are session state now, re-applied in `rebuildInto`, which
every path that replaces the view goes through.
── THE PIN TRANSFERRED, AND IT IS BIGGER THAN BEFORE ──────────────────────────
`test_console_thinness` named `render` explicitly so its deletion would fail
LOUDLY rather than shrink the scanned set. It did exactly that. The property --
"a THIRD renderer over the same reports, never a second place that computes
them" -- belongs to the API layer now: `api`, `events`, `security`, `server` and
`staticfiles` are pinned where three were.
`keel/web/security.py`'s five layers are untouched.
Claude-Session: https://claude.ai/code/session_01T6yA5khYnJ2qzheArRToQ2
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 8a973e4 commit de5c326
22 files changed
Lines changed: 1188 additions & 2084 deletions
File tree
- keel/web
- static
- css
- js
- tests
- commands
- web
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
40 | 40 | | |
41 | 41 | | |
42 | 42 | | |
| 43 | + | |
43 | 44 | | |
44 | 45 | | |
45 | 46 | | |
| |||
157 | 158 | | |
158 | 159 | | |
159 | 160 | | |
160 | | - | |
| 161 | + | |
| 162 | + | |
161 | 163 | | |
162 | 164 | | |
163 | 165 | | |
164 | 166 | | |
165 | 167 | | |
166 | 168 | | |
167 | 169 | | |
168 | | - | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
169 | 179 | | |
170 | 180 | | |
171 | 181 | | |
| |||
607 | 617 | | |
608 | 618 | | |
609 | 619 | | |
| 620 | + | |
| 621 | + | |
| 622 | + | |
| 623 | + | |
| 624 | + | |
| 625 | + | |
| 626 | + | |
| 627 | + | |
| 628 | + | |
| 629 | + | |
| 630 | + | |
| 631 | + | |
| 632 | + | |
| 633 | + | |
| 634 | + | |
| 635 | + | |
| 636 | + | |
| 637 | + | |
| 638 | + | |
| 639 | + | |
| 640 | + | |
| 641 | + | |
| 642 | + | |
| 643 | + | |
| 644 | + | |
| 645 | + | |
| 646 | + | |
| 647 | + | |
| 648 | + | |
| 649 | + | |
| 650 | + | |
| 651 | + | |
610 | 652 | | |
611 | 653 | | |
612 | 654 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1479 | 1479 | | |
1480 | 1480 | | |
1481 | 1481 | | |
| 1482 | + | |
| 1483 | + | |
| 1484 | + | |
| 1485 | + | |
| 1486 | + | |
| 1487 | + | |
| 1488 | + | |
| 1489 | + | |
| 1490 | + | |
| 1491 | + | |
1482 | 1492 | | |
1483 | 1493 | | |
1484 | 1494 | | |
| |||
1526 | 1536 | | |
1527 | 1537 | | |
1528 | 1538 | | |
| 1539 | + | |
1529 | 1540 | | |
1530 | 1541 | | |
1531 | 1542 | | |
1532 | | - | |
1533 | | - | |
1534 | | - | |
1535 | | - | |
1536 | | - | |
| 1543 | + | |
| 1544 | + | |
| 1545 | + | |
| 1546 | + | |
| 1547 | + | |
| 1548 | + | |
| 1549 | + | |
| 1550 | + | |
| 1551 | + | |
| 1552 | + | |
| 1553 | + | |
| 1554 | + | |
| 1555 | + | |
| 1556 | + | |
| 1557 | + | |
| 1558 | + | |
| 1559 | + | |
| 1560 | + | |
| 1561 | + | |
| 1562 | + | |
1537 | 1563 | | |
1538 | 1564 | | |
1539 | 1565 | | |
1540 | 1566 | | |
| 1567 | + | |
1541 | 1568 | | |
1542 | 1569 | | |
1543 | 1570 | | |
1544 | 1571 | | |
| 1572 | + | |
| 1573 | + | |
| 1574 | + | |
1545 | 1575 | | |
1546 | 1576 | | |
1547 | 1577 | | |
| |||
0 commit comments