diff --git a/README.md b/README.md index 87ff039..765c4e6 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,29 @@ # portal -The question runtime behind [uhhm.no](https://uhhm.no). Every page, -form, review desk, and state machine it serves is declared in the -[`questions`](https://project.uhhm.no/uhhm/questions) content repo — -this codebase is the engine that renders, enforces, and records, and -it special-cases none of it. +A content-driven question engine: one Rust binary that turns a Git +repo of YAML into a live site — pages, forms, review desks, and state +machines, with a durable event-sourced record of every answer +underneath. The engine special-cases nothing: everything a site +serves is declared in its content repo, and the same build serves any +number of sites. + +Point an instance at a content repo and that repo *is* the site: +pages, copy, state graphs, and branding (`site.yaml`: title, +wordmark, and which hero the landing page opens on — the YES canvas, +a gesture-drawing canvas wired to a relay, or plain copy). Content +pushes hot-reload every running instance; instances differ only by +env vars. [uhhm.no](https://uhhm.no) +([uhhm/questions](https://project.uhhm.no/uhhm/questions)) and +[redoal.com](https://redoal.com) +([redoal/questions](https://project.uhhm.no/redoal/questions)) are +two faces of the same binary, deployed from the same release +artifact. + +It composes with the surrounding stack rather than bundling it: Gitea +hosts and serves the content, NATS JetStream stores events and +projections, Kanidm provides identity, and anything downstream +(automations, newsletters, onboarding) subscribes to the answer +stream. ## What the engine provides @@ -28,8 +47,13 @@ it special-cases none of it. lineage; `?chain=` links carry it, and self-service transitions (unsubscribe) authorize by holding one. - **Live resources** (`src/resource.rs`): content can pull a KV - bucket, Gitea starred/org repos, or any public JSON URL (SSRF - fail-closed), reshaped by a content-declared `jq` filter. + bucket, Gitea starred/org repos/releases, or any public JSON URL + (SSRF fail-closed), reshaped by a content-declared `jq` filter. +- **Input kinds as content**: a requirement's `type:` picks the + widget — plain fields, a rich-text editor, or a `gesture` drawing + canvas that can connect to a redoal relay so similar strokes echo + between visitors live. Submitted values are arbitrary JSON; the + engine records what the widget produced. - **Review desks**: any Kv resource with `transitions` renders rows with per-state action buttons and one shared confirm per alternative — owners walk records through their graphs without