|
| 1 | +# zhub in 10 minutes — hands-on |
| 2 | + |
| 3 | +Goal: by the end of this, you have an OpenAI-compatible AI endpoint reachable from your phone. We'll go from `git clone` to "Pocket talks to your AI" in a single sitting. |
| 4 | + |
| 5 | +## Minute 0–2: install |
| 6 | + |
| 7 | +```bash |
| 8 | +git clone https://github.com/Zawwarsami16/zhub |
| 9 | +cd zhub |
| 10 | +python3 -m venv .venv && source .venv/bin/activate |
| 11 | +pip install -e '.[server,brains]' |
| 12 | +``` |
| 13 | + |
| 14 | +Sanity check: |
| 15 | + |
| 16 | +```bash |
| 17 | +python -m zhub doctor |
| 18 | +``` |
| 19 | + |
| 20 | +You should see green checks for `import zhub`, the server deps, and `cloudflared` (if installed). Brain creds will all show ✗ unless you've already set env vars — that's fine, we'll fix it next. |
| 21 | + |
| 22 | +## Minute 2–3: pick a brain |
| 23 | + |
| 24 | +You need credentials for **one** of these — any of the eight will work. Pick by what you have or want to use: |
| 25 | + |
| 26 | +| Brain | Why pick it | Get a key | |
| 27 | +|---|---|---| |
| 28 | +| **Ollama** | Free, runs on your machine, no signup | `ollama serve` then `ollama pull llama3.2` | |
| 29 | +| **Groq** | Free tier, **700 tok/s**, fastest perceived | https://console.groq.com | |
| 30 | +| **OpenAI** | Familiar, gpt-4o-mini is cheap | https://platform.openai.com | |
| 31 | +| **Anthropic** | Best reasoning (Claude) | https://console.anthropic.com | |
| 32 | +| **Together / Mistral / Cohere / Cerebras** | More options | their respective consoles | |
| 33 | + |
| 34 | +For this tutorial we'll use **Groq** because the free tier is generous and replies are instant: |
| 35 | + |
| 36 | +```bash |
| 37 | +export GROQ_API_KEY=gsk_your_key_here |
| 38 | +``` |
| 39 | + |
| 40 | +## Minute 3–4: bring it up |
| 41 | + |
| 42 | +```bash |
| 43 | +python -m zhub up |
| 44 | +``` |
| 45 | + |
| 46 | +You'll see something like: |
| 47 | + |
| 48 | +``` |
| 49 | +================================================================ |
| 50 | + brain: Groq Llama 3.3 70B |
| 51 | + URL: https://stuck-bonus-eight-spider.trycloudflare.com/me/v1 |
| 52 | + KEY: zk_BWOuFb8-Fiw8JpVjWO3hNwCaTfASE_To |
| 53 | + paste both into Pocket / openai-py / curl / Claude Desktop |
| 54 | +================================================================ |
| 55 | +``` |
| 56 | + |
| 57 | +That's it. `python -m zhub up` started: |
| 58 | +- A hub server on port 8080 |
| 59 | +- A Cloudflare quick-tunnel (the `*.trycloudflare.com` URL is reachable from anywhere) |
| 60 | +- A publisher that proxies chat to Groq |
| 61 | +- Persistent SQLite at `./zhub.db` so the `zk_` key survives restarts |
| 62 | + |
| 63 | +## Minute 4–6: try it from the command line |
| 64 | + |
| 65 | +In another terminal: |
| 66 | + |
| 67 | +```bash |
| 68 | +curl -X POST https://stuck-bonus-eight-spider.trycloudflare.com/me/v1/chat/completions \ |
| 69 | + -H "Authorization: Bearer zk_BWO..." \ |
| 70 | + -H "Content-Type: application/json" \ |
| 71 | + -d '{"messages":[{"role":"user","content":"explain zhub in one sentence"}]}' |
| 72 | +``` |
| 73 | + |
| 74 | +If that works, your AI is reachable from anywhere on the internet. |
| 75 | + |
| 76 | +## Minute 6–7: open the dashboard |
| 77 | + |
| 78 | +Open `http://localhost:8080/` in a browser. You'll see a live operator console — particles flow through the SVG every time a request lands. Stat tiles show publishers / connections / requests / p95 latency. The recent-requests feed scrolls in real time. |
| 79 | + |
| 80 | +## Minute 7–9: plug it into a chat app |
| 81 | + |
| 82 | +### Pocket (browser-based BYOK) |
| 83 | + |
| 84 | +Pocket auto-detects zhub providers. Open Pocket → settings → add provider → paste: |
| 85 | +- Base URL: `https://stuck-bonus-eight-spider.trycloudflare.com/me/v1` |
| 86 | +- API Key: `zk_BWO...` |
| 87 | + |
| 88 | +Save. The model `me` appears in the picker. Send a message — it round-trips through your hub. |
| 89 | + |
| 90 | +### Claude Desktop / Cursor / Cline |
| 91 | + |
| 92 | +Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (or your client's MCP config): |
| 93 | + |
| 94 | +```json |
| 95 | +{ |
| 96 | + "mcpServers": { |
| 97 | + "me": { |
| 98 | + "command": "python", |
| 99 | + "args": [ |
| 100 | + "-m", "zhub.mcp_server", |
| 101 | + "--hub", "https://stuck-bonus-eight-spider.trycloudflare.com", |
| 102 | + "--ai", "me", |
| 103 | + "--key", "zk_BWO..." |
| 104 | + ] |
| 105 | + } |
| 106 | + } |
| 107 | +} |
| 108 | +``` |
| 109 | + |
| 110 | +Restart Claude Desktop. Your AI shows up as a `chat` tool. Anything connected to your hub via `expose()` (next section) shows up as additional MCP tools. |
| 111 | + |
| 112 | +### openai-py |
| 113 | + |
| 114 | +```python |
| 115 | +from openai import OpenAI |
| 116 | +client = OpenAI( |
| 117 | + base_url="https://stuck-bonus-eight-spider.trycloudflare.com/me/v1", |
| 118 | + api_key="zk_BWO...", |
| 119 | +) |
| 120 | +print(client.chat.completions.create( |
| 121 | + model="me", |
| 122 | + messages=[{"role": "user", "content": "hi"}], |
| 123 | +).choices[0].message.content) |
| 124 | +``` |
| 125 | + |
| 126 | +## Minute 9–10: add a tool |
| 127 | + |
| 128 | +Your AI doesn't have a `weather_lookup` tool, but you can give it one in 30 lines. In a third terminal: |
| 129 | + |
| 130 | +```python |
| 131 | +# weather_sensor.py |
| 132 | +import asyncio |
| 133 | +from zhub import expose |
| 134 | + |
| 135 | +e = expose( |
| 136 | + name="weather-sensor", |
| 137 | + capabilities={ |
| 138 | + "weather_lookup": ( |
| 139 | + {"type": "object", "required": ["city"], |
| 140 | + "properties": {"city": {"type": "string"}}}, |
| 141 | + lambda args: {"city": args["city"], "temp_c": 14, "condition": "cloudy"}, |
| 142 | + ), |
| 143 | + }, |
| 144 | + hub_url="ws://127.0.0.1:8080", |
| 145 | + public=True, |
| 146 | +) |
| 147 | + |
| 148 | +async def main(): |
| 149 | + while not e.exposure_id: |
| 150 | + await asyncio.sleep(0.05) |
| 151 | + print(f"weather sensor exposed as {e.exposure_id}") |
| 152 | + await asyncio.Event().wait() |
| 153 | + |
| 154 | +asyncio.run(main()) |
| 155 | +``` |
| 156 | + |
| 157 | +Run it: `python weather_sensor.py`. Now the dashboard shows an "exposure" tile. Any AI on the hub can invoke this capability via `POST /exposures/<id>/invoke` with a publisher's bearer key. After Phase 1.9 capability injection lands for exposures, the AI will see it as a tool automatically. |
| 158 | + |
| 159 | +## What you have now |
| 160 | + |
| 161 | +A reusable, production-ready AI endpoint that: |
| 162 | +- Speaks OpenAI Chat Completions wire format → works in any BYOK client |
| 163 | +- Streams via SSE (Cursor / Continue compatible) |
| 164 | +- Is reachable from anywhere via Cloudflare Tunnel |
| 165 | +- Survives restarts (SQLite persistence keeps the `zk_` key stable) |
| 166 | +- Has a live operator dashboard |
| 167 | +- Routes tool calls to connected device-only "exposures" |
| 168 | +- Bridges to Claude Desktop via MCP (chat + every connected capability becomes a tool) |
| 169 | + |
| 170 | +## Next steps |
| 171 | + |
| 172 | +- [`docs/DEPLOY.md`](DEPLOY.md) — deploy this on a $5 VPS so it stays up forever |
| 173 | +- [`examples/`](../examples/README.md) — more runnable demos (federation, council, multi-brain, full-stack) |
| 174 | +- `curl <hub>/entity` — zhub's self-knowledge: routes, errors, patterns, debug recipes |
| 175 | +- `python -m zhub status <hub-url>` — pretty-print any remote hub's state |
| 176 | + |
| 177 | +## Common pitfalls |
| 178 | + |
| 179 | +| Symptom | Fix | |
| 180 | +|---|---| |
| 181 | +| `python -m zhub up` says "no brains available" | Set one of the env vars in step 2 | |
| 182 | +| Cloudflared tunnel didn't start | `apt install cloudflared` (or skip with `--no-tunnel`) | |
| 183 | +| Pocket says "couldn't auto-detect" | Make sure your URL ends with `/v1` (zhub provider matches that pattern) | |
| 184 | +| `zk_` key changed after restart | You're missing `--db ./zhub.db` flag on the hub. `zhub up` sets it by default | |
| 185 | +| Browser dashboard is blank | Check `http://localhost:8080/api/dashboard` returns JSON. If not, check `python -m zhub doctor` | |
0 commit comments