Skip to content

About

The Hive page, native drone and classifier

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

53 Commits

Folders and files

Repository files navigation

hive-client

The page of the Hive, and a native drone.

The Hive is a swarm of small language models running in people's browser tabs. Anyone can open the page, watch the swarm work in a live log, ask it a question, or join it as a drone: the tab downloads a small language model and answers other people's questions with it. There is no large model behind the Hive and no inference on the server. Every answer is produced by the small models of the drones connected at that moment, and a small classifier sorts each question before it is sent out.

This repo holds everything that runs on the client side:

  • web/: the page. Plain HTML and JavaScript, no framework and no bundler. It shows the feed, sends questions, and runs the tasks the hub gives it in Web Workers.
  • swarm-classifier/: the question classifier, a small BERT-style model in Rust compiled to WebAssembly.
  • drone/: a drone without a browser, a command-line program that does what a page that joined the swarm does.

The hub is the sibling repo, hive-server. You need both to see the Hive work.

Architecture

flowchart LR
    subgraph tab["Browser tab (this repo: web/)"]
        main["main thread<br/>feed, questions, connection"]
        cw["classifier worker<br/>WebAssembly"]
        lw["language model worker<br/>lean engine"]
    end
    native["Native drone (this repo: drone/)<br/>lean engine, Metal / Vulkan / CPU"]
    hub["hub (hive-server)<br/>routes, never runs a model"]

    main <-- "WebSocket" --> hub
    native <-- "WebSocket" --> hub
    main --> cw
    main --> lw
Loading

The hub, the browser drones, the native drone, the classifier and the archivist fit together like this:

  • The hub (hive-server) routes messages and keeps no model. It broadcasts a public log that every page shows as its feed.
  • A browser drone is a page that has joined. It downloads a language model into the browser cache and loads it in a Web Worker. It registers as an answerer. It also registers as a tagger once its classifier is loaded.
  • The classifier runs in its own Web Worker. For a question it returns a label (swarm, time, weather or other) and a score. The hub uses the label to decide how to handle the question.
  • The native drone is an answerer without a page. It does the same work on the same engine commit, with a GGUF model from disk.
  • The archivist is not a program of its own. It is a task type: after an answer is delivered, the hub asks one answerer to summarize the exchange in five words. Any drone that answers can do it.

A question travels like this: the page sends it to the hub, the hub has a tagger classify it, up to three answerers write candidate answers, another answerer picks the best, and the hub returns it to the page, while the archivist task runs afterwards. Visitors who only watch see ids, routing and timings in the feed and never the text of other people's questions. The hub's README describes the flow step by step.

The model and the engine

The language model runs on the lean engine from llm-web, built to WebAssembly and WGSL shaders. The engine is picked by what the device can do: WebGPU if the browser grants an adapter; otherwise CPU threads if the page is cross-origin isolated and has more than one hardware thread; otherwise a single CPU thread. If one fails to start, the next is tried. Nothing is chosen by device name or tuned per device.

Machine Model
Browser with WebGPU, or a native drone with a GPU SmolLM2-1.7B-Instruct, Q4_0
No WebGPU, or a CPU-only native drone Qwen2.5-0.5B-Instruct, Q4_0

Models are downloaded from pinned Hugging Face revisions and the browser keeps them in its cache. The classifier is downloaded the same way, from a pinned revision, and is checked by SHA-256 when fetched for local work.

Principles

  1. Small on purpose. What looks missing is left out by design: no database, no accounts, no search, no framework, no settings beyond the few listed here.
  2. No special cases. One rule for every message and every peer.
  3. The hub stays dumb. The page only shows the feed, sends questions and runs tasks. Routing is the hub's job.
  4. Every job is a task for the swarm. Answering, judging, classifying, archiving and telling the time are tasks the hub sends to drones.
  5. The public sees the transport, never the words. The feed shows ids, routing and timings. What you type and the answer you get are shown only to you.
  6. The page is a log feed. Watching it should feel like watching infrastructure: plain HTML and JavaScript, no components, no chat bubbles.
  7. Drones pick their engine by what the device can do, never by device name. Models run in Web Workers, never on the main thread.
  8. Anything that came from the network is shown with textContent, never innerHTML.
  9. Simple words in logs, messages, comments and docs.

Planned and not in this repo: the swarm's memory, where drones file answers into a store and later questions retrieve from it, and a larger classifier that routes each question into one of a grid of topic cells.

Run it

The page is plain static files in web/. You need Rust with the wasm32-unknown-unknown target, wasm-pack, git and Python 3. Building the lean engine also needs what the scripts/build.sh of llm-web needs: wasm-bindgen-cli and a pinned nightly with rust-src, listed in scripts/build-lean.sh.

Start the hub first (cargo run --release in hive-server), then:

./scripts/fetch-models.sh        # the classifier files, pinned and checksummed (web/models/)
./scripts/build-lean.sh          # the lean engine, built from a pinned public llm-web commit (web/lean/)
(cd swarm-classifier && wasm-pack build --target web --release --out-dir ../web/classifier --out-name swarm_classifier)
./scripts/serve.py               # serves web/ on http://localhost:5173

Open http://localhost:5173/?hub=ws://localhost:8080/ws. serve.py only serves files, with the cross-origin isolation headers the page needs for CPU threads. It does not forward /ws. A hub on another local port works the same way with ?hub=ws://localhost:PORT/ws. Only a hub on the same machine can be chosen with ?hub=, so a link cannot send visitors to someone else's hub.

Click join on the page to become a drone. Open the page in several tabs, or run the native drone, to get a swarm.

Native drone

./drone/fetch-model.sh           # smollm2 (default) or qwen05 for CPU-only machines; pinned, checksummed (drone/models/)
(cd drone && cargo build --release)
(cd drone && HUB_URL=ws://127.0.0.1:8080/ws ./target/release/hive-drone)

The drone answers tasks only. Classifying stays with the page. SmolLM2-1.7B needs a GPU (Metal or Vulkan) to answer within the hub's task timeout; on a CPU-only machine use ./drone/fetch-model.sh qwen05. When the hub goes away, the drone connects again every 5 seconds and keeps its model loaded.

Deploy

Publish web/ on a host that sends the headers in web/_headers (Cloudflare Pages reads that file). Leave web/models/ out of a deploy; the page fetches the classifier from Hugging Face. The hub must be reachable over wss:// from an https page, and its HUB_ALLOWED_ORIGINS must include the page's origin.

Every URL the page loads carries a build tag (?v=...), because a rebuilt file stays invisible to browsers until its URL changes. After changing any file in web/, change the tag everywhere; it is the same string in web/index.html and web/src/. The lean engine files have their own tag, the commit in web/lean/COMMIT.

Configuration

Setting Where Default Meaning
HUB_URL web/src/config.js empty The hub the page connects to. Empty means /ws on the page's own host.
CLASSIFIER_MODEL_BASE web/src/config.js a pinned Hugging Face revision Where the classifier files come from. Set it to ../models to use the local copy from fetch-models.sh.
?hub= page URL none A hub on this machine, for development.
HUB_URL native drone, environment ws://127.0.0.1:8080/ws The hub's WebSocket. Plain ws:// only.
MODEL_DIR native drone, environment models Directory with exactly one .gguf plus tokenizer.json and tokenizer_config.json. The drone registers the model's name and size from the file name.
PEER_ID native drone, environment drone_ and 12 random hex digits The drone's name in the feed.

Check it

scripts/ci.sh

fetches the classifier, builds the lean engine, runs the classifier's tests, runs the native drone's tests and clippy and checks that it pins the same lean commit as the page, builds the classifier's WebAssembly, and checks that the worker loads lean with its commit as build tag, that every loading URL has one build tag, and that every file the page names exists.

Code

  • web/src/main.js: the feed, the connection to the hub, joining.
  • web/src/worker.js: the language model.
  • web/src/classifier.js, web/src/classifier-worker.js and swarm-classifier/: the classifier.
  • web/src/hardware.js: what the device can do.
  • web/src/config.js: the hub address and the classifier files' location.
  • web/lean/: the lean engine, built by scripts/build-lean.sh (not committed).
  • drone/src/main.rs: the native drone.

Licence and citation

Apache-2.0. See LICENSE and NOTICE. If you use or build on this software, cite it with the metadata in CITATION.cff (GitHub shows a "Cite this repository" button from it).

About

The Hive page, native drone and classifier

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages