| … | … | @@ -1,16 +1,9 @@ |
| 1 | 1 | # favela-surface |
| 2 | 2 | |
| 3 | | The favela renderer. Server-side HTML, no JavaScript required to read. |
| 3 | **Read your favela in a browser.** `favela-peer` owns the data; this turns it into pages. |
| 4 | 4 | |
| 5 | | ## The boundary |
| 6 | | |
| 7 | | ⛔ **No storage, no keys, no swarm.** It holds a socket to `favela-peer` and nothing else. A surface |
| 8 | | that grows a Corestore has become a second peer implementation, which is the fork risk |
| 9 | | `favela-markdown/spec/library.md` §1 names for the codec and which applies here identically. |
| 10 | | |
| 11 | | Everything it knows, it asked the daemon. Everything it renders, `favela-markdown` rendered. |
| 12 | | |
| 13 | | A test enforces both: no `atproto-*`/`ap-*` imports, and no `corestore`/`hypercore`/`autobee`. |
| 5 | It is a renderer and only a renderer. Everything it knows it asked the daemon over a unix socket, |
| 6 | and everything it shows, `favela-markdown` rendered. Close it and you lose nothing but the window. |
| 14 | 7 | |
| 15 | 8 | ## Running |
| 16 | 9 | |
| … | … | @@ -19,6 +12,14 @@ favela-peer & # the daemon owns the data |
| 19 | 12 | favela-surface # http://127.0.0.1:4042 |
| 20 | 13 | ``` |
| 21 | 14 | |
| 15 | ⚠ **If every page says the daemon is down, it is almost always the socket.** `FAVELA_SOCK` has to |
| 16 | point at the *daemon's* storage, not the surface's. The two apps have separate defaults, so a |
| 17 | surface left on its own looks for a socket nobody created. This package's pgh defaults set it to |
| 18 | `${HOME}/.config/favela-peer/favela.sock`; set it yourself if you run the daemon elsewhere. |
| 19 | |
| 20 | `FAVELA_SURFACE_PORT` and `FAVELA_SURFACE_HOST` move the listener. It binds `127.0.0.1` — this is |
| 21 | your own daemon's data with no authentication in front of it, so exposing it is a decision. |
| 22 | |
| 22 | 23 | ## Signing in |
| 23 | 24 | |
| 24 | 25 | Every route is gated except `/login`. A browser gets in with a **one-time invite**, minted on the |
| … | … | @@ -30,24 +31,48 @@ favela browsers # who is admitted |
| 30 | 31 | favela revoke <pubkey> # retire one |
| 31 | 32 | ``` |
| 32 | 33 | |
| 33 | | The browser generates an Ed25519 keypair, keeps it in `localStorage`, and answers a challenge with |
| 34 | | it. There is **no expiry and no password** — the key is the credential, and revocation is the only |
| 35 | | way out. ⛔ A revoke lands on the browser's *next request*, not when a session lapses: the daemon is |
| 36 | | asked whether the key is still allowed on every page load. |
| 34 | Your browser makes an Ed25519 keypair, keeps it in `localStorage`, and answers a challenge with it. |
| 35 | There is **no expiry and no password** — the key is the credential, so revoking is the only way out. |
| 36 | |
| 37 | Two consequences worth knowing before they surprise you: |
| 38 | |
| 39 | - A revoke lands on that browser's **next request**, not whenever a session would have lapsed. The |
| 40 | daemon is asked whether the key is still allowed on every page load. |
| 41 | - Clearing site data means asking for a new invite. Nothing on the server remembers you. |
| 42 | - Restarting the surface signs everyone out, because sessions live in memory. That is a restart, |
| 43 | not an expiry, and it follows from the surface holding no storage. |
| 37 | 44 | |
| 38 | | ⚠ **`/login` is the one page with JavaScript.** Reading needs none; signing does — a form post |
| 39 | | cannot produce an Ed25519 signature. The signer is served from this process (`/static/…`), never a |
| 40 | | CDN: a page that loaded its signer from someone else's server would let that server mint signatures |
| 41 | | with your key. |
| 45 | ## JavaScript |
| 46 | |
| 47 | Reading needs none. Timelines, profiles, posts, search, settings, the graph — all server-rendered |
| 48 | HTML, and they work with JavaScript switched off. |
| 49 | |
| 50 | Three pages load a script, for three different reasons — and only two of them need one: |
| 51 | |
| 52 | | page | what it loads | without JavaScript | |
| 53 | |---|---|---| |
| 54 | | `/login` | an inline module + the vendored signer (21K) | **doesn't work** — a form post cannot produce an Ed25519 signature | |
| 55 | | `/` (the timeline) | `composer.js` (2.6K), the `@`-picker | **works** — it posts a real multipart form; the picker only saves you typing | |
| 56 | | `/explore` | `explore.js` + three.js (~730K) | **doesn't work** — it is a rotating 3D view | |
| 57 | |
| 58 | The composer is the pattern to copy. It inserts `@alice` — ordinary text you could have typed — |
| 59 | and the daemon resolves mentions on the write path either way. With the script missing the composer |
| 60 | still works; with it present it cannot produce a mention the server would not have produced anyway. |
| 61 | |
| 62 | `/explore` is the one place a browser pays for a megabyte, and only that page loads it. Its legend |
| 63 | is server-rendered on purpose — an explanation of what the dots mean is no use if it arrives after |
| 64 | the WebGL. |
| 65 | |
| 66 | ⛔ **Everything under `/static/` is vendored, never a CDN.** A page that loaded its signer from |
| 67 | someone else's server would let that server mint signatures with your key. See `static/README.md`. |
| 68 | |
| 69 | ## The boundary |
| 42 | 70 | |
| 43 | | ⚠ Sessions live in memory, so restarting the surface signs everyone out. That is a restart, not an |
| 44 | | expiry, and it follows from the surface holding no storage. |
| 71 | ⛔ **No storage, no keys, no swarm.** This process holds a socket and nothing else. A surface that |
| 72 | grows a Corestore has quietly become a second peer implementation — the fork risk |
| 73 | `favela-markdown/spec/library.md` §1 names for the codec, which applies here identically. |
| 45 | 74 | |
| 46 | | ⚠ `FAVELA_SOCK` must point at the DAEMON's socket, which is `favela-peer`'s storage — declared in |
| 47 | | this package's pgh defaults as `${HOME}/.config/favela-peer/favela.sock`. The two apps have separate |
| 48 | | storage defaults, so a surface left on its own default looks up a socket that does not exist and |
| 49 | | every page reports the daemon as down. |
| 75 | `test/boundary.test.js` enforces it: no source file and no dependency may name `corestore`, |
| 76 | `hypercore`, `autobee`, `autobase`, `hyperswarm`, `hyper-overlay`, `atproto-*` or `ap-bridge`. |
| 50 | 77 | |
| 51 | | `FAVELA_SOCK` points at the daemon; `FAVELA_SURFACE_PORT` and `FAVELA_SURFACE_HOST` at the listener. |
| 52 | | It binds `127.0.0.1` by default — this is your own daemon's data with no authentication in front of |
| 53 | | it, so exposing it is a decision, not a default. |
| 78 | `npm test` — 175 tests, 565 asserts, all passing. |