Skip to content

Latest commit

 

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

chat-oh

Kurz auf Deutsch. Dies ist der Bürger-Chat der Plattform OpenHelvetia: ein oberflächenfreier Rust-Kern, dessen Gesprächsschleife eine Zustandsmaschine ist, mit der deterministischen Prüfung jedes Zitats, jedes Schlüssels und jeder Zahl gegen das, was die Werkzeuge geliefert haben, einer Anbietertabelle, die den Schlüssel des Besuchers nur an den gewählten Anbieter sendet, und einer Dioxus-Hülle, die zu WebAssembly kompiliert wird. Das ist der Chat, der auf openhelvetia.swiss läuft. Der Rest dieser Seite ist auf Englisch.

chat-oh is the citizen chat of the association OpenHelvetia. It answers questions of Swiss federal law and political data through the platform's gateway (mcp-gateway-oh), with a model the visitor chooses and pays for with their own key. The platform hosts no model; it keeps the grounding. After the model has answered, three deterministic checks run as string comparisons against the record of what the tools delivered: every cited key is matched against the record, every quotation is normalised for spacing and typography and searched character by character in the delivered texts, every figure is looked up among the delivered values. The results are shown, the answer stays untouched, the reader decides.

What it is not. It is not legal advice, and it does not judge whether a conclusion follows from a norm; that is not a string comparison, and the module says so. A model sentence with no tool behind it renders as unverified, computed from the exchange's own record, never claimed by the model.


Contents

  1. Before you start
  2. Run the tests in five minutes
  3. Build the browser bundle
  4. Host it on a page
  5. How the chat keeps its promises
  6. The providers
  7. What is in this repository
  8. How it is verified
  9. When something does not work
  10. Where this repository comes from
  11. Contributing, security, licence

1. Before you start

Need Why How to get it
Rust, stable (rustc and cargo) the chat is a Rust program, core and shell alike https://rustup.rs — one command, then open a new terminal and check with cargo --version
Git to clone this repository macOS: Xcode command-line tools (xcode-select --install); Linux: your package manager; Windows: https://git-scm.com
About 3 GB of disk and some minutes the first build compiles the core, the shell and the API spine the tests start; later builds take seconds
For the browser bundle only: the wasm32-unknown-unknown target and the wasm-bindgen CLI the shell is compiled to WebAssembly and post-processed into a JavaScript module rustup target add wasm32-unknown-unknown and cargo install wasm-bindgen-cli --version 0.2.127 --locked (the version app/web/Cargo.lock resolves; the build script checks it)

Network: the tests need none. They start a real API spine on a local port and never mock a transport; the provider tests read the source, not the providers.

2. Run the tests in five minutes

git clone https://github.com/OpenHelvetia/chat-oh.git
cd chat-oh
cargo test --locked --manifest-path app/core/Cargo.toml
cargo test --locked --manifest-path app/web/Cargo.toml
cargo test --locked --manifest-path app/conformance/Cargo.toml

The core suite holds the exchange loop, the verification, the provider table and the transcript; the shell suite renders the components to strings and asserts on the markup; the conformance suite is the construction gate (§5). All three end with every test passing.

3. Build the browser bundle

bash tools/build-chat-wasm.sh --out build/chat-bundle

The script compiles app/web's oh-chat binary for wasm32-unknown-unknown, runs wasm-bindgen --target web, and writes the pair oh-chat-<hash>.js and oh-chat-<hash>.wasm under one hash, so a page never serves a new glue against an old module. build/chat-bundle/manifest.json names the two files.

4. Host it on a page

The bundle mounts into an element the page provides; the gateway endpoint and the language are attributes of that element, nothing is compiled in. A minimal page, with the two file names from manifest.json:

<div id="chat-app" data-endpoint="https://mcp.openhelvetia.swiss" data-lang="de">
  <p class="chat-loading"></p>
</div>
<script type="module">
  import init from "./oh-chat-<hash>.js";
  init({ module_or_path: "./oh-chat-<hash>.wasm" }).then(() => {
    document.querySelector("#chat-app .chat-loading")?.remove();
  });
</script>

Point data-endpoint at a gateway: the association's live one, or a local mcp-gateway-oh started with --http 8781 (http://127.0.0.1:8781). data-lang is de or en. Serve the files over HTTP (a file:// page cannot load a WebAssembly module); any static server does.

The same bundle also mounts as the site-wide assistant into an element with id assistant-app, which additionally carries data-path, data-title and data-sitemap; the page at openhelvetia.swiss uses both mounts from one download.

5. How the chat keeps its promises

  • The loop is a state machine (app/core/src/chat/conversation.rs). Every honesty rule is a transition with a test: an empty turn is not sent, a capped list says what it kept of how many, a session that runs out of budget ends in an answer that names the bound rather than padding one.
  • The check is deterministic (app/core/src/chat/verify.rs). Three stages after the answer, keys, quotations, figures, each a string comparison against the record of delivered sources; three states for a quotation, found, read but absent, nothing read. The middle state is paraphrase presented as quotation, the failure a reader is least able to see. Nothing rewrites the answer.
  • Sources are listed so a reader can open the original; for a statistics source the rail carries the command that reproduces the figures.
  • The construction rule has a gate (app/conformance). The shell reaches the backend only through the core, and the core only through the generated API client; no server function may exist that a machine handed the well-known URL could not find. The gate reads the dependency graphs and scans the sources, and it is proven to fire.
  • The core is UI-free, and the gate reads the core's own manifest to keep it so: every shell renders the same view models its way.

6. The providers

The visitor chooses a provider and pastes their own key; the key stays in the browser and goes to that provider's origin and to no other. app/core/tests/chat_key.rs enforces this by scanning the source for URL literals: exactly the origins in the provider table, and one construction of the Authorization header. The table (app/core/src/chat/provider.rs) holds four providers today: Anthropic, OpenAI, Google, and a Swiss host offering the Apertus models, which the browser reaches through the gateway's relay door because the host refuses browser calls. The relay returns the upstream's answer whole and keeps nothing.

7. What is in this repository

Path What it is
app/core/ the UI-free core: the loop, the verification, the provider table, the transcript, the view models; the generated API client
app/web/ the Dioxus shell: the chat page, the site-wide assistant, the browser bundle entry point
app/conformance/ the construction gate
skills/own/ the two research skills the core embeds, for federal law and for political data, in English and German
tools/build-chat-wasm.sh the bundle build
registry/api/, profile/auth/, profile/org/ the API spine the core talks to and the tests start; published on their own as api-spine-oh
registry/entries/, mcp/gateway/tool-inventory.json what the spine serves and the tools the skills are held against
REUSE.toml, LICENSES/, publiccode.yml licence metadata by path and the machine-readable description

8. How it is verified

The core's tests (app/core/tests/): chat_loop.rs for the state machine and the budget bound, chat_verify_counted.rs for the three-stage check, chat_key.rs for the one-origin rule, chat_cap.rs and chat_empty_turn.rs for the honesty transitions, chat_transcript.rs and chat_surface.rs for what reaches the reader, chat_skill.rs for the embedded skills against the gateway inventory, platform.rs against a really running spine. The shell's tests (app/web/tests/) render every component to a string and assert on it, among them that the session token never reaches the markup. The conformance suite plants a forbidden idiom and shows the gate fires.

9. When something does not work

You see What it means What to do
error: package … requires rustc 1.xx your Rust is too old rustup update stable
wasm-bindgen CLI is … but app/web resolves the crate at … glue and crate must be the same version install the version the message names
the page shows only the loading line the module failed to load or the endpoint is unreachable open the browser console; check data-endpoint and that the files are served over HTTP
a provider answers with a CORS error that provider refuses browser calls use it through a gateway started with --relay <name>

Where this repository comes from

The association develops all its modules in one corpus, on its own GitLab, where every change runs through a gate (formatting, Clippy without warnings, all tests, seal and drift checks). This repository is assembled from that corpus by the publication lane, tested in the assembled tree, and pushed here.

This copy was published from corpus commit 835819c on 2026-09-07.

Contributing, security, licence

  • Issues here are welcome: a wrong result, a missing case, an unclear sentence in this README. Please include the command you ran and what came back.
  • Changes go through the corpus and arrive here with the next publication; a pull request here is read and carried over by hand.
  • Security reports, in confidence: admin@openhelvetia.swiss. The association answers within a working week.
  • Licence: the app crates and the auth profile declare MIT, the API crates Apache-2.0 (LICENSE, attribution in NOTICE, both texts under LICENSES/). REUSE.toml names the licence of every path.

About

The OpenHelvetia citizen chat: a UI-free Rust core whose exchange loop is a state machine, with the deterministic three-stage check of every quotation, key and figure against what the tools delivered, a provider table that sends the visitor's key to the chosen provider only, and a Dioxus shell compiled to WebAssembly

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages