1# Chapter 18: web/src, one page and the little it takes to serve it 2 3Seven files. Read them in this order and you have the whole site. 4 5**[content.rs](content.rs) is what the page says, as data.** The title and 6the description a link preview carries. The addresses it links to. The 7hook, twelve pixels by twelve, that sits in every title bar. And the four 8pictures, each a `Shot`: the file, a title, a sentence or two, the words 9for someone who cannot see it, and its real width and height. There is no 10markup in this file, on purpose: you can change what the site says without 11touching how it is drawn. 12 13**[hooks.rs](hooks.rs) is the map of hook points.** Claude Code raises 14thirty-three kinds of event a plugin can answer, and the page lists every 15one with what jevhooks does there: judged, heard, an idea, or nothing yet. 16The list is not typed out a second time. It is the plugin's own 17(`jevhooks_events::HookEvent`, chapter 8), the very code the mod and the 18daemon are compiled from, and "judged" and "heard" are read from its 19`role`. So the page cannot claim an event the plugin does not handle. What 20this file adds is where an event sits in a session, a sentence on what 21happens there, and the ideas somebody has written down. 22 23**[rules.rs](rules.rs) is the rules, drawn.** Chapter 12's rules are 24compiled into three small networks, and the page shows each one as a 25picture. Not a picture *of* them, drawn by hand beside the code, which 26would be wrong by the second change: `rete-draw` draws the very network 27the daemon runs, compiled into this Worker from the same crate. What this 28file adds is ten worked cases (`cargo test`, a force push, a turn that 29stopped early, ...). Each is an answer Jev could give, read through the 30plugin's own thresholds and run through the network, and how it ended is 31whatever the network said. Nobody typed "allow" next to `cargo test`. 32 33**[view.rs](view.rs) draws it.** `page(origin)` is a pure function: an 34address in, a string of HTML out. It fetches nothing, reads no clock and 35knows no visitor. That is what makes the page testable without a browser 36or a Cloudflare account: the tests call `page`, and read the string. 37 38**[lib.rs](lib.rs) is the Worker.** Every request passes through `fetch`, 39which does four things in order: 40 41```mermaid 42flowchart TD 43 R["a request"] --> G{"came to the canonical address,<br/>or a local one?"} 44 G -- "no, and it is a GET or HEAD" --> M["308 to the same path<br/>on hooks.lmjtfy.fun"] 45 G -- yes --> C{"the preview card?"} 46 C -- yes --> A["from the static assets"] 47 C -- no --> P["the router:<br/>the page, the icon, robots.txt,<br/>a picture, a shared file, or 404"] 48 M --> H["security headers on every answer"] 49 A --> H 50 P --> H 51 H --> L["one log line: route, method, status, time"] 52``` 53 54**[log.rs](log.rs) is what gets written down**, and mostly what does not. 55A path is turned into one of nine `Route`s before anything is logged, so 56the log can say `GET shot -> 200 in 1 ms` and has nowhere to put an 57address, a query or a header. A path nobody serves is the one word 58`other`, never its own text. 59 60**[bin/card.rs](bin/card.rs) draws the link preview's card** (chapter 17's 61second aside) and prints it. 62 63> **Aside: a policy with no holes to forget.** Every answer carries a 64> Content Security Policy, the header that tells a browser what a page may 65> load. This site's says: its own files, and nothing else. No script from 66> anywhere else, no script written into the page, none built from a 67> string. It comes from `aldebaran-headers`, where a policy starts at 68> "nothing" and each allowance is a method you have to call by name 69> (this site calls two: `with_inline_styles`, for the stylesheet the shell 70> puts in the head, and `with_data_images`, because the dotted background 71> is a picture the shared script draws in your browser and hands to the 72> page as data; the first deploy left that one out, and the dots were 73> simply not there). A test builds the page and fails if an inline 74> `<script>` ever appears, because the browser would drop it without a 75> word. 76 77> **Try it.** `mise run test:web`, then break something: 78> give a `Shot` the wrong `height` in `content.rs` and 79> `each_picture_declares_its_real_size` reads the real one out of the SVG 80> and says so; put a second `loading="eager"` picture in and 81> `every_feature_has_its_picture` notices. 82 83## For the people who maintain it 84 85| Path | What | 86| --- | --- | 87| [content.rs](content.rs) | `NAME`, `TITLE`, `DESCRIPTION`, the links, `TRY_IT`, the install lines (`VERSION`, `install`, `release_key`, which reads `tools/release.pub`), the card's path and size, `HOOK`, `Shot` and the four of them (`SHOTS`), and `FILES`, the bundle the pictures are served from. | 88| [hooks.rs](hooks.rs) | `Phase`, `State`, `Hook`, `hook(event)` (an exhaustive match: a new event does not compile until it is placed), `all`, `count`, `BEYOND` (the two mod events used for model choice), and tests that hold "built" to the plugin's own roles. | 89| [rules.rs](rules.rs) | `run` (a network run to its end, with the rules that decided), `judgments` (the three, each with its cases as `<details>`, drawn by `rete_draw::rete`), and tests that hold each case's ending to the network's. | 90| [view.rs](view.rs) | `page`, `favicon`, the link preview (`jev_ui::preview::Preview`), and the page's tests. | 91| [page.css](page.css) | The site's own few rules, after the shared sheet: the lede, the picture's frame, the promise, the steps, the map's chips, a case, and the look of a drawn network (`.rete`; `rete-draw` writes classes and no styles). Tokens only. | 92| [lib.rs](lib.rs) | `fetch`, `gate`, `card`, the router, `policy`, `origin` (from `SITE_ORIGIN`), and the gate's and policy's tests. | 93| [log.rs](log.rs) | `Route` and its names; the note a missing card writes (`mise run web:build`). | 94| [bin/card.rs](bin/card.rs) | The card as SVG on stdout: `jev_ui::card::Chrome` at twice its base size, 1200 by 630, three lines in the window. | 95 96← Previous: [Chapter 17, web/](../) · Up: [web](../) · Next: nothing; you have read it all. Back to [the start](../../) →