Repository navigation
Expand file tree
/
Copy pathindex.html
More file actions
486 lines (465 loc) · 35.1 KB
/
Copy pathindex.html
File metadata and controls
486 lines (465 loc) · 35.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
---
layout: default
title: ai-process — open-source infrastructure for production LLMs
description: Keystone (KTMS) is a self-hosted LLM gateway in Go. With light-tokenmeter and ktms-admin it keeps your vendor keys, routes by effort, falls back, caps spend and shows who burns the tokens. Apache-2.0-licensed.
---
<section class="hero">
<div class="container hero-grid">
<div>
<span class="pill"><span class="dot"></span>Open source · Apache 2.0 · Written in Go</span>
<h1>One switchboard for <span class="grad-text">every LLM</span> you run</h1>
<p class="lead">Keystone. The Model Switchboard. (KTMS) is a self-hosted LLM gateway: it keeps your vendor keys, routes each request by effort, falls back when a provider fails and caps spend. light-tokenmeter shows who burns the tokens. ktms-admin puts it all in your browser.</p>
<div class="hero-cta">
<a class="btn btn-primary" href="#demo">See it route a request</a>
<a class="btn btn-ghost" href="/docs/">Read the docs</a>
</div>
<div class="hero-meta">
<span><svg viewBox="0 0 24 24"><path d="M20 6 9 17l-5-5"/></svg>No enterprise edition</span>
<span><svg viewBox="0 0 24 24"><path d="M20 6 9 17l-5-5"/></svg>gRPC + OpenAI-compatible HTTP</span>
<span><svg viewBox="0 0 24 24"><path d="M20 6 9 17l-5-5"/></svg>amd64 & arm64 images</span>
</div>
</div>
<div class="install-card" data-install>
<div class="install-head">
<b>Try Keystone in a minute</b>
<span>free · Apache 2.0</span>
</div>
<div class="install-tabs" role="tablist" aria-label="Ways to install">
<button role="tab" aria-selected="true" data-way="docker">Docker</button>
<button role="tab" aria-selected="false" data-way="claude">Claude Code</button>
<button role="tab" aria-selected="false" data-way="k8s">Kubernetes</button>
<button role="tab" aria-selected="false" data-way="assistant">Cursor · Codex</button>
<button role="tab" aria-selected="false" data-way="node">GPU node</button>
<button role="tab" aria-selected="false" data-way="cli">CLI</button>
</div>
<div class="install-panel" data-panel-way="docker">
<div class="install-cmd"><code><span class="p">$</span> curl -fsSL <span class="nw">https://ai-process.dev/install.sh</span> | sh</code><button class="install-copy" data-copy-cmd>Copy</button></div>
<ul class="install-steps">
<li>Runs Keystone, Postgres and Redis from published images: no clone, no build</li>
<li>Generates its keys and asks for one OpenAI, Anthropic or Gemini key</li>
<li>Prints your first request. Needs Docker; <code>--uninstall</code> removes it all</li>
</ul>
</div>
<div class="install-panel" data-panel-way="claude" hidden>
<div class="install-cmd"><code><span class="p">$</span> claude plugin marketplace add ai-process/ktms && claude plugin install ktms@ai-process</code><button class="install-copy" data-copy-cmd>Copy</button></div>
<ul class="install-steps">
<li><code>/ktms:assess</code> looks at your system and says where Keystone fits</li>
<li><code>/ktms:deploy</code> writes Compose or Helm for the whole stack</li>
<li><code>/ktms:integrate</code> moves a service's LLM calls onto it. <a href="/docs/skills/">All skills →</a></li>
</ul>
</div>
<div class="install-panel" data-panel-way="k8s" hidden>
<div class="install-cmd"><code><span class="p">$</span> helm install ktms oci://ghcr.io/ai-process/charts/ktms --set secrets.existingSecret=ktms-secrets</code><button class="install-copy" data-copy-cmd>Copy</button></div>
<ul class="install-steps">
<li>The chart is published with every release and runs the image of the same version</li>
<li>Create the <code>ktms-secrets</code> secret first: database URL, keyring, admin key</li>
<li><a href="/docs/ktms/#helm-chart-kubernetes">Helm guide and production checklist →</a></li>
</ul>
</div>
<div class="install-panel" data-panel-way="assistant" hidden>
<div class="install-cmd"><code>Open “Deploy the stack”, copy the prompt, paste it into your assistant</code><a class="install-copy" href="/docs/prompts/deploy-stack/">Open</a></div>
<ul class="install-steps">
<li>Ready prompts for Cursor, Codex, Copilot or any chat assistant</li>
<li>Deploy, integrate a service, configure routing, add a GPU node, migrate</li>
<li><a href="/docs/prompts/">All prompts →</a></li>
</ul>
</div>
<div class="install-panel" data-panel-way="node" hidden>
<div class="install-cmd"><code><span class="p">$</span> curl -fsSL <span class="nw">https://raw.githubusercontent.com/ai-process/ktms/main/scripts/install-node.sh</span> | sh -s -- --proxy ktms.example.com --node-id gpu-1 --model qwen3:8b</code><button class="install-copy" data-copy-cmd>Copy</button></div>
<ul class="install-steps">
<li>Runs Ollama and the node agent on a Mac or a GPU box as user services</li>
<li>Keystone sends work there first and falls back to the cloud when it is busy</li>
<li><a href="/docs/quickstart-local-node/">Node quick start →</a></li>
</ul>
</div>
<div class="install-panel" data-panel-way="cli" hidden>
<div class="install-cmd"><code><span class="p">$</span> go install github.com/ai-process/ktms/cmd/ktms@latest</code><button class="install-copy" data-copy-cmd>Copy</button></div>
<ul class="install-steps">
<li>Contexts and keys, configuration as a file with plan and apply</li>
<li><code>ktms route explain</code> shows which model a request would get and why</li>
<li><a href="/docs/cli/">Command-line tools →</a></li>
</ul>
</div>
</div>
</div>
</section>
<section class="section" id="architecture" style="padding-top:24px;padding-bottom:64px">
<div class="container">
<div class="section-head center reveal">
<span class="eyebrow">How it fits together</span>
<h2>One endpoint in. <span class="grad-text">Every model out.</span></h2>
<p>Your services keep a single address and a scoped <code>ktms_…</code> key. Everything behind it can change without a deploy.</p>
</div>
<div class="flow reveal">
<div class="flow-col">
<div class="flow-box"><b>Your services</b><small>gRPC clients, typed Protobuf</small></div>
<div class="flow-box"><b>OpenAI SDKs & agents</b><small>base_url → :8080/v1</small></div>
</div>
<div class="flow-arrow"></div>
<div class="flow-box flow-core">
<b class="grad-text">KTMS</b>
<div><small>Postgres for config & encrypted keys · Redis for limits</small></div>
<div class="chips"><span>vendor keys</span><span>effort rules</span><span>fallbacks</span><span>RPM & budgets</span><span>batches</span><span>node pool</span></div>
</div>
<div class="flow-arrow"></div>
<div class="flow-col">
<div class="flow-box"><b>Cloud vendors</b><small>OpenAI · Anthropic · Gemini · DeepSeek</small></div>
<div class="flow-box"><b>Your GPUs</b><small>Ollama · vLLM · llama.cpp</small></div>
</div>
</div>
<div class="flow-down reveal">↓ one usage event per call</div>
<div class="flow-bottom reveal">
<div class="flow-box"><b>light-tokenmeter</b><small>who spends, on what, and is it worth it</small></div>
<div class="flow-box"><b>ktms-admin</b><small>models, rules, keys, playground, spend</small></div>
</div>
</div>
</section>
<section class="section" id="keys" style="padding-top:24px;padding-bottom:64px">
<div class="container">
<div class="section-head center reveal">
<span class="eyebrow">One key per service</span>
<h2>Stop scattering vendor keys.<br><span class="grad-text">Hand out one per service.</span></h2>
<p>Every service gets a single <code>ktms_…</code> key. Vendor credentials stay encrypted inside Keystone, and the admin decides which vendors, models and limits each key gets, without touching the service.</p>
</div>
<div class="keys">
<div class="keys-card before reveal">
<div class="card-title"><span>Without Keystone</span><span class="tag-bad">keys everywhere</span></div>
<div class="env"><b>billing/.env</b>OPENAI_API_KEY=sk-proj-…<br>ANTHROPIC_API_KEY=sk-ant-…</div>
<div class="env"><b>search/.env</b>OPENAI_API_KEY=sk-proj-…<br>GEMINI_API_KEY=AIza…</div>
<div class="env"><b>support-bot/.env</b>ANTHROPIC_API_KEY=sk-ant-…<br>DEEPSEEK_API_KEY=sk-…</div>
<p class="keys-note">Which service holds which key? Who can call the expensive model? A rotation means a redeploy everywhere.</p>
</div>
<div class="keys-card after reveal d1">
<div class="card-title"><span>With Keystone</span><span class="tag-good">one key each</span></div>
<div class="env"><b>billing/.env</b>KTMS_API_KEY=ktms_…</div>
<div class="env"><b>search/.env</b>KTMS_API_KEY=ktms_…</div>
<div class="env"><b>support-bot/.env</b>KTMS_API_KEY=ktms_…</div>
<div class="policy">
<div class="policy-row head"><span>key</span><span>vendors</span><span>models · limits</span></div>
<div class="policy-row"><span>billing</span><span>openai</span><span>gpt-mini · 120 rpm</span></div>
<div class="policy-row"><span>search</span><span>google, local</span><span>gemini-flash · 200k tpm</span></div>
<div class="policy-row"><span>support-bot</span><span>anthropic</span><span>claude · any</span></div>
</div>
<p class="keys-note">Set in ktms-admin or over the Admin API. Rotate a vendor key once, revoke a service key in one call.</p>
</div>
</div>
</div>
</section>
<section class="section" id="demo" style="padding-top:24px;padding-bottom:64px">
<div class="container hero-grid">
<div class="reveal">
<span class="eyebrow">Once it runs</span>
<h2>Every request, <span class="grad-text">routed for you</span></h2>
<p class="demo-text">Your code asks for an effort, not a model. Keystone picks the model from your rules, keeps cheap work on your own GPUs and moves to the next vendor when one fails. The response headers say who answered and why.</p>
<ul class="demo-points">
<li><code>auto:high</code> goes to the strongest model in the chain</li>
<li><code>auto:low</code> stays on a local node: zero cloud tokens</li>
<li>a vendor 503 becomes a fallback the caller never sees</li>
</ul>
</div>
<div class="terminal reveal d1" aria-label="Requests through Keystone and how they were routed">
<div class="terminal-bar"><i></i><i></i><i></i><span>~/your-service</span></div>
<div class="terminal-body" data-terminal></div>
</div>
</div>
</section>
<section class="stats">
<div class="container">
<div class="stats-grid reveal">
<div class="stat"><b class="grad-text">~7 MiB</b><span>idle memory</span></div>
<div class="stat"><b class="grad-text">~0.6 ms</b><span>added latency, p50</span></div>
<div class="stat"><b class="grad-text">≥4,000</b><span>RPS on 4 vCPU</span></div>
<div class="stat"><b class="grad-text">~170 ms</b><span>from start to first answer</span></div>
</div>
<p class="stats-note">KTMS, measured in October 2026 on GCP <code>c3-standard-4</code> against a mock upstream. Compiled proxies such as agentgateway, APISIX and Kong add even less latency; KTMS plays in their league and adds the features. <a href="https://github.com/ai-process/ktms#resource-footprint">Method and full numbers →</a></p>
<div class="adoption reveal">
<div class="adopt"><b>{{ site.data.stats.docker_pulls | divided_by: 100 | divided_by: 10.0 }}k</b><span>Docker pulls of Keystone, light-tokenmeter and ktms-admin</span></div>
<div class="adopt"><b>{{ site.data.stats.latest_release }}</b><span>latest release, {{ site.data.stats.latest_release_date | date: "%b %-d, %Y" }}</span></div>
<div class="adopt"><b>{{ site.data.stats.releases }}</b><span>releases of Keystone so far</span></div>
</div>
<details class="methods reveal">
<summary>Every installation method · sources</summary>
<div class="methods-body">
<table>
<thead><tr><th>Method</th><th>Command or artifact</th><th>Guide</th></tr></thead>
<tbody>
<tr><td>Try locally</td><td><code>curl -fsSL https://ai-process.dev/install.sh | sh</code></td><td><a href="https://github.com/ai-process/ktms/blob/main/scripts/install.sh">script</a></td></tr>
<tr><td>Docker image</td><td><code>coyl/ktms</code> (amd64, arm64; <code>:audio</code> with ffmpeg)</td><td><a href="/docs/ktms/#docker-image">docs</a></td></tr>
<tr><td>Kubernetes</td><td><code>oci://ghcr.io/ai-process/charts/ktms</code></td><td><a href="/docs/ktms/#helm-chart-kubernetes">docs</a></td></tr>
<tr><td>From source</td><td><code>git clone https://github.com/ai-process/ktms</code> + <code>docker compose up --build</code></td><td><a href="/docs/ktms/#building-from-source">docs</a></td></tr>
<tr><td>Claude Code</td><td><code>claude plugin install ktms@ai-process</code></td><td><a href="/docs/skills/">docs</a></td></tr>
<tr><td>Other assistants</td><td>ready prompts</td><td><a href="/docs/prompts/">docs</a></td></tr>
<tr><td>GPU node</td><td><code>scripts/install-node.sh</code> (Ollama + node agent)</td><td><a href="/docs/quickstart-local-node/">docs</a></td></tr>
<tr><td>CLI and MCP</td><td><code>go install github.com/ai-process/ktms/cmd/ktms@latest</code>, <code>coyl/ktms-cli</code></td><td><a href="/docs/cli/">docs</a></td></tr>
<tr><td>Usage meter</td><td><code>coyl/light-tokenmeter</code></td><td><a href="/docs/light-tokenmeter/">docs</a></td></tr>
<tr><td>Web console</td><td><code>coyl/ktms-admin</code></td><td><a href="/docs/ktms-admin/">docs</a></td></tr>
</tbody>
</table>
<p>Pulls are the sum of Docker Hub's <code>pull_count</code> for <code>coyl/ktms</code>, <code>coyl/light-tokenmeter</code>, <code>coyl/ktms-admin</code> (with its former name <code>coyl/llm-proxy-admin</code>) and <code>coyl/ktms-cli</code>, as of {{ site.data.stats.measured }}. A pull is not an install: CI, upgrades and mirrors pull too. Releases come from the GitHub API. Both refresh daily when the site builds.</p>
</div>
</details>
</div>
</section>
<section class="section" id="products">
<div class="container">
<div class="section-head reveal">
<span class="eyebrow">Products</span>
<h2>Three small services. <span class="grad-text">One production stack.</span></h2>
<p>Use them together or one at a time. Each is a single Go binary with its own Docker image, Helm-friendly defaults and an Apache 2.0 licence.</p>
</div>
<div class="products">
<article class="product featured reveal">
<div class="product-icon"><svg viewBox="0 0 24 24"><path d="M4 6c5 0 6 6 10 6M4 18c5 0 6-6 10-6M4 12h10M14 12h6M17 9l3 3-3 3"/></svg></div>
<h3>Keystone. The Model Switchboard. (KTMS)</h3>
<div class="tag">LLM gateway · gRPC & HTTP</div>
<p>Your services talk to one endpoint. Keystone holds the vendor keys, picks the model, retries elsewhere when a vendor is down and records every call.</p>
<ul>
<li>Vendor keys encrypted in Postgres (AES-256-GCM): OpenAI, Anthropic, Gemini, DeepSeek</li>
<li>Routing rules by effort and attributes, changed live</li>
<li>RPM, daily token budgets and USD spend caps per key, user and team</li>
<li>Batches at 50% off, speech, images, typed <code>Judge</code></li>
<li>Pool of your own GPU nodes with cloud fallback</li>
</ul>
<div class="product-links">
<a href="/docs/ktms/">Docs →</a>
<a href="https://github.com/ai-process/ktms">GitHub</a>
<a href="https://hub.docker.com/r/coyl/ktms">Docker Hub</a>
</div>
</article>
<article class="product reveal d1">
<div class="product-icon"><svg viewBox="0 0 24 24"><path d="M4 20V10M10 20V4M16 20v-7M22 20H2"/></svg></div>
<h3>light-tokenmeter</h3>
<div class="tag">Usage meter · Postgres / ClickHouse</div>
<p>Your apps, or KTMS, push one event per model call. Ask who is spending your tokens, and whether they are worth it.</p>
<ul>
<li>Append-only, idempotent events: sending twice is harmless</li>
<li>Bearer tokens with write and read scopes, Prometheus metrics</li>
<li>Daily summaries with any filters and groupings</li>
<li>HTTP/JSON for dashboards, gRPC for services</li>
<li>Optional ClickHouse for large volumes, with no lost events</li>
</ul>
<div class="product-links">
<a href="/docs/light-tokenmeter/">Docs →</a>
<a href="https://github.com/ai-process/light-tokenmeter">GitHub</a>
<a href="https://hub.docker.com/r/coyl/light-tokenmeter">Docker Hub</a>
</div>
</article>
<article class="product reveal d2">
<div class="product-icon"><svg viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="14" rx="2"/><path d="M3 9h18M8 21h8M7 13h4M7 15.5h7"/></svg></div>
<h3>ktms-admin</h3>
<div class="tag">Web console · one static binary</div>
<p>Manage models, routing rules and keys, try a prompt in the playground and read the spend dashboard. No more <code>grpcurl</code>.</p>
<ul>
<li>Password, OpenID Connect, GitHub or access-proxy sign-in, read-only users</li>
<li>Greedy / worthy verdict per project, user and model</li>
<li>Works with Keystone, light-tokenmeter or both</li>
<li>UI embedded in the Go binary, no front-end build</li>
<li>Light and dark themes, usable from a phone</li>
</ul>
<div class="product-links">
<a href="/docs/ktms-admin/">Docs →</a>
<a href="https://github.com/ai-process/ktms-admin">GitHub</a>
<a href="https://hub.docker.com/r/coyl/ktms-admin">Docker Hub</a>
</div>
</article>
</div>
</div>
</section>
<section class="section" id="features" style="padding-top:24px">
<div class="container">
<div class="section-head center reveal">
<span class="eyebrow">Why Keystone</span>
<h2>Stop hardcoding model names.<br><span class="grad-text">Let the gateway decide.</span></h2>
</div>
<div class="feature">
<div class="feature-text reveal">
<div class="feature-num">1</div>
<h3>Ask for effort, not for a model</h3>
<p>Clients send <code>effort: LOW | MEDIUM | HIGH</code> and free-form attributes. An ordered rule list maps them to a chain of models with fallbacks.</p>
<p>Rules change at runtime over the admin API or in ktms-admin. A new model release is a config change, not a refactor across a dozen services.</p>
</div>
<div class="card reveal d1">
<div class="card-title"><span>Routing rules</span><span>live</span></div>
<div class="rule"><span class="effort low">LOW</span><div class="chain"><span class="primary">local · gemma</span><em>→</em><span>gemini-flash</span></div></div>
<div class="rule"><span class="effort med">MEDIUM</span><div class="chain"><span class="primary">gpt-mini</span><em>→</em><span>deepseek-chat</span><em>→</em><span>gemini-flash</span></div></div>
<div class="rule"><span class="effort high">HIGH</span><div class="chain"><span class="primary">claude</span><em>→</em><span>gpt</span><em>→</em><span>gemini-pro</span></div></div>
<div class="rule"><span class="effort high">HIGH</span><div class="chain"><em>if</em><span>tenant=eu</span><em>→</em><span class="primary">gemini-pro</span><em>→</em><span>local · llama</span></div></div>
</div>
</div>
<div class="feature flip">
<div class="feature-text reveal">
<div class="feature-num">2</div>
<h3>Your GPUs first, the cloud when they are busy</h3>
<p>Run Ollama, vLLM or llama.cpp on your own hardware. <code>ktms-node-agent</code> registers each machine and reports VRAM, time to first token and tokens per second.</p>
<p>KTMS balances the pool with Smooth WRR, keeps hot or overloaded nodes out of rotation and, with <code>nodes_first</code>, spills over to cloud vendors only when local capacity runs out.</p>
</div>
<div class="card reveal d1">
<div class="card-title"><span>Node pool</span><span>placement: nodes_first</span></div>
<div class="node"><div class="node-name">gpu-01<small>vLLM · 48 GB</small></div><div class="bar"><i data-w="72%"></i></div><div class="node-state ok">HEALTHY</div></div>
<div class="node"><div class="node-name">gpu-02<small>Ollama · 24 GB</small></div><div class="bar"><i data-w="54%"></i></div><div class="node-state ok">HEALTHY</div></div>
<div class="node"><div class="node-name">gpu-03<small>llama.cpp · 24 GB</small></div><div class="bar"><i data-w="97%"></i></div><div class="node-state warn">SUSPECT</div></div>
<div class="node"><div class="node-name">cloud<small>OpenAI · Gemini</small></div><div class="bar"><i data-w="12%"></i></div><div class="node-state cloud">overflow</div></div>
</div>
</div>
<div class="feature">
<div class="feature-text reveal">
<div class="feature-num">3</div>
<h3>Budgets that actually stop the spend</h3>
<p>Per-model requests-per-minute and daily token budgets for every API key and every end user, plus hard USD spend caps per key, user and team, enforced in Redis before the vendor ever sees the request.</p>
<p>Non-interactive work goes through the vendors' Batch APIs at a 50% discount, with background polling and a synchronous fallback.</p>
</div>
<div class="card reveal d1">
<div class="card-title"><span>Daily token budgets</span><span>today</span></div>
<div class="budget"><div class="budget-row"><span>key · support-bot</span><span>1.2M / 3M</span></div><div class="bar"><i data-w="40%"></i></div></div>
<div class="budget"><div class="budget-row"><span>key · nightly-reports</span><span>4.1M / 5M</span></div><div class="bar"><i data-w="82%"></i></div></div>
<div class="budget"><div class="budget-row"><span>user · u-4821</span><span>200k / 200k</span></div><div class="bar"><i data-w="100%"></i></div></div>
<div class="limit-hit">u-4821 → ResourceExhausted: daily token budget reached</div>
</div>
</div>
<div class="feature flip">
<div class="feature-text reveal">
<div class="feature-num">4</div>
<h3>Know who is greedy and who is worthy</h3>
<p>KTMS sends one usage event per call to light-tokenmeter. Slice spend by project, user, action, vendor or model, with failure rates and latency next to the cost.</p>
<p>ktms-admin turns it into a verdict: the workloads that burn tokens for little value, and the ones that earn them.</p>
</div>
<div class="card reveal d1">
<div class="card-title"><span>Spend by action</span><span>last 7 days</span></div>
<div class="spend-row"><div class="who">answer-ticket<small>support-bot</small></div><div class="tok">8.4M tok</div><span class="verdict worthy">worthy</span></div>
<div class="spend-row"><div class="who">summarize-thread<small>inbox</small></div><div class="tok">2.1M tok</div><span class="verdict worthy">worthy</span></div>
<div class="spend-row"><div class="who">retry-loop<small>agent-sandbox</small></div><div class="tok">19.7M tok</div><span class="verdict greedy">greedy</span></div>
<div class="spend-row"><div class="who">rerank-all<small>search</small></div><div class="tok">11.3M tok</div><span class="verdict greedy">greedy</span></div>
</div>
</div>
</div>
</section>
<section class="section" id="assistant" style="padding-top:24px">
<div class="container">
<div class="section-head center reveal">
<span class="eyebrow">Works inside your AI assistant</span>
<h2>Your assistant does the setup. <span class="grad-text">You review it.</span></h2>
<p>The Claude Code plugin brings skills and two read-only MCP servers. Ask in plain words; the assistant reads your system, writes the configuration and shows a plan before anything changes.</p>
</div>
<div class="assist-grid">
<div class="assist-card reveal"><code>/ktms:assess</code><h4>Where does it fit?</h4><p>Surveys your services, LLM calls, deployment, secrets and GPUs, and says what Keystone changes and costs here.</p></div>
<div class="assist-card reveal d1"><code>/ktms:deploy</code><h4>Run the stack</h4><p>Compose or Helm for Keystone, light-tokenmeter and ktms-admin, with secrets from your store and a first-start runbook.</p></div>
<div class="assist-card reveal d2"><code>/ktms:integrate</code><h4>Connect a service</h4><p>One client module, an effort per call site, attribution, an error policy and tests, in your codebase.</p></div>
</div>
<div class="assist-install reveal">
<div class="install-cmd"><code><span class="p">$</span> claude plugin marketplace add ai-process/ktms && claude plugin install ktms@ai-process</code><button class="install-copy" data-copy-cmd>Copy</button></div>
<p>Also <code>/ktms:configure</code>, <code>/ktms:node</code>, <code>/ktms:migrate</code>, <code>/ktms:usage-reports</code>, <code>/ktms:production-review</code>. Cursor, Codex or Copilot? Use the same steps as <a href="/docs/prompts/">ready prompts</a>.</p>
</div>
</div>
</section>
<section class="section" id="quickstart">
<div class="container qs">
<div class="reveal">
<span class="eyebrow">Quick start</span>
<h2>One command to a <span class="grad-text">routed request</span></h2>
<div class="steps">
<div class="step"><div><h4>Start the stack</h4><p>The installer runs Keystone with Postgres and Redis from published images, generates its keys, asks for a vendor key and prints your first request. No clone, no build.</p></div></div>
<div class="step"><div><h4>Point your SDK at it</h4><p>Change <code>base_url</code> and use <code>model: "auto"</code>. Routing, fallbacks and limits come for free.</p></div></div>
<div class="step"><div><h4>Go to production</h4><p>Docker images for amd64 and arm64, a Helm chart and a production checklist are in the <a href="/docs/ktms/">KTMS docs</a>.</p></div></div>
</div>
</div>
<div class="code-tabs reveal d1" data-tabs>
<div class="tab-bar" role="tablist">
<button role="tab" aria-selected="true" data-tab="0">Install</button>
<button role="tab" aria-selected="false" data-tab="1">curl</button>
<button role="tab" aria-selected="false" data-tab="2">Python</button>
<button class="copy" data-copy>Copy</button>
</div>
<pre data-panel="0"><span class="k">curl</span> -fsSL https://ai-process.dev/install.sh | <span class="k">sh</span>
<span class="c"># or pass the vendor key up front, no prompt:</span>
OPENAI_API_KEY=sk-... <span class="k">sh</span> -c <span class="s">"$(curl -fsSL https://ai-process.dev/install.sh)"</span>
<span class="c"># options: --port, --version, --dir, --uninstall</span>
<span class="k">curl</span> -fsSL https://ai-process.dev/install.sh | <span class="k">sh</span> -s -- --help</pre>
<pre data-panel="1" hidden><span class="k">curl</span> -s http://localhost:8080/v1/chat/completions \
-H <span class="s">"Authorization: Bearer $KTMS_CLIENT_KEY"</span> \
-H <span class="s">"Content-Type: application/json"</span> \
-d <span class="s">'{"model":"auto","messages":[{"role":"user","content":"Say hi in three words."}]}'</span>
<span class="c"># the installer prints your key; it is also in ~/ktms/.env</span>
<span class="c"># response headers tell you who answered and why:</span>
<span class="c"># X-KTMS-Vendor, X-KTMS-Rule, X-KTMS-Placement</span></pre>
<pre data-panel="2" hidden><span class="k">from</span> openai <span class="k">import</span> OpenAI
client = OpenAI(base_url=<span class="s">"http://localhost:8080/v1"</span>, api_key=<span class="s">"ktms_local_…"</span>)
response = client.chat.completions.create(
model=<span class="s">"auto"</span>, <span class="c"># or "auto:low", "auto:high"</span>
messages=[{<span class="s">"role"</span>: <span class="s">"user"</span>, <span class="s">"content"</span>: <span class="s">"Say hi in three words."</span>}],
)
<span class="k">print</span>(response.choices[0].message.content)</pre>
</div>
</div>
</section>
<section class="section" style="padding-top:24px">
<div class="container">
<div class="principles">
<div class="principle reveal"><span class="eyebrow">Honest open source</span><h4>Everything is Apache 2.0</h4><p>No enterprise edition, no paywalled security features, no licence keys and no telemetry. Fork it and run it forever.</p></div>
<div class="principle reveal d1"><span class="eyebrow">Small on purpose</span><h4>Behaves like a proxy</h4><p>Static Go binaries and small images that start in milliseconds, fit scale-to-zero and leave the memory to your models.</p></div>
<div class="principle reveal d2"><span class="eyebrow">Typed contracts</span><h4>gRPC first</h4><p>Protobuf APIs with streaming and reflection for services, plus an OpenAI-compatible HTTP API for everything else.</p></div>
</div>
</div>
</section>
<section class="section" id="docs">
<div class="container">
<div class="section-head reveal">
<span class="eyebrow">Documentation</span>
<h2>Read it, run it, <span class="grad-text">ship it</span></h2>
<p>Guides for every service, from a single Docker command to a GPU pool in production.</p>
</div>
<div class="docs-grid">
<a class="doc-card reveal" href="/docs/architecture/"><h4>Architecture & deployment</h4><p>How the services fit together, with one Compose file for the whole stack.</p></a>
<a class="doc-card reveal d1" href="/docs/ktms/"><h4>Keystone (KTMS) gateway</h4><p>APIs, effort routing, fallbacks, limits, batches, speech and images.</p></a>
<a class="doc-card reveal d2" href="/docs/local-nodes/"><h4>Local GPU node pool</h4><p>Self-registering nodes, health states, Smooth WRR and cloud fallback.</p></a>
<a class="doc-card reveal" href="/docs/quickstart-local-node/"><h4>Quick start: local node</h4><p>Connect Ollama, vLLM, TGI or llama.cpp to KTMS step by step.</p></a>
<a class="doc-card reveal d1" href="/docs/light-tokenmeter/"><h4>light-tokenmeter</h4><p>Events, auth, summaries, ClickHouse, metrics and the greedy vs worthy analysis.</p></a>
<a class="doc-card reveal d2" href="/docs/ktms-admin/"><h4>ktms-admin console</h4><p>Sign-in, allowlists, gateway management and the spend dashboard.</p></a>
<a class="doc-card reveal" href="/docs/cli/"><h4>Command-line tools</h4><p>ktms and tokenmeter: configuration as a file, route explain, usage reports.</p></a>
<a class="doc-card reveal d1" href="/docs/mcp/"><h4>MCP servers</h4><p>Gateway and usage tools for Claude Code, Claude Desktop and Cursor.</p></a>
<a class="doc-card reveal d2" href="/docs/skills/"><h4>Skills & plugin</h4><p>Seven skills and both MCP servers in one Claude Code plugin.</p></a>
</div>
</div>
</section>
<section class="section" id="faq" style="padding-top:24px">
<div class="container faq">
<div class="section-head reveal">
<span class="eyebrow">A few details</span>
<h2>Questions <span class="grad-text">before you begin</span></h2>
</div>
<div class="faq-list reveal">
<details><summary>Why another gateway when LiteLLM exists?</summary><p>LiteLLM is great for Python-centric work with hundreds of niche providers. Keystone is built for production infrastructure: a gRPC API with typed contracts, effort-based routing, budgets per key and user, and a local GPU pool. On our stand it was ready in ~170 ms instead of ~32 s and used ~145× less memory at 200 RPS. Both can run side by side while you compare. <a href="https://github.com/ai-process/ktms#resource-footprint">Method and numbers →</a></p></details>
<details><summary>Is it the fastest gateway?</summary><p>No. On the same stand agentgateway, APISIX and Kong add 0.25–0.35 ms at p50 against ~0.64 ms for Keystone and use less CPU per request. Keystone is in the same sub-millisecond league and sustains ≥4,000 RPS on 4 vCPU; it differs on features and licence, not raw speed.</p></details>
<details><summary>Does it work with local models?</summary><p>Yes. Add any OpenAI-compatible server (Ollama, vLLM, llama.cpp, LocalAI) as a model, or run <code>ktms-node-agent</code> on your machines to get a self-balancing pool that reports VRAM and speed and falls back to the cloud when busy. <a href="/docs/local-nodes/">Local GPU node pool →</a></p></details>
<details><summary>Which providers are supported?</summary><p>OpenAI, Anthropic, Google Gemini, DeepSeek, Groq and any OpenAI-compatible endpoint. We go deep on each (streaming, structured output, batches at 50% off, speech, images) rather than wide on adapter count.</p></details>
<details><summary>What does the installer change on my machine?</summary><p>It writes <code>~/ktms/compose.yaml</code> and <code>~/ktms/.env</code> (generated keys, readable only by you) and starts three containers; only Keystone is published, on localhost. Run it again to add a vendor key or update; <code>--uninstall</code> removes the containers, their data and the files. <a href="https://github.com/ai-process/ktms/blob/main/scripts/install.sh">Read the script →</a></p></details>
<details><summary>What if the project is abandoned?</summary><p>Everything is Apache 2.0, tested with <code>-race</code> in CI and plain Go, with a <a href="/docs/roadmap/">public roadmap</a>. Anyone can fork it and run it indefinitely.</p></details>
<details><summary>How do the maintainers make money?</summary><p>Keystone and light-tokenmeter were built for our own infrastructure and are maintained as open-source core tools. There is no paid edition and no licence to sell.</p></details>
</div>
</div>
</section>
<section class="section" style="padding-top:0">
<div class="container">
<div class="blog-card reveal">
<div>
<span class="eyebrow">Engineering blog</span>
<h3>Notes from running LLMs in production</h3>
<p>Architecture patterns, benchmarks with the method attached, and what we learn while building KTMS.</p>
<ul class="blog-posts" data-blog-posts hidden></ul>
</div>
<a class="btn btn-ghost" href="https://blog.ai-process.dev/">Read the blog →</a>
</div>
</div>
</section>
<section class="cta">
<div class="container reveal">
<h2>Put one switchboard<br>in front of <span class="grad-text">all your models</span></h2>
<p>Free, self-hosted and Apache-2.0-licensed. Run it next to your current setup and compare.</p>
<div class="cta-install">
<div class="install-cmd"><code><span class="p">$</span> curl -fsSL <span class="nw">https://ai-process.dev/install.sh</span> | sh</code><button class="install-copy" data-copy-cmd>Copy</button></div>
</div>
<div class="hero-cta">
<a class="btn btn-ghost" href="/docs/">Read the docs</a>
<a class="btn btn-ghost" href="https://github.com/ai-process/ktms">Star Keystone on GitHub</a>
</div>
</div>
</section>