kavach-ai 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,168 @@
1
+ # Kavach
2
+
3
+ Your agents, your data, your machine.
4
+
5
+ ```bash
6
+ npm install -g kavach-ai
7
+ kavach install model # free local model, ~1.5GB
8
+ kavach open # studio at http://localhost:5181
9
+ ```
10
+
11
+ The package is `kavach-ai`; the command it installs is `kavach`. (`kavach` on
12
+ npm belongs to an unrelated project.)
13
+
14
+ The binary ships inside a small per-platform package that npm selects
15
+ automatically, so there is no download step and nothing to fetch outside the
16
+ registry — it installs cleanly behind a corporate proxy.
17
+
18
+ No account needed for the free tier. The model runs on your CPU, works offline,
19
+ and costs nothing — forever.
20
+
21
+ ## What runs where
22
+
23
+ Kavach is a **hosted control plane** with a **local data plane**. The split is
24
+ the point of the product:
25
+
26
+ | | Where it lives |
27
+ |---|---|
28
+ | Chat sessions and messages | your machine |
29
+ | Knowledge base — files, chunks, embeddings | your machine |
30
+ | Agents you write, and their prompts | your machine |
31
+ | Automations and workflow definitions | your machine |
32
+ | Run history, traces, outputs | your machine |
33
+ | Your model API keys | your machine, encrypted at rest |
34
+ | Login, entitlement, billing | Kavach |
35
+ | Licensed marketplace templates | Kavach |
36
+
37
+ Your documents are chunked, embedded, and searched locally. They are never
38
+ uploaded. Kavach could not produce them under subpoena, because Kavach never
39
+ had them. The client is never issued a credential that can read Kavach's
40
+ database — the control-plane API has no endpoint that returns tenant content,
41
+ so a stolen token grants access to nothing.
42
+
43
+ ## Cost tiers
44
+
45
+ The composer's toggle picks how a turn is served:
46
+
47
+ - **free** — the bundled local model. Zero cost, works with the network
48
+ unplugged, needs no login.
49
+ - **auto** — scores the task, then routes to the cheapest connected model that
50
+ can handle it. The default.
51
+ - **paid** — always the strongest model you have connected.
52
+
53
+ `auto` explains itself: hover the model chip to see which model answered and
54
+ why.
55
+
56
+ ## Commands
57
+
58
+ ```
59
+ kavach open start the daemon and open the studio
60
+ kavach serve start the daemon without opening a browser
61
+ kavach install model download the local model that fits this machine
62
+ kavach install whisper add local speech-to-text (~148MB)
63
+ kavach install router add the routing model used by `auto` (~500MB)
64
+ kavach install engine the inference engine only, without a model
65
+ kavach model list show the models this machine can run
66
+ kavach model use <id> switch local models
67
+ kavach doctor check the install and report anything wrong
68
+ kavach status show entitlement and configuration
69
+ kavach login link this install to a Kavach account
70
+ ```
71
+
72
+ ## Local models
73
+
74
+ `kavach install model` probes the machine and picks:
75
+
76
+ | ID | Model | Download | Needs |
77
+ |---|---|---|---|
78
+ | `qwen3.5-2b` | Qwen3.5-2B, Opus-distilled | 1.5GB | 3GB free RAM, 2 cores |
79
+ | `qwen3.5-4b` | Qwen3.5-4B v2, Opus-distilled | 2.6GB | 6GB free RAM, 4 cores |
80
+
81
+ Selection is deliberately conservative and judged on *available* memory, not
82
+ installed: a machine with 16GB installed and 1GB free will OOM exactly like a
83
+ 2GB machine. If the machine later has room for a better model, `kavach doctor`
84
+ says so.
85
+
86
+ These are small models. They are good at chat, summarisation, and extraction,
87
+ and they will visibly struggle with long tool-calling agent runs. That is what
88
+ `auto` is for.
89
+
90
+ ## How `auto` decides
91
+
92
+ By default `auto` scores a request with a built-in heuristic: deterministic,
93
+ offline, no download, no added latency.
94
+
95
+ An optional routing model (Qwen3.5-0.8B plus the `acrouter` LoRA) scores more
96
+ accurately. It is **not** a download — upstream publishes the adapter as
97
+ safetensors against a safetensors base, and llama.cpp needs GGUF for both, so
98
+ building it is a one-time local conversion. `kavach install router` prints the
99
+ exact commands.
100
+
101
+ Routing never fails closed: if the model is absent, cold, or slow, `auto`
102
+ falls back to the heuristic. Failing to decide which model answers would mean
103
+ not answering at all.
104
+
105
+ ## Speech to text
106
+
107
+ `kavach install whisper` adds whisper.cpp. Prebuilt engines are not published
108
+ for every platform; where none exists the command says so and prints the two
109
+ commands that build it, rather than reporting success and leaving you with a
110
+ microphone button that fails.
111
+
112
+ Dictated audio is transcribed on your machine and never leaves it — a voice
113
+ note is exactly the kind of content this architecture exists to keep local. The
114
+ transcriber starts on first use, so installs that never touch the microphone
115
+ pay nothing for it.
116
+
117
+ ## Trial and billing
118
+
119
+ The trial is **7 days**, and the clock lives on Kavach's servers keyed to your
120
+ installation — reinstalling or deleting `~/.kavach` does not restart it.
121
+
122
+ When a trial or subscription lapses, Kavach becomes **read-only**:
123
+
124
+ - everything you created is still there and still readable
125
+ - writes are refused, and scheduled agents stop running
126
+ - nothing is deleted, and billing keeps working so you can resume
127
+
128
+ **Your local model and local data are never gated.** A personal install has no
129
+ login, no entitlement and no network requirement; `kavach chat` works forever,
130
+ lapsed subscription or not. Read-only applies only to a deployment running in
131
+ org mode, where Kavach is serving a team.
132
+
133
+ ## Automations
134
+
135
+ Scheduled automations in personal mode **run only while this machine is
136
+ awake**. A missed schedule fires once on wake, marked as a catch-up, rather
137
+ than replaying every slot that was missed while the laptop was closed.
138
+
139
+ For schedules that must be reliable, deploy the same binary on always-on
140
+ infrastructure you own (a VM, your Docker host, your cluster) and point the
141
+ team's browsers at it. Same product, same data ownership, plus shared knowledge
142
+ bases, shared agents, and cron that actually fires.
143
+
144
+ ## Environment
145
+
146
+ | Variable | Effect |
147
+ |---|---|
148
+ | `KAVACH_HOME` | Install root. Default `~/.kavach` |
149
+ | `KAVACH_LLAMA_SERVER` | Use your own llama.cpp build (e.g. compiled for your GPU) |
150
+ | `KAVACH_WHISPER_SERVER` | Use your own whisper.cpp build |
151
+ | `KAVACH_LLAMA_RELEASE_BASE` | Mirror for llama.cpp engine downloads |
152
+ | `KAVACH_WHISPER_RELEASE_BASE` | Mirror for whisper.cpp engine downloads |
153
+ | `KAVACH_BINARY` | Run a specific binary, e.g. one you built yourself |
154
+ | `KAVACH_LOG` | Log filter, e.g. `kavach_client=debug` |
155
+
156
+ ## Uninstalling
157
+
158
+ ```bash
159
+ npm uninstall -g kavach-ai
160
+ rm -rf ~/.kavach
161
+ ```
162
+
163
+ Everything Kavach stored is under that one directory, which is also all you
164
+ need to back up.
165
+
166
+ ## Licence
167
+
168
+ MIT.
package/bin/kavach.js ADDED
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * Launcher. Hands every argument to the native daemon.
6
+ *
7
+ * The wrapper stays as thin as possible: no argument parsing, no config
8
+ * reading, no business logic. Anything implemented here would have to be
9
+ * implemented a second time for people who run the binary directly, and the
10
+ * two copies would drift.
11
+ */
12
+
13
+ const { spawn } = require('child_process');
14
+
15
+ const { binaryPath, developmentBinary, detect } = require('../scripts/platform');
16
+
17
+ function resolve() {
18
+ let info;
19
+ try {
20
+ info = detect();
21
+ } catch (error) {
22
+ // Unsupported platform — detect() already explains what to do.
23
+ console.error(error.message);
24
+ process.exit(1);
25
+ }
26
+
27
+ const installed = binaryPath();
28
+ if (installed) return installed;
29
+
30
+ // Working on the daemon itself: a locally-built binary beats reinstalling.
31
+ const local = developmentBinary();
32
+ if (local) return local;
33
+
34
+ console.error(
35
+ `kavach: the binary for ${info.key} is not installed.\n\n` +
36
+ `It ships in ${info.package}, an optional dependency of this package.\n` +
37
+ `Installers skip optional dependencies when given --no-optional or\n` +
38
+ `--omit=optional, and some CI caches drop them.\n\n` +
39
+ ` Fix: npm install -g kavach-ai --include=optional\n` +
40
+ ` Build: cargo build --release -p kavach-client\n`
41
+ );
42
+ process.exit(1);
43
+ }
44
+
45
+ function main() {
46
+ const child = spawn(resolve(), process.argv.slice(2), { stdio: 'inherit' });
47
+
48
+ // Forward termination so Ctrl-C in the terminal stops the daemon rather
49
+ // than orphaning it holding the port and gigabytes of model memory.
50
+ for (const signal of ['SIGINT', 'SIGTERM', 'SIGHUP']) {
51
+ process.on(signal, () => child.kill(signal));
52
+ }
53
+
54
+ child.on('exit', (code, signal) => {
55
+ // Reproduce signal death as the conventional 128+n so shell scripts and
56
+ // supervisors see what actually happened.
57
+ process.exit(signal ? 128 + signalNumber(signal) : code ?? 0);
58
+ });
59
+
60
+ child.on('error', (error) => {
61
+ console.error(`kavach: could not start the daemon — ${error.message}`);
62
+ process.exit(1);
63
+ });
64
+ }
65
+
66
+ function signalNumber(signal) {
67
+ return { SIGHUP: 1, SIGINT: 2, SIGQUIT: 3, SIGKILL: 9, SIGTERM: 15 }[signal] ?? 0;
68
+ }
69
+
70
+ main();
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "kavach-ai",
3
+ "version": "0.1.0",
4
+ "description": "Kavach — your agents, your data, your machine. Runs a local AI model and studio on your own hardware.",
5
+ "keywords": [
6
+ "ai",
7
+ "agents",
8
+ "local-first",
9
+ "llm",
10
+ "privacy",
11
+ "knowledge-base"
12
+ ],
13
+ "license": "MIT",
14
+ "homepage": "https://bluehawks.ai/kavach",
15
+ "bin": {
16
+ "kavach": "bin/kavach.js"
17
+ },
18
+ "files": [
19
+ "bin/",
20
+ "scripts/platform.js",
21
+ "README.md"
22
+ ],
23
+ "scripts": {
24
+ "test": "node --test \"scripts/*.test.js\"",
25
+ "build:platforms": "node scripts/build-platform-packages.js",
26
+ "prepublishOnly": "node scripts/no-publish.js"
27
+ },
28
+ "engines": {
29
+ "node": ">=18"
30
+ },
31
+ "optionalDependencies": {
32
+ "@kavach-ai/cli-darwin-arm64": "0.1.0",
33
+ "@kavach-ai/cli-darwin-x64": "0.1.0",
34
+ "@kavach-ai/cli-linux-x64": "0.1.0",
35
+ "@kavach-ai/cli-linux-arm64": "0.1.0"
36
+ }
37
+ }
@@ -0,0 +1,122 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Locating the native binary.
5
+ *
6
+ * ## Why the binary ships inside npm packages rather than being downloaded
7
+ *
8
+ * The obvious design is a postinstall that fetches a release asset from
9
+ * GitHub. It cannot work here: the source repository is private, and a private
10
+ * repo's release assets are not anonymously downloadable, so every install
11
+ * would 404. Publishing the binaries to a second public repo would mean
12
+ * running release infrastructure whose only job is to be public.
13
+ *
14
+ * So the binaries are published as per-platform npm packages —
15
+ * `@kavach-ai/cli-linux-x64` and friends — and this package lists them as
16
+ * `optionalDependencies`. npm reads each one's `os`/`cpu` fields and installs
17
+ * only the one that matches, skipping the rest. This is what esbuild, swc and
18
+ * turbo do, and it buys three things beyond solving the private-repo problem:
19
+ * no network call outside the registry (so it works behind a corporate proxy
20
+ * that allows npm and nothing else), no checksum logic to get wrong, and an
21
+ * install that either fully succeeds or fully fails.
22
+ *
23
+ * Only compiled binaries are published. No source of any kind is in the
24
+ * tarballs — see `files` in package.json and the platform-package generator.
25
+ */
26
+
27
+ const path = require('path');
28
+ const fs = require('fs');
29
+
30
+ /**
31
+ * Platform package per `${process.platform}-${process.arch}`.
32
+ *
33
+ * Kept as a literal map rather than built by string concatenation so that
34
+ * `require.resolve` sees a static name and bundlers do not have to guess.
35
+ */
36
+ const PACKAGES = {
37
+ 'darwin-arm64': '@kavach-ai/cli-darwin-arm64',
38
+ 'darwin-x64': '@kavach-ai/cli-darwin-x64',
39
+ 'linux-x64': '@kavach-ai/cli-linux-x64',
40
+ 'linux-arm64': '@kavach-ai/cli-linux-arm64',
41
+ };
42
+
43
+ /** Rust target triples, used by the release build and the generator. */
44
+ const TARGETS = {
45
+ 'darwin-arm64': 'aarch64-apple-darwin',
46
+ 'darwin-x64': 'x86_64-apple-darwin',
47
+ 'linux-x64': 'x86_64-unknown-linux-gnu',
48
+ 'linux-arm64': 'aarch64-unknown-linux-gnu',
49
+ };
50
+
51
+ // Windows (`win32-x64` / `x86_64-pc-windows-msvc`) is deliberately absent as of
52
+ // 0.1.0. It is not unsupported in principle — the `.exe` handling below and in
53
+ // the platform-package generator is intact and the Rust target builds — there
54
+ // was simply no Windows machine available to build and smoke-test on when this
55
+ // released, and shipping a Windows binary nobody has ever run is worse than
56
+ // shipping none: `detect` below tells those users to build from source, whereas
57
+ // a listed-but-missing package installs cleanly and then cannot find its
58
+ // binary. Restore both entries above once a Windows build is verified.
59
+
60
+ /**
61
+ * Resolve the platform package for the current machine.
62
+ *
63
+ * @throws {Error} naming the supported set, on an unsupported platform
64
+ */
65
+ function detect(platform = process.platform, arch = process.arch) {
66
+ const key = `${platform}-${arch}`;
67
+ const pkg = PACKAGES[key];
68
+
69
+ if (!pkg) {
70
+ // "Unsupported platform" with no further detail is the message that
71
+ // generates support tickets. Name what is supported and the way out.
72
+ throw new Error(
73
+ `Kavach has no prebuilt binary for ${key}.\n` +
74
+ `Supported: ${Object.keys(PACKAGES).join(', ')}.\n` +
75
+ `You can build from source: cargo build --release -p kavach-client`
76
+ );
77
+ }
78
+
79
+ return {
80
+ key,
81
+ package: pkg,
82
+ target: TARGETS[key],
83
+ binaryName: platform === 'win32' ? 'kavach.exe' : 'kavach',
84
+ };
85
+ }
86
+
87
+ /**
88
+ * Absolute path to the installed binary, or `null` when the platform package
89
+ * is not present.
90
+ *
91
+ * `null` rather than a throw: a missing optional dependency is a recoverable
92
+ * install problem with a known fix, and the caller prints that fix.
93
+ */
94
+ function binaryPath(platform = process.platform, arch = process.arch) {
95
+ const { package: pkg, binaryName } = detect(platform, arch);
96
+
97
+ try {
98
+ // Resolve the package's own manifest rather than a path inside it — that
99
+ // works regardless of how the installer laid the tree out (hoisted,
100
+ // nested, pnpm's symlinked store).
101
+ const manifest = require.resolve(`${pkg}/package.json`, { paths: [__dirname] });
102
+ const candidate = path.join(path.dirname(manifest), 'bin', binaryName);
103
+ return fs.existsSync(candidate) ? candidate : null;
104
+ } catch {
105
+ return null;
106
+ }
107
+ }
108
+
109
+ /** Where a locally-built binary lives, for developers working on the daemon. */
110
+ function developmentBinary() {
111
+ const override = process.env.KAVACH_BINARY;
112
+ if (override) return fs.existsSync(override) ? override : null;
113
+
114
+ const { binaryName } = detect();
115
+ for (const profile of ['dist', 'release', 'debug']) {
116
+ const candidate = path.resolve(__dirname, '..', '..', '..', 'target', profile, binaryName);
117
+ if (fs.existsSync(candidate)) return candidate;
118
+ }
119
+ return null;
120
+ }
121
+
122
+ module.exports = { PACKAGES, TARGETS, detect, binaryPath, developmentBinary };