sublera-cli 0.0.0-stage → 1.3.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/CHANGELOG.md +172 -0
- package/LICENSE +21 -0
- package/README.md +585 -2
- package/SECURITY.md +78 -0
- package/THIRD_PARTY_NOTICES.md +37 -0
- package/dist/agent.d.ts +270 -0
- package/dist/agent.js +1867 -0
- package/dist/commands.d.ts +76 -0
- package/dist/commands.js +841 -0
- package/dist/config.d.ts +27 -0
- package/dist/config.js +95 -0
- package/dist/engine/binaryManager.d.ts +67 -0
- package/dist/engine/binaryManager.js +590 -0
- package/dist/engine/gguf.d.ts +88 -0
- package/dist/engine/gguf.js +458 -0
- package/dist/engine/llamaRunner.d.ts +78 -0
- package/dist/engine/llamaRunner.js +282 -0
- package/dist/engine/modelManager.d.ts +63 -0
- package/dist/engine/modelManager.js +510 -0
- package/dist/engine/selfCheck.d.ts +34 -0
- package/dist/engine/selfCheck.js +108 -0
- package/dist/exitCodes.d.ts +20 -0
- package/dist/exitCodes.js +20 -0
- package/dist/hardware/gpuMonitor.d.ts +32 -0
- package/dist/hardware/gpuMonitor.js +378 -0
- package/dist/hardware/sleepGuard.d.ts +42 -0
- package/dist/hardware/sleepGuard.js +149 -0
- package/dist/hardware/types.d.ts +40 -0
- package/dist/hardware/types.js +2 -0
- package/dist/health.d.ts +17 -0
- package/dist/health.js +38 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +268 -0
- package/dist/logger.d.ts +53 -0
- package/dist/logger.js +201 -0
- package/dist/network/proxy.d.ts +58 -0
- package/dist/network/proxy.js +328 -0
- package/dist/network/streamProxy.d.ts +51 -0
- package/dist/network/streamProxy.js +286 -0
- package/dist/network/tunnelClient.d.ts +54 -0
- package/dist/network/tunnelClient.js +257 -0
- package/dist/settings.d.ts +53 -0
- package/dist/settings.js +171 -0
- package/dist/state.d.ts +52 -0
- package/dist/state.js +71 -0
- package/dist/storageCommand.d.ts +22 -0
- package/dist/storageCommand.js +81 -0
- package/dist/types.d.ts +222 -0
- package/dist/types.js +43 -0
- package/dist/ui/progress.d.ts +29 -0
- package/dist/ui/progress.js +82 -0
- package/dist/ui/terminalDashboard.d.ts +88 -0
- package/dist/ui/terminalDashboard.js +295 -0
- package/dist/util/env.d.ts +3 -0
- package/dist/util/env.js +6 -0
- package/dist/util/exec.d.ts +23 -0
- package/dist/util/exec.js +116 -0
- package/dist/util/format.d.ts +47 -0
- package/dist/util/format.js +125 -0
- package/dist/util/fs.d.ts +18 -0
- package/dist/util/fs.js +140 -0
- package/dist/util/http.d.ts +28 -0
- package/dist/util/http.js +128 -0
- package/dist/util/instanceLock.d.ts +3 -0
- package/dist/util/instanceLock.js +108 -0
- package/dist/util/open.d.ts +16 -0
- package/dist/util/open.js +65 -0
- package/dist/util/storage.d.ts +10 -0
- package/dist/util/storage.js +69 -0
- package/dist/version.d.ts +15 -0
- package/dist/version.js +29 -0
- package/package.json +68 -4
package/README.md
CHANGED
|
@@ -1,3 +1,586 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Sublera CLI
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A standalone terminal agent for the Sublera peer-to-peer GPU market. It runs on the
|
|
4
|
+
GPU owner's machine, manages `llama-server` and GGUF files, keeps the machine
|
|
5
|
+
awake during overnight bookings, and holds one outbound WebSocket to the central
|
|
6
|
+
server. Buyers never connect to the host directly.
|
|
7
|
+
|
|
8
|
+
This package is the implementation of `../CLI_PLAN.md`. It is deliberately
|
|
9
|
+
separate from the website/server so it can be published to npm and updated on
|
|
10
|
+
host machines without redeploying the marketplace.
|
|
11
|
+
|
|
12
|
+
```text
|
|
13
|
+
Browser / buyer
|
|
14
|
+
│ OpenAI-compatible stream
|
|
15
|
+
▼
|
|
16
|
+
Sublera central server ◄──── outbound WSS tunnel ──── sublera-cli (this package)
|
|
17
|
+
│ │
|
|
18
|
+
│ ├── llama-server on 127.0.0.1
|
|
19
|
+
│ ├── GGUF cache + SHA-256
|
|
20
|
+
│ └── GPU monitor + sleep guard
|
|
21
|
+
▼
|
|
22
|
+
Postgres/SQLite ledger, wallet, reliability, request board
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Migration from Sublet
|
|
26
|
+
|
|
27
|
+
- The local package name is `sublera-cli`, with `sublera` and `sublera-cli`
|
|
28
|
+
command names. The old `sublet` and `sublet-cli` command aliases remain supported.
|
|
29
|
+
- Use `SUBLERA_*` environment variables in new examples. Legacy `SUBLET_*`
|
|
30
|
+
variables remain supported. If both names for a setting are set, `SUBLERA_*`
|
|
31
|
+
wins; command-line flags still take precedence over environment settings.
|
|
32
|
+
- The default data directory is intentionally still `~/.sublet`, not a new
|
|
33
|
+
`~/.sublera`: old and new names share credentials, model/engine cache, recovery
|
|
34
|
+
state, and the instance lock. Do not move it merely for the rebrand. Existing
|
|
35
|
+
model `.sublet.json` metadata filenames are unchanged.
|
|
36
|
+
- The repository remains `somemusic69-sudo/Sublet-cli`. The default server is
|
|
37
|
+
`https://sublera.com` (it was `https://sublet.duckdns.org` before 1.3.0).
|
|
38
|
+
- The npm package is `sublera-cli`, published only by the Release workflow
|
|
39
|
+
(`.github/workflows/release.yml`) with npm provenance.
|
|
40
|
+
|
|
41
|
+
## Quick start
|
|
42
|
+
|
|
43
|
+
You need Node.js 20.18.1 or newer (the LTS version from nodejs.org).
|
|
44
|
+
From a local checkout on the GPU machine, run:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
cd agent-cli
|
|
48
|
+
npm install
|
|
49
|
+
npm run build
|
|
50
|
+
node dist/index.js start
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
For subsequent examples, use `node dist/index.js` in place of `sublera` or
|
|
54
|
+
`npx sublera-cli` when running from this local build.
|
|
55
|
+
|
|
56
|
+
On the first run, `start` opens the Sublera website so you can connect this
|
|
57
|
+
computer (click **Connect**), installs the matching `llama-server` build (CUDA
|
|
58
|
+
for NVIDIA, Vulkan for AMD/Intel, Metal for Apple Silicon, CPU fallback), and
|
|
59
|
+
then waits for work. The default server is `https://sublera.com`;
|
|
60
|
+
`--server <url>` uses another Sublera server. Servers must use https, except on
|
|
61
|
+
this machine (`localhost`, `127.0.0.1`); `SUBLERA_ALLOW_HTTP=1` allows plain http
|
|
62
|
+
for a test server on a trusted network.
|
|
63
|
+
|
|
64
|
+
`sublera doctor` checks the machine (`--fix` installs the engine ahead of time).
|
|
65
|
+
`sublera login` connects the computer again; on a machine without a browser,
|
|
66
|
+
open the printed link on any phone or laptop, or use `login --key sh_...`.
|
|
67
|
+
|
|
68
|
+
To serve one buyer request, pick it on the website's market. The board shows
|
|
69
|
+
which requests fit your GPU and which models are already on your disk. Click
|
|
70
|
+
**Take**, so no other host can claim it, then run the command that the website
|
|
71
|
+
shows:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
npx sublera-cli start r_abc123
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The agent downloads and verifies the model, passes the model check, serves the
|
|
78
|
+
booking, and exits when the booking ends. If other bookings still use the GPU at
|
|
79
|
+
that point, it keeps serving them and exits after the last one. If you do not start within 15 minutes,
|
|
80
|
+
the request goes back to the board. With `sublera start` already running, a Take
|
|
81
|
+
on the website starts the download at once.
|
|
82
|
+
|
|
83
|
+
For public hosting from the terminal:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
npx sublera-cli host \
|
|
87
|
+
https://huggingface.co/bartowski/Qwen2.5-7B-Instruct-GGUF/resolve/main/Qwen2.5-7B-Instruct-Q4_K_M.gguf \
|
|
88
|
+
--price 0.25 --price-hour 0.5 --ctx 8192 --slots 4
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
A public host earns three ways, all on the one loaded model. Per-token buyers and
|
|
92
|
+
speed seats share the GPU; a full-GPU rental has it alone:
|
|
93
|
+
|
|
94
|
+
- **Per 1M tokens.** Buyers pay per answer at `--price` (or `--price-in` /
|
|
95
|
+
`--price-out`).
|
|
96
|
+
- **Per hour, full GPU.** One renter gets the whole GPU for a number of hours at
|
|
97
|
+
`--price-hour` (default `0.50` USD; `0` turns hourly rentals off; at most `50`).
|
|
98
|
+
While it runs there are no seats and no per-token selling.
|
|
99
|
+
- **Per hour, speed seat.** A renter reserves part of the measured speed, for
|
|
100
|
+
example 50 of 300 tok/s, for a number of hours. The rest keeps serving other
|
|
101
|
+
seats and per-token buyers. The server sets seat prices from `--price-hour`.
|
|
102
|
+
|
|
103
|
+
The server benchmarks the loaded model, admits only the seats and jobs that fit,
|
|
104
|
+
and paces each seat to its reserved speed. The agent serves every booking side by
|
|
105
|
+
side and never restarts `llama-server` while a booking or public buyers use it.
|
|
106
|
+
The dashboard lists each rental, the one that ends first, and earnings: per-token
|
|
107
|
+
earnings plus the time share of every booking. Per-token earnings made while
|
|
108
|
+
rentals run are not counted there, because the agent cannot tell a seat
|
|
109
|
+
renter's jobs from per-token jobs; the wallet on the website is exact.
|
|
110
|
+
|
|
111
|
+
For an SSH/mini-datacenter host that wants to see the buyer request board:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
npx sublera-cli jobs
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
`jobs` claims the selected request and, if no agent is already running, starts
|
|
118
|
+
the tunnel/model/download/serve loop in the same terminal until the booking
|
|
119
|
+
ends and no other booking uses the GPU, or you press Ctrl+C.
|
|
120
|
+
|
|
121
|
+
## Commands
|
|
122
|
+
|
|
123
|
+
| Command | Purpose |
|
|
124
|
+
| --- | --- |
|
|
125
|
+
| `sublera login` | Link this computer to your account: show a code, open the website, receive a host key when you click Connect. `--key` saves a key directly; `--no-browser` only prints the link. The key is saved with mode 0600. |
|
|
126
|
+
| `sublera doctor` | Agent version, Node, server, key, download speed, GPU, free VRAM, sleep guard, llama-server and cache checks. `--fix` installs the engine. `--json` for machines. |
|
|
127
|
+
| `sublera start [code]` | Main mode: connect, download/verify the model of a request taken on the website, pass the model check, serve. With a code, serve that request; exit after it ends, once no other booking is active. |
|
|
128
|
+
| `sublera host <url\|path>` | Public mode with a chosen GGUF: a price per 1M tokens, plus hourly rentals of the full GPU or a speed seat (`--price-hour`). |
|
|
129
|
+
| `sublera jobs` | Table of open requests with VRAM fit and payout. Pick one and it claims + serves in this terminal; `--claim <id>` is non-interactive. |
|
|
130
|
+
| `sublera status` | Read-only host, reliability, wallet and a line for every active booking. |
|
|
131
|
+
| `sublera config [show\|path]` | The settings in use and where each one came from (flag, env, `agent.json`, login, default). Keys are masked. |
|
|
132
|
+
| `sublera logout` | Remove the saved host key. |
|
|
133
|
+
|
|
134
|
+
Global flags: `--server`, `--key`, `--ws-path`, `--simulation`, `--gpu`,
|
|
135
|
+
`--vram`, `--ctx`, `--slots`, `--price`, `--price-in`, `--price-out`,
|
|
136
|
+
`--price-hour`, `--tok-s`, `--no-install`, `--no-browser`, `--json`, `--verbose`, `--log-level`,
|
|
137
|
+
`--log-file`, `--no-color`, `--once`. Each can also be set with a `SUBLERA_*`
|
|
138
|
+
environment variable or in `~/.sublet/agent.json` (see [Configuration](#configuration)).
|
|
139
|
+
`--gpu` and `--vram` also correct a wrongly detected GPU name or VRAM size on a real host.
|
|
140
|
+
|
|
141
|
+
### Test mode
|
|
142
|
+
|
|
143
|
+
Everything can be exercised without a GPU or a multi-GB download:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
sublera start --simulation --gpu "NVIDIA GeForce RTX 4060" --vram 8188 --tok-s 24
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Simulation mode still uses the real tunnel, metering, billing, model checks, booking
|
|
150
|
+
timer, reconnect logic and dashboard. Only the generated words are a stand-in.
|
|
151
|
+
The `simulate` flag on the `hello` frame tells the server this is a test node.
|
|
152
|
+
|
|
153
|
+
## What is in the package
|
|
154
|
+
|
|
155
|
+
```text
|
|
156
|
+
agent-cli/
|
|
157
|
+
├── src/
|
|
158
|
+
│ ├── index.ts CLI entry, parser, exit codes, shebang
|
|
159
|
+
│ ├── commands.ts login / doctor / start / host / jobs / status
|
|
160
|
+
│ ├── agent.ts orchestration of tunnel, engine and dashboard
|
|
161
|
+
│ ├── config.ts ~/.sublet, URL normalization, key masking
|
|
162
|
+
│ ├── logger.ts human and JSON logs (stderr, never dashboard stdout)
|
|
163
|
+
│ ├── hardware/
|
|
164
|
+
│ │ ├── gpuMonitor.ts nvidia-smi / rocm-smi / macOS / Windows fallback
|
|
165
|
+
│ │ ├── sleepGuard.ts SetThreadExecutionState / caffeinate / systemd-inhibit
|
|
166
|
+
│ │ └── types.ts
|
|
167
|
+
│ ├── engine/
|
|
168
|
+
│ │ ├── binaryManager.ts GitHub release picker, checksum, extraction, manifest
|
|
169
|
+
│ │ ├── modelManager.ts resumable download, streaming SHA-256, GGUF header
|
|
170
|
+
│ │ ├── llamaRunner.ts hidden 127.0.0.1 port, locked flags, crash restart
|
|
171
|
+
│ │ └── gguf.ts header parser + VRAM estimator
|
|
172
|
+
│ ├── network/
|
|
173
|
+
│ │ ├── tunnelClient.ts WSS, 1/2/5/10/15/30s backoff, heartbeat, dead-link detection, auth fail, version gate
|
|
174
|
+
│ │ └── streamProxy.ts SSE parser, stream=true piping, token probabilities
|
|
175
|
+
│ └── ui/
|
|
176
|
+
│ ├── terminalDashboard.ts the overnight status box
|
|
177
|
+
│ └── progress.ts download bar with speed and ETA
|
|
178
|
+
├── tests/ unit tests, no network or GPU required
|
|
179
|
+
├── scripts/e2e.mjs 9-check end-to-end proof against a live Sublera server
|
|
180
|
+
└── README.md
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## What a host sees
|
|
184
|
+
|
|
185
|
+
```
|
|
186
|
+
┌──────────────────────────────────────────────────────────────────────┐
|
|
187
|
+
│ Sublera GPU TERMINAL AGENT v1.0.0 STATUS: ● WORKING │
|
|
188
|
+
├──────────────────────────────────────────────────────────────────────┤
|
|
189
|
+
│ Host Endpoint : https://host.example.com/api/v1 │
|
|
190
|
+
│ Reliability : 98.5% (OK - held only after repeated failures) │
|
|
191
|
+
│ GPU Detected : NVIDIA GeForce RTX 3090 (21.2 GB free / 24.0 GB) │
|
|
192
|
+
│ GPU Temp/Power: 61°C · 185W · Wake-Lock: ACTIVE - sleep blocked │
|
|
193
|
+
├──────────────────────────────────────────────────────────────────────┤
|
|
194
|
+
│ CURRENT JOB : Dedicated Buyer Request (#804) │
|
|
195
|
+
│ Model Loaded : Llama-3.3-70B-Abliterated-Q4_K_M ✓ verified │
|
|
196
|
+
│ Time Remaining: 06:14:32 / 08:00:00 │
|
|
197
|
+
│ Live Speed : 38.4 tok/s (min required: 25.0 tok/s ✓) │
|
|
198
|
+
├──────────────────────────────────────────────────────────────────────┤
|
|
199
|
+
│ Session Earn. : $4.68 / $6.00 Guaranteed │
|
|
200
|
+
│ Total Wallet : $42.15 │
|
|
201
|
+
│ Server : https://sublera.example.com │
|
|
202
|
+
└──────────────────────────────────────────────────────────────────────┘
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
The dashboard redraws in place. Logs go to stderr, so terminal logs never
|
|
206
|
+
destroy the box. `--json` switches logs to JSON lines and is suitable for
|
|
207
|
+
systemd/journald.
|
|
208
|
+
|
|
209
|
+
## Data directory
|
|
210
|
+
|
|
211
|
+
Everything lives under `~/.sublet` (override with `SUBLERA_HOME`):
|
|
212
|
+
|
|
213
|
+
```text
|
|
214
|
+
~/.sublet/config.json host key + server, written by sublera login (0600)
|
|
215
|
+
~/.sublet/agent.json optional settings file you write (see Configuration)
|
|
216
|
+
~/.sublet/state.json recovery snapshot of every active booking (0600)
|
|
217
|
+
~/.sublet/bin/ llama-server builds + manifest.json
|
|
218
|
+
~/.sublet/models/ verified .gguf files + .sublet.json sidecars
|
|
219
|
+
~/.sublet/cache/ GitHub release metadata, temporary archives
|
|
220
|
+
~/.sublet/logs/agent.log agent log, JSON lines, rotated (0600)
|
|
221
|
+
~/.sublet/logs/llama-server.log engine log (prompt logging disabled)
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
A model that is already on disk is not downloaded again, but it is hashed again
|
|
225
|
+
before it is served (once per agent run), so the SHA-256 sent to the server is
|
|
226
|
+
of the bytes on disk and not of the sidecar note beside them. A file that fails
|
|
227
|
+
is deleted and downloaded again. A resume sends `Range: bytes=N-` and appends
|
|
228
|
+
only the missing bytes. If a server ignores Range, the partial file is discarded
|
|
229
|
+
and the download restarts from zero rather than corrupting weights.
|
|
230
|
+
|
|
231
|
+
## Configuration
|
|
232
|
+
|
|
233
|
+
### Production operations
|
|
234
|
+
|
|
235
|
+
`start`, `host`, and a serving `jobs` agent exclusively own `SUBLERA_HOME` via
|
|
236
|
+
`agent.lock`. A second process refuses to start. Storage maintenance acquires
|
|
237
|
+
the same lock, as does engine installation by `doctor --fix`. After an abrupt crash
|
|
238
|
+
the next agent takes the lock over only when the process that wrote it is no
|
|
239
|
+
longer running, so a service manager can restart the agent and it restores its
|
|
240
|
+
booking. A lock held by a running process, or one that cannot be read, is
|
|
241
|
+
refused: check every agent using that directory, then remove only `agent.lock`.
|
|
242
|
+
Never remove `state.json` just to unlock the directory.
|
|
243
|
+
|
|
244
|
+
- `sublera health live --json`: local process liveness.
|
|
245
|
+
- `sublera health ready --json`: local readiness (default health mode). Requires
|
|
246
|
+
a fresh snapshot, connected tunnel, loaded ready engine (or simulation), and
|
|
247
|
+
no drain/model load. Exit 0 means the requested check passed; exit 1 means
|
|
248
|
+
failed/unknown. Snapshots update every 5 seconds and expire after 15 seconds.
|
|
249
|
+
Simulation is explicitly labeled; these checks are not model attestation,
|
|
250
|
+
server-side billing approval, or proof of GPU performance/capacity.
|
|
251
|
+
- SIGTERM enters drain: refuse new inference/model loads, allow active inference
|
|
252
|
+
up to 30 seconds, then cancel leftovers, stop the engine, and preserve booking
|
|
253
|
+
state. Configure `--drain-timeout <ms>` / `SUBLERA_DRAIN_TIMEOUT_MS` /
|
|
254
|
+
`agent.json` `drainTimeoutMs` (0–300000). Ctrl+C remains immediate stop.
|
|
255
|
+
Drain is local admission control; it does not implement a server scheduling
|
|
256
|
+
handshake. Use server-side maintenance controls for long booked windows.
|
|
257
|
+
- `sublera storage status --json`: reports model, engine, cache and log sizes.
|
|
258
|
+
- `sublera storage prune --json`: **preview only**, for regular model `.part`
|
|
259
|
+
and `.part.json` files older than seven days. Add `--force` to apply. Stop
|
|
260
|
+
serving first. Complete models, engine versions, keys and booking state are
|
|
261
|
+
never pruned by this command; recent partial downloads remain resumable.
|
|
262
|
+
|
|
263
|
+
Use fixed CLI/engine versions, a dedicated non-admin service account and a
|
|
264
|
+
dedicated local `SUBLERA_HOME` (not a shared network filesystem). Configure your
|
|
265
|
+
service supervisor with bounded restart/backoff, log rotation and enough stop
|
|
266
|
+
grace for drain plus engine teardown. Health snapshots include no prompts/keys,
|
|
267
|
+
but are local diagnostics, not tamper-proof attestation. Service installation,
|
|
268
|
+
fleet metrics/alerts, server billing/idempotency validation, real GPU soak and
|
|
269
|
+
rollback acceptance remain operator/release gates; see `SOAK_TEST.md`.
|
|
270
|
+
|
|
271
|
+
Engine replacement validates the staged binary's presence and hash before
|
|
272
|
+
promotion and attempts rollback on ordinary errors. Promotion and manifest
|
|
273
|
+
updates are not one crash-atomic transaction: abrupt termination can leave
|
|
274
|
+
uniquely named `.tmp`/`.bak` directories requiring operator inspection. Disk
|
|
275
|
+
headroom checks cannot reserve space against other applications. During shutdown,
|
|
276
|
+
ownership remains held until pending message handlers settle; a stuck handler
|
|
277
|
+
may therefore require supervisor intervention rather than unsafe lock takeover.
|
|
278
|
+
|
|
279
|
+
### Storage safety
|
|
280
|
+
|
|
281
|
+
The CLI does not create screenshot browser profiles. Its application data lives
|
|
282
|
+
under `~/.sublet`, or `SUBLERA_HOME` on a drive you choose.
|
|
283
|
+
|
|
284
|
+
- Model downloads share a **20 GiB cache budget**, including completed models,
|
|
285
|
+
resumable `.part` files, sidecars and nested files. A full cache refuses new
|
|
286
|
+
downloads; it never automatically deletes a model or booking recovery data.
|
|
287
|
+
- Downloads retain **5 GiB free disk headroom**. Unknown-length model streams
|
|
288
|
+
are bounded too; free space is checked periodically during large transfers.
|
|
289
|
+
- Set `SUBLERA_MODEL_CACHE_GB=40` for a larger model cache, and
|
|
290
|
+
`SUBLERA_MIN_FREE_DISK_GB=10` for more headroom. Values are positive GiB and
|
|
291
|
+
these are environment-only controls, not `agent.json` keys.
|
|
292
|
+
- New engine installs refuse a `bin` directory above **10 GiB** and archive
|
|
293
|
+
downloads above **2 GiB each**. Extraction is bounded; the installer requires
|
|
294
|
+
additional temporary disk space and removes its work/staging on normal exit.
|
|
295
|
+
- Default agent logs rotate at **10 MiB**, with **5 previous files** (about
|
|
296
|
+
**60 MiB** total). External stdout/service-manager logs need their own rotation.
|
|
297
|
+
|
|
298
|
+
Existing oversized caches are not erased or shrunk automatically. Stop the
|
|
299
|
+
agent before manually removing unused cached models/partials or old engine
|
|
300
|
+
versions. Interrupted downloads remain resumable and count toward the budget;
|
|
301
|
+
forced termination can leave installer work directories. Limits apply to new
|
|
302
|
+
writes, not an absolute filesystem quota: other processes, simultaneous CLI
|
|
303
|
+
instances, npm caches and external engine writes can still consume disk space.
|
|
304
|
+
|
|
305
|
+
The settings listed below work three ways. The first one found wins:
|
|
306
|
+
|
|
307
|
+
1. a command-line flag (`--ctx 16384`)
|
|
308
|
+
2. a `SUBLERA_*` environment variable (`SUBLERA_CTX=16384`)
|
|
309
|
+
3. the corresponding legacy `SUBLET_*` environment variable (`SUBLET_CTX=16384`)
|
|
310
|
+
4. `~/.sublet/agent.json` (path override: `SUBLERA_CONFIG`, legacy `SUBLET_CONFIG`)
|
|
311
|
+
|
|
312
|
+
When both environment names are set, `SUBLERA_*` wins. This also applies to
|
|
313
|
+
`SUBLERA_HOME` / `SUBLET_HOME`; without an override the home remains `~/.sublet`.
|
|
314
|
+
|
|
315
|
+
```json
|
|
316
|
+
{
|
|
317
|
+
"server": "https://sublera.com",
|
|
318
|
+
"context": 8192,
|
|
319
|
+
"slots": 4,
|
|
320
|
+
"price": 0.25,
|
|
321
|
+
"priceHour": 0.5,
|
|
322
|
+
"noBrowser": true,
|
|
323
|
+
"logLevel": "info"
|
|
324
|
+
}
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
| `agent.json` key | Environment variable | Flag |
|
|
328
|
+
| --- | --- | --- |
|
|
329
|
+
| `server` | `SUBLERA_SERVER` | `--server` |
|
|
330
|
+
| `token` | `SUBLERA_KEY` | `--key` |
|
|
331
|
+
| `wsPath` | `SUBLERA_WS_PATH` | `--ws-path` |
|
|
332
|
+
| `context` | `SUBLERA_CTX` | `--ctx` |
|
|
333
|
+
| `slots` | `SUBLERA_SLOTS` | `--slots` |
|
|
334
|
+
| `price` / `priceIn` / `priceOut` | `SUBLERA_PRICE` / `SUBLERA_PRICE_IN` / `SUBLERA_PRICE_OUT` | `--price` / `--price-in` / `--price-out` |
|
|
335
|
+
| `priceHour` | `SUBLERA_PRICE_HOUR` | `--price-hour` |
|
|
336
|
+
| `gpu` / `vram` | `SUBLERA_GPU` / `SUBLERA_VRAM` | `--gpu` / `--vram` |
|
|
337
|
+
| `tokPerSec` | `SUBLERA_TOK_S` | `--tok-s` |
|
|
338
|
+
| `noInstall` | `SUBLERA_NO_INSTALL` | `--no-install` |
|
|
339
|
+
| `noBrowser` | `SUBLERA_NO_BROWSER` | `--no-browser` |
|
|
340
|
+
| `requireChecksum` | `SUBLERA_REQUIRE_CHECKSUM` | `--require-checksum` |
|
|
341
|
+
| `logLevel` | `SUBLERA_LOG_LEVEL` | `--log-level` |
|
|
342
|
+
| `logFile` | `SUBLERA_LOG_FILE` | `--log-file` |
|
|
343
|
+
|
|
344
|
+
An unknown key or a bad value in `agent.json` stops the agent with exit code 2
|
|
345
|
+
and names the problem; a typo is never ignored silently. `sublera config` prints
|
|
346
|
+
the value in use for every setting and where it came from (keys masked);
|
|
347
|
+
`sublera config --json` gives the same for tooling.
|
|
348
|
+
|
|
349
|
+
## Corporate networks
|
|
350
|
+
|
|
351
|
+
The agent only connects outward: the Sublera server (HTTPS + one WebSocket),
|
|
352
|
+
HuggingFace for models, GitHub for llama.cpp releases, and npm for the version
|
|
353
|
+
check. Nothing listens on the network; `llama-server` binds to `127.0.0.1`.
|
|
354
|
+
|
|
355
|
+
```bash
|
|
356
|
+
export HTTPS_PROXY=http://user:password@proxy.corp.example:3128
|
|
357
|
+
export NO_PROXY=.corp.example,10.0.0.5
|
|
358
|
+
export NODE_EXTRA_CA_CERTS=/etc/ssl/corp-root.pem # TLS-inspecting proxies
|
|
359
|
+
sublera doctor # "Network: via proxy http://***@proxy.corp.example:3128"
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
- `HTTPS_PROXY` / `HTTP_PROXY` / `NO_PROXY` are read in upper or lower case.
|
|
363
|
+
`NO_PROXY` takes host names, `.domain` suffixes, exact IP addresses,
|
|
364
|
+
`host:port`, or `*` (CIDR ranges are not supported).
|
|
365
|
+
- The tunnel and downloads use HTTP CONNECT, so TLS runs end to end and the
|
|
366
|
+
host key is never visible to the proxy. Proxy passwords are never printed.
|
|
367
|
+
- Only `http://` and `https://` proxies are supported (no SOCKS); anything else
|
|
368
|
+
stops the agent with a clear message and exit code 2.
|
|
369
|
+
- Loopback (`localhost`, `127.*`) is never proxied.
|
|
370
|
+
|
|
371
|
+
## Logs
|
|
372
|
+
|
|
373
|
+
`start`, `host`, and `jobs` write every record, debug included, to
|
|
374
|
+
`~/.sublet/logs/agent.log` as JSON lines (`ts`, `level`, `message`, fields).
|
|
375
|
+
The file rotates at 10 MB and keeps 5 old files (`agent.log.1` ... `.5`). Use
|
|
376
|
+
`--log-file <path>` to move it or `--log-file off` to disable it. The
|
|
377
|
+
terminal shows `info` and above (`--log-level`, `--verbose`). Host keys, API
|
|
378
|
+
keys, bearer tokens, and `token=` URL parameters are replaced with `***`
|
|
379
|
+
everywhere. Prompts and model output are never logged.
|
|
380
|
+
|
|
381
|
+
## Exit codes
|
|
382
|
+
|
|
383
|
+
| Code | Meaning |
|
|
384
|
+
| --- | --- |
|
|
385
|
+
| `0` | Success, or a booking that ended normally |
|
|
386
|
+
| `1` | Runtime error: server unreachable, host key rejected, request refused, crash loop |
|
|
387
|
+
| `2` | Usage or settings error: unknown command or flag, invalid value, bad `agent.json`, bad proxy |
|
|
388
|
+
| `130` | Stopped with Ctrl+C; saved booking state is cleared |
|
|
389
|
+
| `143` | Stopped by SIGTERM (shutdown, `systemctl stop`); saved booking state is kept so the next start reattaches |
|
|
390
|
+
|
|
391
|
+
## Running unattended
|
|
392
|
+
|
|
393
|
+
A service manager should restart the agent and send SIGTERM to stop it. A
|
|
394
|
+
systemd example (Linux):
|
|
395
|
+
|
|
396
|
+
```ini
|
|
397
|
+
# /etc/systemd/system/sublera.service
|
|
398
|
+
[Unit]
|
|
399
|
+
Description=Sublera GPU host agent
|
|
400
|
+
Wants=network-online.target
|
|
401
|
+
After=network-online.target
|
|
402
|
+
|
|
403
|
+
[Service]
|
|
404
|
+
User=sublera
|
|
405
|
+
Environment=SUBLERA_NO_BROWSER=true
|
|
406
|
+
# Run "sublera login --key sh_..." once as this user, or provide SUBLERA_KEY from a secret store.
|
|
407
|
+
ExecStart=/usr/bin/env npx --yes sublera-cli@1 start --no-color
|
|
408
|
+
Restart=always
|
|
409
|
+
RestartSec=10
|
|
410
|
+
# Exit code 2 is a configuration mistake: do not restart in a loop.
|
|
411
|
+
RestartPreventExitStatus=2
|
|
412
|
+
KillSignal=SIGTERM
|
|
413
|
+
TimeoutStopSec=30
|
|
414
|
+
|
|
415
|
+
[Install]
|
|
416
|
+
WantedBy=multi-user.target
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
Pin a major version (`sublera-cli@1`) so an unattended machine never jumps to
|
|
420
|
+
a release with breaking changes. On Windows, Task Scheduler ("At startup",
|
|
421
|
+
"Restart on failure") or a service wrapper such as NSSM running
|
|
422
|
+
`npx --yes sublera-cli@1 start` does the same.
|
|
423
|
+
|
|
424
|
+
## Verification and safety
|
|
425
|
+
|
|
426
|
+
1. **Only GGUF.** Pickle/PyTorch/safetensors files are refused before download.
|
|
427
|
+
2. **Streaming SHA-256.** The hash is computed while bytes hit disk. A mismatch
|
|
428
|
+
deletes both the partial and final file. The server's `sha256` is mandatory
|
|
429
|
+
for paid request loads.
|
|
430
|
+
3. **GGUF header.** Magic, architecture, layer count, embedding length, context
|
|
431
|
+
and quantization are read from the first few MB. A 28-layer file cannot call
|
|
432
|
+
itself a 70B model.
|
|
433
|
+
4. **VRAM fit.** The agent checks free VRAM with a 2% safety margin before it
|
|
434
|
+
claims/loads, so a host does not fail a booking on an OOM.
|
|
435
|
+
5. **Locked engine flags.** `--n-gpu-layers 999 --ctx-size <n> --parallel <n>
|
|
436
|
+
--cont-batching --log-disable`, bound to `127.0.0.1`. llama.cpp's own
|
|
437
|
+
environment settings (`LLAMA_ARG_*`, `LLAMA_LOG_*`, `LLAMA_API_KEY`) are
|
|
438
|
+
removed from the engine's environment, so a host's private llama.cpp setup
|
|
439
|
+
cannot change the engine. A `llama-server` from `LLAMA_SERVER` or `PATH` is
|
|
440
|
+
used as it is, with a warning that another build can fail model checks.
|
|
441
|
+
6. **Model checks.** Every job asks `llama-server` for token probabilities
|
|
442
|
+
(`logprobs`, top 5), and the first 32 tokens of every job carry them to the
|
|
443
|
+
server. The server sends hidden check prompts as normal `infer` jobs (a host
|
|
444
|
+
cannot tell them apart) and compares the probabilities with other hosts that
|
|
445
|
+
run the same file, or with a trusted reference host. The booking timer starts
|
|
446
|
+
only after the first check, and checks continue at random times. Asking for
|
|
447
|
+
probabilities costs `llama-server` a little extra work on every token.
|
|
448
|
+
**The server is what catches a fake model.** This program runs on the host's
|
|
449
|
+
machine and is open source, so nothing it reports about itself is proof.
|
|
450
|
+
What it does locally is for the honest host: after every model load it asks
|
|
451
|
+
the engine one fixed question twice at temperature 0 and compares the token
|
|
452
|
+
probabilities, and after a crash restart it compares against the load-time
|
|
453
|
+
answer. A measured difference is a diagnostic warning, not a diagnosis of
|
|
454
|
+
faulty hardware or proof of model identity. Missing, malformed, incomplete,
|
|
455
|
+
or ambiguous evidence is inconclusive, never a pass. The self-check never
|
|
456
|
+
stops a booking, and a buyer's job cancels it. Server-side authenticity and
|
|
457
|
+
billing behavior require separate end-to-end validation.
|
|
458
|
+
7. **Sleep guard.** Windows uses a native `SetThreadExecutionState` loop;
|
|
459
|
+
macOS uses `caffeinate -dimsu`; Linux uses `systemd-inhibit`. The lock is
|
|
460
|
+
released when the job ends and on process exit.
|
|
461
|
+
8. **Crash recovery.** `llamaRunner` restarts a crashed server after 500ms, up
|
|
462
|
+
to five times in two minutes. A crash loop becomes a fatal state instead of
|
|
463
|
+
spinning forever.
|
|
464
|
+
9. **Reconnect grace + crash recovery.** A 10-second Wi-Fi blip reconnects with
|
|
465
|
+
backoff. A tunnel that stops answering pings for 45 seconds (a half-open TCP
|
|
466
|
+
connection after a router reset) is torn down and reconnected. The agent
|
|
467
|
+
keeps `llama-server` and the loaded model in VRAM. The server preserves
|
|
468
|
+
claims and the booking clock for 60 seconds. If the agent process itself is
|
|
469
|
+
killed, `~/.sublet/state.json` lets the next `start` reload the cached model
|
|
470
|
+
and reattach to the same booking instead of starting over. `SIGTERM`
|
|
471
|
+
(shutdown, `systemctl stop`) keeps that file; Ctrl+C clears it.
|
|
472
|
+
10. **Host key transport.** The tunnel authenticates with an
|
|
473
|
+
`Authorization: Bearer` header so the key is not placed in the WebSocket
|
|
474
|
+
URL or proxy access logs. If an older server rejects header auth, the CLI
|
|
475
|
+
retries once with the legacy query-token form.
|
|
476
|
+
11. **HuggingFace-only model downloads.** A model URL must be an `https://`
|
|
477
|
+
link to a `.gguf` file on `huggingface.co` or `hf.co`. The agent follows
|
|
478
|
+
redirects itself, and every redirect must also stay on HuggingFace over
|
|
479
|
+
`https://`, so a model link can never reach the host's own machine or local
|
|
480
|
+
network. A server `load` must be such a URL, never a local path. A download
|
|
481
|
+
stops at its announced size (at most 64 GB) and does not start without
|
|
482
|
+
enough free disk space. Only the unit tests can opt into localhost.
|
|
483
|
+
12. **Dashboard controls.** In an interactive terminal: `W` opens your host
|
|
484
|
+
page on the website, `C` copies the host endpoint, `L` toggles the llama log tail,
|
|
485
|
+
and `?` shows the key help.
|
|
486
|
+
|
|
487
|
+
## Server contract
|
|
488
|
+
|
|
489
|
+
This CLI works with the Sublera server (separate, not public yet) and with any
|
|
490
|
+
server that implements the same small contract:
|
|
491
|
+
|
|
492
|
+
| Endpoint / frame | Purpose |
|
|
493
|
+
| --- | --- |
|
|
494
|
+
| `GET /api/health` | doctor reachability check |
|
|
495
|
+
| `GET /api/agent/me` | host key verification, wallet, host, `activeRequest` (the newest) and `activeRequests` (every live request, newest first; the CLI falls back to `activeRequest` when it is missing) |
|
|
496
|
+
| `POST /api/cli/login`, `POST /api/cli/login/poll` | device login: a user code for the website, then the host key once it is approved |
|
|
497
|
+
| `GET /api/agent/requests` | open buyer requests + VRAM fit + `cached` (the model is already on this host's disk) |
|
|
498
|
+
| `POST /api/agent/requests/:id/claim` | claim from the terminal |
|
|
499
|
+
| `GET /tunnel?token=...` or `/ws/agent` | persistent outbound WebSocket |
|
|
500
|
+
| `hello`, `heartbeat`, `progress`, `loaded`, `load_failed`, `models`, `chunk`, `done`, `error` | agent → server. `hello` carries `requestId` (from `sublera start <code>`), `cachedModels` (SHA-256 of each verified GGUF on disk) and `priceHourUsd` (the full-GPU hourly price; `0` from `start` and `jobs`, and when hourly rentals are off); `models` updates that list after a download. The first 32 tokens of every job send `chunk.lp`: `[{ t, l, top: [[token, logprob], ...] }]`. |
|
|
501
|
+
| HTTP `426` (JSON `minVersion` optional) or WebSocket close `4426` (reason `<min version>` optional) | server refuses an agent that is too old; the CLI stops and tells the host to run `npx sublera-cli@latest` |
|
|
502
|
+
| `welcome`, `infer`, `load`, `cancel`, `verified`, `booking`, `reject` | server → agent. Model checks arrive as normal `infer` jobs. Several `booking` frames for different requests can be active at once; `shared` and `tokS` mark a speed seat (no `tokS` means the full GPU). A booking on the model that is already loaded needs no `load` frame. `reject` ends only its own request. |
|
|
503
|
+
|
|
504
|
+
The agent sends its version as `user-agent: sublera-cli/<version>` on every
|
|
505
|
+
request and as `agentVersion` in `hello`, so the server can refuse old agents
|
|
506
|
+
before a protocol change. A handshake refused with HTTP `401`/`403` is treated
|
|
507
|
+
like a rejected host key.
|
|
508
|
+
|
|
509
|
+
`loaded` is followed by the server's first model check. The server starts the
|
|
510
|
+
booking timer only after that check, then sends `verified`; the CLI displays the
|
|
511
|
+
timer it receives in the `booking` frame. The agent never trusts the host for billing: the central
|
|
512
|
+
proxy counts the SSE chunks.
|
|
513
|
+
|
|
514
|
+
## Development
|
|
515
|
+
|
|
516
|
+
```bash
|
|
517
|
+
npm install
|
|
518
|
+
npm run typecheck
|
|
519
|
+
npm test
|
|
520
|
+
npm run build
|
|
521
|
+
node dist/index.js --help
|
|
522
|
+
|
|
523
|
+
# end-to-end against a running Sublera server
|
|
524
|
+
$env:SUBLERA_HOME="$env:TEMP\sublera-cli-e2e"
|
|
525
|
+
npm run e2e
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
The e2e script signs up a fresh host, issues a key, drives the real tunnel, does
|
|
529
|
+
a billed pay-per-token stream, creates and claims a 30-minute booking, verifies
|
|
530
|
+
the timer starts after the model check, verifies booked tokens are not charged,
|
|
531
|
+
drops the tunnel, reconnects inside the grace window, verifies the same timer
|
|
532
|
+
survives, and cancels the request. It uses simulation mode so it does not need
|
|
533
|
+
a real 4GB+ model.
|
|
534
|
+
|
|
535
|
+
## Packaging and publication status
|
|
536
|
+
|
|
537
|
+
The local package name is `sublera-cli`. Its primary binaries are `sublera` and
|
|
538
|
+
`sublera-cli`; `sublet` and `sublet-cli` remain compatibility aliases.
|
|
539
|
+
For local package inspection only:
|
|
540
|
+
|
|
541
|
+
```bash
|
|
542
|
+
npm pack --dry-run
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
Releases are published to npm as `sublera-cli` by the Release workflow, never
|
|
546
|
+
from a laptop. Check a release's provenance on its npm page.
|
|
547
|
+
|
|
548
|
+
## Verification status
|
|
549
|
+
|
|
550
|
+
This build was verified on a Windows machine with an RTX 4060:
|
|
551
|
+
|
|
552
|
+
- `npm run typecheck`, `npm test`, and `npm run build` pass.
|
|
553
|
+
- `npm run e2e` passes against a live Sublera server: tunnel, billed public
|
|
554
|
+
stream, model check, booking timer, unlimited booked stream, jobs board,
|
|
555
|
+
process-kill crash recovery with the same timer, standalone `jobs` serving,
|
|
556
|
+
escrow cleanup, device login, and take + `start <code>`.
|
|
557
|
+
- On Linux x64 (CPU), `sublera doctor --fix` installed the official llama.cpp
|
|
558
|
+
b12202 build, including its library link chains, and served SmolLM2 GGUFs.
|
|
559
|
+
Those runs measured the model-check thresholds.
|
|
560
|
+
- `sublera doctor --fix` downloaded the official CUDA `llama-server` build
|
|
561
|
+
(b12176), verified its published SHA-256, extracted it, and recorded the
|
|
562
|
+
binary hash in `~/.sublet/bin/manifest.json`.
|
|
563
|
+
- A real 637MB TinyLlama 1.1B Q4_K_M GGUF was downloaded, header-checked,
|
|
564
|
+
loaded into `llama-server`, verified with the HF SHA-256, and served a real
|
|
565
|
+
40-token chat through the central tunnel with billing. The same real booking
|
|
566
|
+
was then SIGKILLed and restored from `state.json`; it reloaded the cached
|
|
567
|
+
model and served another chat at 85 tok/s with the original timer.
|
|
568
|
+
|
|
569
|
+
## Limits
|
|
570
|
+
|
|
571
|
+
- Real inference requires a `llama-server` build and a machine that can fit the
|
|
572
|
+
requested GGUF. `--simulation` is explicitly a test mode and is labeled as
|
|
573
|
+
such in the dashboard and on the wire.
|
|
574
|
+
- The agent downloads official llama.cpp release assets from GitHub. If a
|
|
575
|
+
release has no published digest/sidecar, the agent records the local SHA-256
|
|
576
|
+
and marks the install `verified: false` instead of pretending it was signed.
|
|
577
|
+
Pass `--require-checksum` on `doctor` to make a missing published checksum a
|
|
578
|
+
failure.
|
|
579
|
+
- Withdrawals and real card processing live on the server, not in this package.
|
|
580
|
+
|
|
581
|
+
## License
|
|
582
|
+
|
|
583
|
+
MIT. See [LICENSE](LICENSE) and [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
|
|
584
|
+
The CLI does not bundle model weights; each HuggingFace model keeps its own
|
|
585
|
+
license. Security reports: [SECURITY.md](SECURITY.md). Contributions:
|
|
586
|
+
[CONTRIBUTING.md](CONTRIBUTING.md).
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Security
|
|
2
|
+
|
|
3
|
+
## Reporting
|
|
4
|
+
|
|
5
|
+
Do not open a public issue for a vulnerability. Email the maintainer with:
|
|
6
|
+
|
|
7
|
+
- affected version or commit
|
|
8
|
+
- operating system and Node version
|
|
9
|
+
- reproduction steps or proof of concept
|
|
10
|
+
- impact assessment
|
|
11
|
+
|
|
12
|
+
We will acknowledge the report and coordinate a fix and disclosure date.
|
|
13
|
+
|
|
14
|
+
## Host key handling
|
|
15
|
+
|
|
16
|
+
- Host keys are saved in `~/.sublet/config.json` with mode 0600.
|
|
17
|
+
- The tunnel prefers `Authorization: Bearer`, keeping the key out of URLs.
|
|
18
|
+
The query-token form exists only as a compatibility fallback for older
|
|
19
|
+
servers.
|
|
20
|
+
- Do not paste host keys into screenshots, issues, or logs. The CLI masks keys
|
|
21
|
+
in its own output, but the terminal scrollback is yours.
|
|
22
|
+
- Every log line, on screen and in `~/.sublet/logs/agent.log`, is filtered:
|
|
23
|
+
`sh_`/`sk-` keys, `Bearer` values, and `token=` parameters are replaced with
|
|
24
|
+
`***`. The agent log file is created with mode 0600. Prompts and model
|
|
25
|
+
output are never logged.
|
|
26
|
+
- `SUBLERA_KEY` is supported for fleets and containers; legacy `SUBLET_KEY`
|
|
27
|
+
remains supported, with `SUBLERA_KEY` winning when both are set. The default
|
|
28
|
+
`~/.sublet` intentionally shares credentials, cache, and the instance lock
|
|
29
|
+
across old and new command aliases. Prefer a secret store
|
|
30
|
+
(systemd `LoadCredential`, Docker secrets, a CI secret) over a plain shell
|
|
31
|
+
profile. `sublera config` always shows the key masked.
|
|
32
|
+
|
|
33
|
+
## Network
|
|
34
|
+
|
|
35
|
+
- The agent only makes outbound connections: the Sublera server (HTTPS and one
|
|
36
|
+
WebSocket), `huggingface.co`/`hf.co` and its CDN for models,
|
|
37
|
+
`api.github.com`/`github.com` for llama.cpp releases, and
|
|
38
|
+
`registry.npmjs.org` for the version check. The engine opens a loopback-only
|
|
39
|
+
HTTP port; the agent does not expose a public listening port by default.
|
|
40
|
+
- With `HTTPS_PROXY` set, the tunnel uses HTTP CONNECT, so TLS runs end to end
|
|
41
|
+
between the agent and the Sublera server for HTTPS/WSS destinations. A normal
|
|
42
|
+
forwarding proxy sees destination and traffic metadata, not encrypted content.
|
|
43
|
+
An authorized TLS-inspecting proxy with a trusted root CA can read host keys
|
|
44
|
+
and content. HTTP/WS destinations are not end-to-end encrypted. Corporate roots
|
|
45
|
+
can be configured with `NODE_EXTRA_CA_CERTS`; certificate checks stay enabled.
|
|
46
|
+
- Proxy credentials in `HTTPS_PROXY` are hidden in all output.
|
|
47
|
+
|
|
48
|
+
## Model and binary trust
|
|
49
|
+
|
|
50
|
+
- Model downloads must use HTTPS (localhost is allowed for tests).
|
|
51
|
+
- GGUF files are checked for magic bytes, architecture, layers, and SHA-256
|
|
52
|
+
when HuggingFace publishes an LFS hash.
|
|
53
|
+
- A cached model is hashed again before it is served (once per agent run); the
|
|
54
|
+
sidecar file next to it is not accepted as the hash of a model being served.
|
|
55
|
+
The list of cached models that marks a request "ready now" on the market
|
|
56
|
+
still comes from sidecars; it is a hint, and the file is hashed when taken.
|
|
57
|
+
- The agent's reports about itself (model hash, engine, self-check) are not a
|
|
58
|
+
security boundary: the host controls the machine and can change this
|
|
59
|
+
program. Authenticity requires independent server-side verification. This
|
|
60
|
+
repository cannot establish the coverage or effectiveness of the server's
|
|
61
|
+
hidden model checks. Local repeatability is diagnostic, not attestation.
|
|
62
|
+
- llama.cpp environment settings (`LLAMA_ARG_*`, `LLAMA_LOG_*`,
|
|
63
|
+
`LLAMA_API_KEY`) are not passed to the engine.
|
|
64
|
+
- llama.cpp release archives are checked against the GitHub asset digest when
|
|
65
|
+
available; `--require-checksum` refuses engines without verified archive
|
|
66
|
+
checksums, including custom PATH/environment engines. This is integrity
|
|
67
|
+
checking, not an independently signed authenticity guarantee.
|
|
68
|
+
- The downloaded llama.cpp binary is hashed and recorded in
|
|
69
|
+
`~/.sublet/bin/manifest.json`, then re-checked on use.
|
|
70
|
+
- Only `.gguf` weights are accepted. Pickle, PyTorch, ONNX, and safetensors
|
|
71
|
+
files are refused.
|
|
72
|
+
- `llama-server` is bound to `127.0.0.1`, runs with `--log-disable`, and is not
|
|
73
|
+
exposed to the LAN.
|
|
74
|
+
|
|
75
|
+
## Reporting scope
|
|
76
|
+
|
|
77
|
+
The marketplace server, wallet, and payout infrastructure are a separate
|
|
78
|
+
codebase. This repository covers the terminal agent only.
|