diff --git a/specs/016-mac-spark-hybrid-inference/plan.md b/specs/016-mac-spark-hybrid-inference/plan.md new file mode 100644 index 0000000..311af93 --- /dev/null +++ b/specs/016-mac-spark-hybrid-inference/plan.md @@ -0,0 +1,199 @@ +# Plan: Hybrid Apple Silicon + DGX Spark inference research (016) + +**Feature**: 016-mac-spark-hybrid-inference +**Spec**: [spec.md](./spec.md) +**Date**: 2026-08-20 +**Status**: Specified / not executed. Human go required before Phase A. + +## 1. Two planes (do not mix them) + +``` +desk today + Mac ── Ethernet / Tailscale ── Spark agent plane (already real) + get_agent() / Hermes + Grok default; optional ~30B slot + +research (not green) + Spark CUDA ds4 ── disk .kv file ──► Mac Metal ds4 Phase B, gated + Mac Metal memory ⇄ USB-C RDMA ⇄ Spark CUDA memory MCDMA, watch only +``` + +The agent plane does not wait on this research. KV / tensor work, if it ever +ships, is a **second** plane. Do not route Hermes over MCDMA. Do not treat +file copy as RDMA. + +## 2. What already exists + +- Spec 007 living slot: one local generative LLM on the Spark; CPU STT/TTS; + hosted Grok for quality-critical roles; optional ~30B-class worker. +- Spec 008 spoken loop: I/O sibling + `get_agent()`. Sentence-chunked TTS and + `tts_ttfa_ms` already exist **there**. +- Mac ↔ Spark agent use over Ethernet / Tailscale. + +This plan does not re-tune those paths. + +## 3. MCDMA — watch, do not implement + +Public 2026-08 posts describe Metal↔CUDA RDMA over USB-C. Closed source until +the author publishes source and license. + +**Do:** + +- Watch the public account for OSS + license. +- Keep author-reported BW / RTT labeled **author-reported** (spec table). +- If source + license land: clone **their** tree, run **their** tests on + one Spark + Apple Silicon Mac in the lab, write a decision memo. Still not + a Hermes cutover. + +**Do not:** + +- Start an implementation repo, bindings layer, or “thin wrapper” while the + code is closed. +- Build two-Spark CX7 fabric. +- Treat 939 MB/s / 24 µs (or any other author figure) as a lab result. +- Block ds4 Phase A on MCDMA. The tracks are independent. + +On OSS drop the first honest work is a single-link bench, then a toy tensor +or KV shuttle — **after** a human go, **after** license review. Prefer +upstream hooks over a lab fork. + +## 4. ds4 spike — plan only until go + +Execution is **out of tree**. Do not vendor [antirez/ds4](https://github.com/antirez/ds4) +into `thelab`. This repo keeps SDD only. + +### Human gates + +| Gate | Who | Unlocks | +|------|-----|---------| +| Specify (this folder) | Done | Nothing executable | +| **Go Phase A** | Human | Spark-only CUDA + disk KV + loopback server | +| **Go Phase B** | Human, after A green | File-based Spark-prefill → Mac-decode + identity | +| Hermes default change | Out of scope for the spike | — | + +An agent must not clone, download weights, or start `ds4-server` from this +card without the Phase A go. + +### Phase A — Spark-only CUDA (must pass) + +Goal: prove ds4 is usable on this Spark for Flash q2 with disk KV and a +localhost server. No Mac. No RDMA. + +Suggested sequence after go: + +1. **Quiesce the 007 slot.** Unload / stop the local Nemotron (or any other + serious generative LLM). Sequential, not stacked. Do not delete models. +2. **Clone + pin.** `git clone https://github.com/antirez/ds4.git` outside + this repo. Record `git rev-parse HEAD`. `make cuda-spark`. If the Spark + target fails, capture the log; do not silently switch to a generic CUDA + target without checking GB10 flags. +3. **Weights.** `./download_model.sh ds4f-q2` only. Skip PRO, MXFP4, and + DSpark until CLI + disk KV are green. Engine loads **ds4 GGUFs only**. +4. **CLI smoke.** Load Flash q2 at a modest context (start around 8k). + Greedy (`--temp 0`) short prompt. Pass = completes without OOM or driver + crash; host stays interactive. +5. **Disk KV + server.** Dedicated on-host directory with an explicit size + cap (`--kv-disk-dir`, `--kv-disk-space-mb`). + `./ds4-server ... --host 127.0.0.1 --port 8090` + Cold chat completion → restart process → same prefix should hit disk KV. + Do not bind `0.0.0.0`. +6. **Notes + teardown.** Record commit, quant, pass/fail, blockers. Stop + `ds4-server`. Restore the 007 agent path (Ollama / ~30B or hosted Grok). + +Spark is single-GPU: no `--cuda-tensor-parallel`. + +Phase A exit: all of the above, or an explicit fail with a blocker. Phase A +fail → no Phase B. + +### Phase B — optional file-based handoff + +Proceed only if Phase A is green, identical GGUF can live on both boxes, and +a human still wants the experiment. + +1. **Same commit, same GGUF.** Metal `make` on the Apple Silicon Mac in the + lab. Checksum the GGUF against Spark. +2. **Same-machine baselines** before handoff: greedy tokens for prompt P on + Mac-only and Spark-only. Document backend delta so handoff noise is + separable. +3. **Handoff.** Spark prefills P and writes disk KV. Copy the KV artifact + over the existing network (Wi-Fi first). Mac loads KV and decodes + **without** prefilling P. +4. **Identity gate.** ≥99% greedy token match vs Mac-local full prefill + (temperature 0, continuation length N recorded in notes, e.g. 64 or 128). + Fail closed on miss — that is a valuable negative result. +5. **Timing only after the gate.** Small contexts first (8k, 32k — not a + 500k safari). Compare time-to-first-decode-token: + Spark prefill + ship + Mac load vs Mac-local prefill. Then Mac decode. + Schedule 10GbE only if identity holds and ship time dominates. + +Pacary shipping projections and tweet tok/s figures stay **author-reported**. +Upstream README GB10 / Metal tables stay **upstream-reported**. Our numbers +are whatever Phase A/B notes record after go. + +### Decision tree after a real spike + +``` +Phase A fail → document blocker; no Mac work +Phase A pass, no Mac → optional loopback ds4-server for DeepSeek-shaped + research/coding only; still not Hermes default +Phase B identity fail → keep Spark-only; negative result is the finding +Phase B identity pass + + ship < Mac prefill → interesting hybrid; write a follow-up spec + + ship > Mac prefill → interesting science; not a daily driver without + a faster second plane (still not MCDMA-by-hope) +``` + +## 5. Slot, voice, and defaults + +| Rule | Plan consequence | +|------|------------------| +| 007 one local LLM | Flash q2 **is** the occupied slot while the spike runs | +| No 120B+ | Unchanged | +| No Hermes profile cutover | Do not point architect/coder/reviewer at ds4-server | +| 008 TTFA | Hybrid decode is not the spoken path in this plan. If a later spec + proposes it, first-audio must not regress; measure `tts_ttfa_ms` before + calling it a win | +| Second plane | Tailscale/Ethernet agents keep working if ds4 is down | + +## 6. What we will not do in this plan + +- Execute Phase A or B from this folder without a human go. +- Copy research-note trees, home-directory layouts, or cache paths into git. +- Implement RDMA, wrap a closed MCDMA binary, or start a lab MCDMA repo. +- Two-Spark CX7 fabric. +- Multi-tenant `ds4-server`. +- Stack ds4 + full local Nemotron. +- Change Hermes profile defaults. +- Vendor `ds4` as a submodule of `thelab`. +- Claim author or upstream benches as ours. +- Open `ds4-server` on a non-loopback bind as part of the spike. +- Use hybrid decode on the 008 loop “to try it” without a TTFA comparison. + +## 7. Risks + +| Risk | Mitigation | +|------|------------| +| Unified-memory fight with the 007 slot | Sequential load; teardown restores agents | +| Closed MCDMA copied or wrapped | Watch-only until source + license | +| False hybrid speed claims | Identity ≥99% before any timing narrative | +| KV not portable CUDA → Metal | That is Phase B; fail closed | +| Beta `main` churn | Pin commit after green smoke | +| Policy leak into Hermes | Explicit non-goal; review rejects default edits | +| Voice first-audio regression | Hybrid is not the live I/O path; 008 gate if it ever is | +| Docs imply it already runs | Status line on every file in this folder | +| Disk fill from KV | Size-capped dedicated directory | +| Author numbers become “our” numbers | Citations section; labels on every borrowed figure | + +## 8. Success + +A developer reading this folder can say: + +- Hybrid Metal/MLX + CUDA is the north star for **KV/tensor research**. +- MCDMA is watch-only; numbers in the spec are author-reported. +- ds4 is a planned spike, not executed, not the agent backend. +- Phase A is Spark-only; Phase B is file KV with an identity gate. +- 007 slot and 008 TTFA still constrain any future green light. +- Nothing here was committed as running code. + +Phase A/B success criteria live in [spec.md](./spec.md) and +[tasks.md](./tasks.md). They stay unchecked until a human go and real notes. diff --git a/specs/016-mac-spark-hybrid-inference/spec.md b/specs/016-mac-spark-hybrid-inference/spec.md new file mode 100644 index 0000000..ce04d80 --- /dev/null +++ b/specs/016-mac-spark-hybrid-inference/spec.md @@ -0,0 +1,332 @@ +# Feature Spec: Hybrid Apple Silicon + DGX Spark inference research + +**Feature ID**: 016-mac-spark-hybrid-inference +**Status**: Specified / not executed. Human go required before Phase A. +**Created**: 2026-08-20 +**Owner**: Derek Clair +**Related**: [007-dgx-hardware-optimization](../007-dgx-hardware-optimization/spec.md) +(one local generative slot), +[008-local-tts-lenovo-go-spike](../008-local-tts-lenovo-go-spike/spec.md) +(TTFA must not regress if hybrid decode is ever used) + +Public sources (not our benches): +[antirez/ds4](https://github.com/antirez/ds4), +[danpacary](https://x.com/danpacary/status/2086851964261003615), +[ashxhart](https://x.com/ashxhart/status/2089749434087227672) + +## Honest current state + +This folder is a **research contract**, not a feature in `thelab_langchain`. + +Nothing in this spec has been executed. There is no ds4 build, no disk-KV +handoff, and no Metal↔CUDA RDMA path in this lab. Do not read the folder as +“hybrid inference is running.” + +| Surface | Today | +|---------|--------| +| Agent path | Unchanged: this repo `get_agent()`; hosted Grok default; optional local ~30B-class on the Spark (spec 007). | +| Voice I/O | Spec 008 sibling. STT/TTS on CPU. Spoken loop does **not** use ds4 or MCDMA. | +| Mac ↔ Spark agents | Ethernet / Tailscale already exists. That is the **agent** plane. | +| ds4 (DwarfStar) | Spike **plan** only. Not cloned, not built, not served. | +| MCDMA | **Watch** only. Closed source until the author publishes source and license. | + +Ethernet/Tailscale stays the agent plane. MCDMA or a ds4 KV ship would be a +**second plane** (tensor / KV), if either ever goes green. They do not replace +the agent network. + +## Overview + +**North star:** evaluate hybrid local inference — Apple Silicon Mac in the lab +(Metal / MLX) plus DGX Spark (CUDA) — especially KV and tensor paths, not +chat-over-LAN. + +Two independent tracks: + +1. **MCDMA** (Metal CUDA Direct Memory Access) — public posts, 2026-08. + Claimed USB-C RDMA between Metal unified memory and CUDA memory. **Watch.** + Do not start an implementation repo until source and license are public. +2. **DwarfStar `ds4`** — public engine at [antirez/ds4](https://github.com/antirez/ds4). + Optional file-based Spark-prefill → Mac-decode, in the shape of the Pacary + experiment. **Plan a spike; do not execute until a human says go.** + +`ds4` does **not** replace the Hermes / thelab agent path (Nemotron ~30B / +Grok). It is a parallel research engine for ds4-specific DeepSeek V4 (and +related) GGUFs. + +## Goals + +- Write down the north star so later work does not silently become “new default + agent backend.” +- Keep MCDMA as watch-only until OSS + license. +- Specify a ds4 spike that is Spark-only first (Phase A), then optional + file-based heterogeneous KV (Phase B). +- Require an identity gate (≥99% greedy token match) **before** any hybrid + speed claim. +- Bind any `ds4-server` to loopback (`127.0.0.1`). +- Obey spec 007: one local generative LLM on the Spark; do not stack ds4 with + a full local Nemotron. +- Protect spec 008: if hybrid decode is ever used on a spoken path, time to + first audio must not regress. + +## Non-goals + +- 120B+ agent loops (spec 007). +- Multi-tenant serving. +- Implementing RDMA / MCDMA ourselves, or wrapping a closed binary. +- Two-Spark ConnectX-7 fabric (this lab is one Spark). +- Changing Hermes profile defaults in the spike. +- Vendoring `ds4` into this repo. +- Replacing Ethernet / Tailscale agent traffic with a KV plane. +- Treating author-reported or upstream README numbers as our benches. +- Wiring hybrid decode into the live 008 voice loop in this spec’s delivery. + +## User stories + +1. As the operator, I can tell a researcher: hybrid Metal/CUDA is a **watch + + planned spike**, not production, and the agent still uses Grok / ~30B. +2. As the person who would run Phase A, I know Spark-only CUDA + disk KV + + localhost server is the whole first gate, and I must not start without a + human go. +3. As the person who might run Phase B, I know file copy of KV is the + experiment, identity comes before speed, and Wi-Fi is first. +4. As a reviewer of git, I reject MCDMA implementation work, 007 slot stacking, + Hermes default cutover, and pasted author benches labeled as ours. +5. As the 008 voice owner, I know this track must not worsen `tts_ttfa_ms` if + it ever touches the spoken path. + +## Two tracks (do not merge them) + +``` +existing agent plane + Mac ── Ethernet / Tailscale ── Spark (Hermes / get_agent(); already real) + +research second plane (not green) + (A) ds4 file KV: Spark CUDA prefill → disk .kv → copy → Mac Metal decode + (B) MCDMA: Metal ↔ CUDA RDMA over USB-C [watch; closed] +``` + +MCDMA is not “faster ds4.” File-based KV is not a substitute for RDMA. Prove +or reject each on its own evidence. + +### Track 1 — MCDMA (watch) + +Public posts describe: + +- Registered memory and rkeys +- One-sided READ/WRITE +- Two-sided SEND/RECV with credit-based flow control +- Symmetric verbs (no master/slave) +- Transport: USB-C (author: USB3-class rates today; USB4 if a locked + controller can train) + +**Our topology if it ever opens:** one Spark + Apple Silicon Mac in the lab +over USB-C. The author’s two-Spark CX7 + dual USB-C Studio diagram is +**reference only**. We do not build that fabric. + +Author-reported figures from the 2026-08 public post +([ashxhart](https://x.com/ashxhart/status/2089749434087227672)). **Unverified +here. Label as author-reported. Do not treat as our benches.** + +| Metric (author-reported) | Value | +|--------------------------|-------| +| Single USB-C link | 939 MB/s | +| Mac → both Sparks, concurrent | 1.80 GB/s | +| Both Sparks → Mac, concurrent | 1.25 GB/s | +| Round-trip | 24 µs | +| Small-message rate | 41k msg/s | + +“Every byte delivery verified” is the author’s claim. Reproduce only after +source + license are public. + +**Standing rule:** do not start an implementation repo until source and +license are public. Prefer upstream integration (when it exists) over a +closed-source fork. + +### Track 2 — ds4 spike (plan, not executed) + +[antirez/ds4](https://github.com/antirez/ds4) is a narrow native engine +(Metal / CUDA / ROCm) for ds4-specific GGUFs — not a general llama.cpp zoo, +not a Nemotron loader. Spark target: `make cuda-spark`. Mac target: Metal +`make`. Surfaces: CLI, `ds4-server` (OpenAI- and Anthropic-style HTTP), +optional agent binary. First-class **disk KV** (content-addressed prefix +files) is why a file-based handoff is even thinkable. + +Pacary’s public experiment +([danpacary](https://x.com/danpacary/status/2086851964261003615)): Spark +prefill, Mac decode, same byte-identical GGUF, ship disk KV, Wi-Fi then +10GbE. Shipping-time projections and tweet prefill rates in that post are +**author-reported, not our benches.** We adopt the **correctness gate**, not +the speed narrative: + +> A handed-off cache must produce ≥99% token-identical greedy output vs +> prefilling locally. Correctness first, then speed. + +Upstream `ds4` README GB10 vs Metal tables are **upstream-reported**, not +lab results. They motivate why Spark-prefill / Mac-decode is interesting +(CUDA prefill vs Metal decode asymmetry). They are not a substitute for +Phase A notes. + +#### Phase A — Spark-only (must pass before any Mac work) + +- CUDA build (`make cuda-spark`); record commit SHA. +- Flash q2 weights only for the first spike (skip PRO / MXFP4 / tensor-parallel). +- CLI greedy short prompt succeeds. +- Disk KV: cold prefill → process restart → warm prefix hit. +- `ds4-server` chat completion on **localhost**. +- Teardown: stop the server; restore the 007 agent slot (do not leave Flash + resident next to Nemotron). + +Spark is a **single-GPU** target. Do not pass CUDA multi-GPU tensor-parallel +flags on this box. + +#### Phase B — optional file-based handoff (separate human go) + +Only if Phase A is green **and** a human wants Mac time. + +- Same commit and **byte-identical** GGUF on Mac and Spark (checksum). +- Mac Metal build. +- Spark writes KV for prefix P; file lands on the Mac; Mac decodes + continuation **without** prefilling P. +- Identity: ≥99% greedy token match vs Mac-local full prefill of P + (same prompt, temperature 0, fixed continuation length recorded in notes). +- Fail closed: if the gate fails, stop speed work. A negative result is + still a result. +- Transfer timing on the existing network first; 10GbE tuning is deferred + until identity passes. +- No “faster E2E” claim until identity **and** a timed comparison: + `(Spark prefill + ship + Mac load)` vs `Mac-local prefill`, plus Mac decode. + +Phase B is file copy. It is not MCDMA. + +## Functional requirements + +### FR-1 Status honesty + +Docs, PRs, and commit messages **MUST** say specified / not executed until +Phase A notes exist. Do not imply a live hybrid path. + +### FR-2 MCDMA is watch-only + +- No implementation repo, bindings, or vendored blob while source or license + is unpublished. +- Author-reported BW / RTT **MUST** stay labeled author-reported. +- Two-Spark CX7 + dual Mac links are out of scope. + +### FR-3 ds4 does not replace the agent path + +- Hermes / thelab defaults stay Grok (quality-critical) and ~30B-class local + (optional worker). +- `get_agent()` is unchanged by this spec. +- A localhost `ds4-server`, if it ever stands, is a **named research/coding + endpoint**, not a silent profile cutover. + +### FR-4 One-slot rule (007) + +- Do not load ds4 Flash and a full local Nemotron (or any second serious + generative LLM) at the same time on the Spark. +- Sequential use: quiesce the occupied slot, run the spike, teardown, restore. +- No 120B+ loops. + +### FR-5 Loopback bind + +- `ds4-server` **MUST** bind `127.0.0.1` unless a later spec explicitly opens + a firewalled bind (still not a public bind by default). +- Proposed research port if executed: `8090` on loopback. Not baked into this + package. + +### FR-6 Identity before speed + +- Phase B **MUST NOT** publish speed comparisons until ≥99% greedy token + identity vs the Mac-local prefill baseline. +- Tweet / README prefill and decode rates are citations, not results. + +### FR-7 Voice TTFA (008) + +- Live spoken path stays 008 until a later spec says otherwise. +- If hybrid decode is ever used on that path, `tts_ttfa_ms` / time-to-first-audio + **MUST NOT** regress vs the then-current all-on-Spark (or hosted) loop. +- Fail the hybrid voice idea rather than ship a slower first chunk. + +### FR-8 Execution is out of tree + +- Clone and build `ds4` outside this repo. Do not submodule it here. +- Spike notes (commit, quant, pass/fail, identity %) stay out of git if they + include prompts, transcripts, host identifiers, or hardware serials. +- This folder remains the SDD; it is not the run log. + +### FR-9 Second plane + +- Agent RPC stays on the existing Ethernet / Tailscale path. +- KV / tensor research, if green, is a second plane. Do not collapse the two. + +## Non-functional requirements + +- No secrets, serials, household identifiers, chat-product routing, lab IPs + (including RFC1918), or required hostnames in this spec, plan, or tasks. +- Hardware in prose: “DGX Spark” and “Apple Silicon Mac in the lab.” Do not + inventory a named personal Mac generation or a return date. +- Disk KV lives in a **dedicated on-host directory with a size cap**, not a + path committed here. +- Beta engine: pin a commit after a green smoke; do not chase `main` mid-spike. +- Weights: only `download_model.sh` targets. First spike = Flash q2. +- Privacy: prompts used for identity tests stay on-host; do not commit them. + +## Acceptance criteria + +- [x] Spec states north star, two tracks, and honest “not executed” status. +- [x] MCDMA is watch-only; author-reported numbers labeled; no implementation + repo until source + license. +- [x] ds4 Phase A (Spark-only) and Phase B (optional file KV) are specified + with the ≥99% identity gate. +- [x] `ds4-server` loopback bind and 007 one-slot rule are written down. +- [x] ds4 does not replace Hermes / thelab (Nemotron ~30B / Grok). +- [x] 008 TTFA non-regression is named if hybrid decode is ever used. +- [x] Non-goals include 120B+, multi-tenant, implementing RDMA, two-Spark + CX7, and Hermes default changes. +- [ ] Phase A executed — **not done** (human go required). +- [ ] Phase B executed — **not done**. +- [ ] MCDMA OSS evaluation — **not done** (blocked on public source + license). + +## Seams this package must keep stable + +| Seam | Contract | +|------|----------| +| `get_agent(user_id)` | Unchanged. No ds4 or MCDMA side-effect. | +| Spec 007 slot | One local generative LLM; ds4 occupies it if loaded. | +| Spec 008 I/O | Still the spoken path; TTFA protected. | +| Agent network | Existing Ethernet / Tailscale. | +| `ds4-server` | Loopback only if/when executed; not this package. | + +## Relationship to other specs + +- **007** — one-slot policy. This spike **is** occupying the slot while Flash + is loaded. Stacking with Nemotron is a 007 violation. 120B+ remains + forbidden as a daily loop. +- **008** — live voice. Hybrid decode is not the desk loop. If it ever is, + first-audio latency is a hard gate. +- **001** — long-term desktop voice. This research does not revive Riva/NIM + compose as production. +- **012 / 009** — fleet roles and architect/coder handoff stay on Grok / + existing local workers. Do not retarget profiles at ds4 in the spike. +- **015** — if any hybrid timings are exported later, they are content-free + durations only. This spec does not add OTEL. + +## Open questions (do not block specifying; do block speed claims) + +- Exact `ds4` flags to **export** a KV file another backend will accept + (confirm in `--help` / source during Phase A). +- Whether CLI session KV and server KV files are the same format for handoff. +- Whether CUDA-written KV is portable to Metal at all (that **is** Phase B). +- Standing localhost server after a green Phase A: optional, still not Hermes + default, still loopback, still sequential with the 007 slot. +- MCDMA OSS date and license: unknown. Watch; do not schedule implementation. + +## Citations + +Use as **sources**. Do not claim this lab reproduced them. + +- Engine: https://github.com/antirez/ds4 +- Heterogeneous file-KV experiment (author-reported): + https://x.com/danpacary/status/2086851964261003615 +- MCDMA (author-reported): + https://x.com/ashxhart/status/2089749434087227672 diff --git a/specs/016-mac-spark-hybrid-inference/tasks.md b/specs/016-mac-spark-hybrid-inference/tasks.md new file mode 100644 index 0000000..fa6c857 --- /dev/null +++ b/specs/016-mac-spark-hybrid-inference/tasks.md @@ -0,0 +1,116 @@ +# Tasks: Hybrid Apple Silicon + DGX Spark inference research (016) + +**Feature**: 016-mac-spark-hybrid-inference +**Spec**: [spec.md](./spec.md) · **Plan**: [plan.md](./plan.md) +**Status**: Specified / not executed. Human go required before Phase A. + +Checkboxes are honest. Spec-only work can be marked done. Do **not** mark +Phase A, Phase B, or MCDMA evaluation done because public posts or the ds4 +README exist. Do not clone, build, or serve from this list without a human go. + +## Phase 0 — Specify the contract (this folder) + +- [x] Write `spec.md` (north star, two tracks, non-goals, 007/008 binds) +- [x] Write `plan.md` (watch vs spike, planes, decision tree, no execute) +- [x] Write `tasks.md` (this file) +- [x] Label MCDMA BW/RTT as author-reported; cite public posts without + claiming we ran them +- [x] Cite [antirez/ds4](https://github.com/antirez/ds4) as the engine; + Pacary post as the file-KV experiment shape +- [x] Bind `ds4-server` to `127.0.0.1`; 007 one-slot; no Hermes default change +- [x] Privacy: no lab IPs, RFC1918, serials, required hostnames, or cache + paths in this folder + +## Phase 1 — MCDMA watch (not an implementation) + +Policy (specified): + +- [x] Track is **watch only** until source **and** license are public +- [x] Do **not** start an implementation repo while closed +- [x] Do **not** implement RDMA ourselves +- [x] Two-Spark CX7 fabric is out of scope +- [x] Author-reported figures stay labeled; not our benches + +Blocked on OSS (leave unchecked): + +- [ ] Public source + license reviewed +- [ ] Human go to run **upstream** tests on one Spark + Apple Silicon Mac + in the lab (USB-C) +- [ ] Independent single-link BW / RTT capture, labeled as *our* run +- [ ] Decision memo: keep watching vs toy tensor/KV shuttle vs drop +- [ ] Any Hermes or 008 wiring — **forbidden until** a later spec + +Do not treat an OSS rumor as a checkbox. + +## Phase 2 — ds4 Phase A (Spark-only; not started) + +**Human go required.** Out of tree. Do not vendor into `thelab`. + +- [ ] Explicit human go for Phase A +- [ ] Quiesce the 007 slot (no ds4 + full local Nemotron) +- [ ] Clone [antirez/ds4](https://github.com/antirez/ds4) outside this repo; + record commit SHA +- [ ] `make cuda-spark` succeeds (Spark is single-GPU; no CUDA TP flags) +- [ ] `ds4f-q2` only (skip PRO / MXFP4 / DSpark on the first spike) +- [ ] CLI greedy short prompt OK (modest context first) +- [ ] Disk KV: cold prefill → restart → warm prefix hit, in a dedicated + size-capped on-host directory (path not committed here) +- [ ] `ds4-server` chat completion on `127.0.0.1` (proposed port 8090) +- [ ] Notes: commit, quant, pass/fail, blockers (no secrets, no serials, + no prompts in this git tree) +- [ ] Teardown: stop server; restore 007 agent path +- [ ] Decision: stop | optional loopback-only standing server (still not + Hermes default) | ask for Phase B go + +Phase A fail → do not start Phase B. + +## Phase 3 — ds4 Phase B (optional file KV; gated) + +**Separate human go** after Phase A green. + +- [ ] Explicit human go for Phase B +- [ ] Same commit + byte-identical GGUF on Mac and Spark (checksum) +- [ ] Mac Metal build OK +- [ ] Same-machine greedy baselines recorded (Mac-only vs Spark-only) +- [ ] Spark writes KV; file copied over existing network (Wi-Fi first); + Mac decodes without local prefill of that prefix +- [ ] Identity gate: ≥99% greedy token match vs Mac-local prefill + (temp 0; continuation length N in notes) +- [ ] If gate fails: stop speed work; keep Spark-only; record negative result +- [ ] If gate passes: time Spark prefill + ship + Mac load vs Mac-local + prefill (8k / 32k first). 10GbE only if identity holds and ship dominates +- [ ] No “faster E2E” claim until identity **and** that comparison exist +- [ ] Identity notes stay out of this git tree if they include prompts + +Phase B is **file copy**. It is not MCDMA. + +## Out of scope (stay unchecked here) + +- [ ] Implementing MCDMA / RDMA / USB-C verbs +- [ ] Two-Spark ConnectX-7 fabric +- [ ] 120B+ agents +- [ ] Multi-tenant ds4 serve +- [ ] Hermes profile default cutover to ds4 +- [ ] Stacking ds4 with a full local Nemotron +- [ ] Binding `ds4-server` off loopback as part of the spike +- [ ] Vendoring `ds4` into this repo +- [ ] Hybrid decode on the 008 spoken path +- [ ] Claiming author or upstream benches as lab results +- [ ] Changing 007 slot policy or 008 TTFA contract except to obey them + +## Traceability + +| Want | Where it lives | +|------|----------------| +| North star (Metal/MLX + CUDA KV/tensor) | This folder | +| Agent plane Mac ↔ Spark | Existing Ethernet / Tailscale; unchanged | +| One local LLM | Spec 007 | +| Spoken loop + TTFA | Spec 008 sibling | +| ds4 engine | Public `antirez/ds4` (not executed here) | +| File-KV experiment shape | Pacary public post (author-reported) | +| MCDMA | ashxhart public post (watch; author-reported) | +| Hybrid running in this lab | **Does not** | + +Live consume path for agents remains +`get_agent()` (this package) plus the 008 I/O sibling. ds4 and MCDMA are not +on that path. diff --git a/specs/017-mcp-runtime-trust-boundary/plan.md b/specs/017-mcp-runtime-trust-boundary/plan.md new file mode 100644 index 0000000..3942222 --- /dev/null +++ b/specs/017-mcp-runtime-trust-boundary/plan.md @@ -0,0 +1,133 @@ +# Plan: MCP / tool-runtime trust boundary (017) + +**Feature**: 017-mcp-runtime-trust-boundary +**Spec**: [spec.md](./spec.md) +**Date**: 2026-08-20 +**Status**: Specified. Linux POC out of tree. Not wired into `get_agent()`. + +## 1. What this plan is + +A map of **where the trust boundary sits** and **what the five detection +classes mean**. It is not a plan to add sensors, an MCP client, or a +security node to `thelab-langchain`. + +Success is an honest SDD: the desk treats MCP stdio / tool exec as a +process boundary; defense in depth is runtime watching; the POC stays out +of tree; this package does not pretend to run it. + +## 2. Boundary (do not collapse it) + +``` +desk agent (Hermes, IDE, or future graph tools) + │ + │ exec / MCP stdio command + ▼ +┌───────────────────────────────────────────┐ +│ tool process ← TRUST BOUNDARY │ +│ children · config · env · network │ +└───────────────────────────────────────────┘ + │ + │ host observations (POC out of tree) + ▼ + five detection classes → content-free alert (015) +``` + +`get_agent()` today sits **above** that picture. It compiles a graph with +memory tools (014). It does not launch MCP servers. The voice sibling +calls `get_agent()` (008) and also does not run these sensors. + +Collapsing the boundary into “MCP JSON looks fine” or “a future protocol +release will patch it” is a failed design. + +## 3. Five classes (distill only) + +Implementations stay out of tree. This table is the contract. + +| Class | Signal (idea) | Not the signal | +|-------|----------------|----------------| +| Shell metacharacters in child cmdline | Child cmdline of a tool/MCP parent contains shell operators | Full argv dump in git or on the wire | +| Unexpected subprocess vs allow-list | Child binary not on that server’s expected list | A global “malware” catalog in this repo | +| Config file integrity | Watched MCP/tool-host config diverges from a known-good hash | Pasting the new file (secrets) into an alert | +| New-server network watch | Shortly after register/start, outbound peer not on allow-list | Lab IPs, household names, or a full netflow archive | +| Dangerous env var changes | Loader/interpreter-related env changed after snapshot | Dump of the whole environment | + +Host techniques the POC *may* use (process table, file watch, connection +table, environ snapshot) are **examples of where to look**. This plan does +not copy scripts, regexes, or audit rule files. + +## 4. What already exists vs what does not + +| Mechanism | Status | +|-----------|--------| +| This SDD folder | This plan | +| Linux POC (five classes) | Out of tree. Prototype. Detection, not prevention. | +| Workstation MCP processes | Real, outside this package | +| Sensors in `thelab_langchain` | **Not done** | +| MCP client in `get_agent()` | **Not done** (and not this spec’s delivery) | +| Content-free alert path for these classes | Specified via 015; **not** implemented here | +| CVE database / KEV ingest in this repo | **Never** under 017 | + +Do not check off “workstation is monitored” because the POC directory +exists on a research machine. + +## 5. Sequence (if anyone implements later) + +Not a commitment. Default order: + +1. **Keep sensors off this package.** Prefer a host-side watcher next to + the actual MCP/tool processes (workstation runtime), not a LangGraph + node. +2. **Do not import the POC.** Re-implement against the five classes, or + keep the POC where it is. Do not vendor it into `src/thelab_langchain/`. +3. **Alerts obey 015.** Rule id + counts/flags + generic names. No + cmdline-with-args, no config diffs, no transcripts, no keys, no lab IPs. +4. **Fail open.** A dead sensor does not break `get_agent()` or the voice + loop. +5. **013 before widening the graph.** Adding MCP or shell tools to + `ToolNode` is a new spec plus reviewer security lens. 017 is not + permission to add them. + +Prevention (kill, freeze config, netns) is a **different** spec with an +operator model. This plan stays detect-and-record. + +## 6. Binding 013 and 015 + +| Spec | Binding | +|------|---------| +| **013** | Tool/exec boundary is security-sensitive. Rubber-stamp is a failed review. Findings: severity, location, fix guidance; no secret dumps. | +| **015** | Sensor alerts are remote/ops data. Tier A only unless a later spec says otherwise. Allow-list fields; ignore unknown keys. No chat-product routing. | + +Do not “debug” a class-1 hit by shipping the child cmdline to a collector. +On-host logs, if kept, follow 015’s on-host vs off-host split. + +## 7. What we will not do in this plan + +- Copy POC Python, tests, or audit rules into this repo. +- Copy research-cycle SUMMARY files, NVD/KEV JSON, or CVE tables. +- Repeat unverifiable stats or flaw chains the research STATUS already + flagged. +- Claim `thelab_langchain` implements the five classes. +- Wire a monitor into `get_agent()` under 017. +- Put keys, IPs, Slack, or household identifiers in this folder. +- Invent detection rates or coverage percentages. + +## 8. Risks + +| Risk | Mitigation | +|------|------------| +| Docs imply the brain already watches MCP | Status line on every file in this folder | +| POC copied “for convenience” | FR-7: idea only; no vendor | +| Protocol patch treated as the control | FR-1 / FR-2: process effects + runtime watch | +| Alert contains secrets or transcripts | FR-5 + spec 015 | +| Reviewer skips the boundary on a tool PR | FR-6 + spec 013 | +| Short-lived children missed by polling | Residual risk named; do not claim prevention | +| Unverifiable research stats leak into SDD | Explicit refuse list in the spec | + +## 9. Success + +- A developer reading this folder can say: tool/MCP exec is a process + boundary; five classes exist as a POC elsewhere; this package does not + run them; `get_agent()` is unchanged. +- Git history of `thelab` is not loaded with CVE catalogs, POC source, or + secret-bearing alert examples. +- Review of later tool-runtime work has a named boundary to tick (013). diff --git a/specs/017-mcp-runtime-trust-boundary/spec.md b/specs/017-mcp-runtime-trust-boundary/spec.md new file mode 100644 index 0000000..90b25f4 --- /dev/null +++ b/specs/017-mcp-runtime-trust-boundary/spec.md @@ -0,0 +1,299 @@ +# Feature Spec: MCP / tool-runtime trust boundary + +**Feature ID**: 017-mcp-runtime-trust-boundary +**Status**: Specified. Linux POC exists out of tree. **Not** implemented in +`thelab-langchain`. **Not** wired into `get_agent()`. +**Created**: 2026-08-20 +**Owner**: Derek Clair +**Related**: [013-reviewer-quality-gate](../013-reviewer-quality-gate/spec.md), +[015-content-free-telemetry](../015-content-free-telemetry/spec.md), +[014-memory-injection-graph](../014-memory-injection-graph/spec.md) + +## Record-keeping note + +This spec records a **workstation design**: MCP stdio and other tool +runtimes are a **trust boundary**. A tool process can spawn children, touch +config, set environment variables, and make network calls. Defense in depth +is **runtime monitoring of what that process does**, not a bet that “the +protocol will be patched.” + +A Linux proof-of-concept with five detection classes exists **outside this +repository**. This folder distills the boundary and those classes. It does +**not** vendor the POC, copy research-cycle dumps, or import CVE catalogs. + +This package has **no** MCP client, **no** host sensors, and **no** +LangGraph node that watches tool processes. `get_agent()` still binds +memory tools only (spec 014). Do not read this folder as “the brain now +monitors MCP.” + +Do **not** copy POC source, audit rule files, NVD/KEV JSON, or research-cycle +SUMMARY notes into this tree. + +## What this spec refuses to record + +The research tree that produced the POC also accumulated CVE lists, CVSS +scores, and cycle stats. Some of those notes are flagged **in that tree** as +unverifiable or hallucinated. This SDD **does not** restate them. + +Out of this folder (and out of this git repo) forever: + +- CVE identifiers, CVSS numbers, or catalog tables +- Unverifiable counts or “instances affected” figures +- Flaw-chain narratives that the research STATUS already marked unverifiable +- Alert routing to chat products +- Keys, tokens, lab IPs, household identifiers, or private host paths as + required layout + +The design below stands without those claims. + +## Overview + +On the agent workstation, **tool execution is not a library call with a +pretty schema**. MCP stdio starts a command. That child is a real OS +process. So is any other tool runner that shells out or execs a server. + +Once running, that process can: + +- spawn further children +- read or rewrite client/server config +- change its environment +- open network connections + +Those abilities are the **trust boundary**. Protocol-level review of MCP +messages is useful and is **not** sufficient. A later protocol revision does +not replace host-side observation. + +Defense in depth for this desk: + +1. **Treat the tool process as untrusted relative to the operator session.** +2. **Watch runtime effects** (children, config, env, new-server network). +3. **Keep alerts content-free** (spec 015). +4. **Do not rubber-stamp** changes that widen this boundary (spec 013). + +The five detection classes below are the recorded sensor *ideas*. They are +implemented in an out-of-tree Linux POC. They are **not** shipped here. + +``` +operator / orchestrator + │ + ▼ + tool runtime (MCP stdio, or any exec/shell tool) + │ + ├── children (cmdline, unexpected binaries) + ├── config files + ├── environment + └── network (especially a newly registered server) + │ + ▼ + host sensors (POC out of tree; not in get_agent()) + │ + ▼ + content-free alerts (015) — no secrets, no transcripts +``` + +## Goals + +- Name MCP stdio / tool execution as a trust boundary on this workstation. +- Record that defense in depth is runtime monitoring, not protocol hope. +- Distill five detection classes without vendoring POC code. +- Stay honest: specified; POC out of tree; this package does not implement + sensors; `get_agent()` is not wired to them. +- Bind 013 (security-sensitive review) and 015 (alerts must not carry + secrets or transcripts). + +## Non-goals + +- Implementing sensors, auditd loaders, eBPF, or an MCP client in + `thelab-langchain`. +- Wiring a monitor into `get_agent()`, `ToolNode`, or the voice sibling. +- Copying POC source, test suites, or generated audit rules into git. +- A CVE program, KEV tracker, or vulnerability database in this repo. +- Claiming the workstation is “protected” because a POC exists. +- Prevention (kill/quarantine) as a shipped control — the recorded POC is + **detection**. +- Slack, PagerDuty, or any chat product as an alert sink. +- Changing memory-tool behavior (014) except to note that tool invoke is + already a boundary, currently HTTP memory rather than MCP stdio. + +## Domain terms (define once) + +| Term | Meaning | +|------|---------| +| **Trust boundary** | The line where the agent (or MCP client) starts a process whose OS effects are no longer “just a function return.” Children, config, env, and network on the other side are in scope. | +| **Tool runtime** | Anything that execs or shells a tool: MCP stdio servers, LangGraph `ToolNode`, CLI tool hosts. This spec is about that runtime, not about a particular vendor product. | +| **MCP stdio** | MCP transport that launches a server as a child command and talks over stdio. The child is a host process. | +| **Detection class** | One kind of runtime check. Five are recorded here. Not a CVE. | +| **Allow-list** | Expected child executables (or expected endpoints) for a given server. Unknown is alert-worthy. | +| **Sensor** | Host-side watcher that implements a detection class. Lives out of tree today. | +| **Content-free alert** | A signal that a class fired, with low-cardinality labels (rule id, generic process name, path *class*). No secrets, transcripts, tool argument bodies, or config diffs that may contain tokens (015). | +| **Out of tree** | Not in this git repository. The Linux POC is research/workspace code, not a `thelab` module. | + +Do not call a protocol changelog “the fix.” Do not call the POC “production +monitoring.” Do not call `get_agent()` an MCP host — it is not. + +## Detection classes (ideas, not source) + +Five classes. Names are the design. Do not paste POC implementations. + +| # | Class | What to watch | +|---|--------|----------------| +| 1 | **Shell metacharacters in child cmdline** | A child of a tool/MCP process whose command line contains shell operators that look like injection (for example `;`, `\|`, `&&`, backticks, `$(…)`). | +| 2 | **Unexpected subprocess vs allow-list** | A child executable that is not on the server’s expected list. | +| 3 | **Config file integrity** | MCP (or tool-host) config files changing away from a known-good baseline, especially from a process that is not a known editor. | +| 4 | **New-server network watch** | Shortly after a server is registered or first started, outbound connections that are not on an allow-list (unknown peers, not loopback). | +| 5 | **Dangerous env var changes** | After a baseline snapshot, changes to process environment that alter loader or interpreter behavior (class examples: `LD_PRELOAD`, `LD_LIBRARY_PATH`, `PYTHONPATH`, `NODE_PATH`). | + +These are **host observations**. They do not parse MCP JSON-RPC as the +primary control. They do not require a CVE id to fire. + +Residual limits (name them; do not hide them): + +- Detection is not prevention. A short-lived child can exit before a poll. +- Allow-lists can be wrong or incomplete. +- Config watch without a baseline is noise. +- Class 4 is a **new-server window**, not a full network IDS. + +## Honest current state + +| Surface | Today | +|---------|--------| +| This spec folder | Contract and distillation | +| Linux POC | Out of tree. Five classes as a prototype. Not a product. | +| `thelab_langchain` | **No** sensors. **No** MCP stdio client. | +| `get_agent()` | Memory tools + `ToolNode` only (014). **Not** wired to the POC. | +| Workstation MCP (Hermes and similar) | Real tool processes on the desk. Outside this package. The boundary still applies there. | + +`get_agent()` tool invoke is already a **smaller** boundary: memory tools +talk to a network store and fail open (014). That is not MCP stdio, and this +spec does not add monitoring around it. + +## Functional requirements + +### FR-1 Boundary is process effects + +- MCP stdio and any exec/shell tool runtime **MUST** be treated as a trust + boundary: children, config, environment, and network are in scope. +- Reviewers and architects **MUST NOT** treat “the protocol will be patched” + as the only control. + +### FR-2 Runtime monitoring is the defense-in-depth layer + +- The recorded control is **watching runtime effects**, not a protocol + patch, not a prompt filter, and not a CVE chase. +- Sensors, when they exist, run **beside** the tool process (host), not as + a required step inside `get_agent()`. + +### FR-3 Five classes + +- The distilled class list is the five rows above. +- A later implementation **MAY** refine signals. It **MUST NOT** drop a + class silently without a spec change. +- This package **MUST NOT** claim those sensors are present until they live + in a named, accepted follow-up and actually run. + +### FR-4 Honesty of this package + +- Docs **MUST NOT** claim `thelab-langchain` monitors MCP or tool processes. +- `get_agent()` **MUST NOT** grow an MCP client or sensor loop under this + spec. +- Do not vendor POC source into `src/` “as a port.” + +### FR-5 Alerts follow 015 + +If sensors emit (out of tree or in a later spec): + +- **MUST NOT** include transcripts, prompts, completions, tool argument or + result bodies, API keys, tokens, `.env` values, or config file contents + that may hold secrets. +- **MUST NOT** include lab IPs or household identifiers (015 FR-2). +- **MAY** include: rule class id, generic process name, boolean/count, path + *class* (for example “mcp-config”), not a dump of the file. +- Unknown payload keys are ignored. Allow-list, not redaction. +- Do not route those alerts to a chat product from this spec. + +### FR-6 Review (013) + +- Tool/exec boundaries are **security-sensitive**. +- A spec or PR that adds MCP, shell tools, or a wider `ToolNode` **MUST NOT** + be rubber-stamped. Reviewer applies the security and privacy lenses (013). +- Findings **MUST NOT** paste secrets or cmdline dumps that contain secrets. + +### FR-7 Out-of-tree POC is not this repo + +- Point at the **idea** of the five classes. +- Do not copy POC Python, audit rule files, or demo transcripts into git. +- Do not make a private research path required layout for this package. + +## Non-functional requirements + +- No keys, IPs, Slack, household identifiers, or CVE dump tables in this + spec, plan, or tasks. +- No invented detection rates, false-positive percentages, or “instances + protected.” +- Sensors, if later built, must fail open relative to the voice loop and + `get_agent()`: a down monitor does not break the turn (same fail-open + spirit as 015 / 014). +- SDD in this folder stays human prose. Do not vendor skill bodies. + +## User stories + +1. As the person at the desk, I know a tool/MCP child is a real process, not + a sandboxed RPC. +2. As an operator, I know defense in depth is watching children, config, + env, and new-server network — not waiting for a protocol patch. +3. As a developer of this package, I know `get_agent()` does not host MCP + and does not run those sensors. +4. As a reviewer (013), I do not approve a wider tool/exec boundary without + the security lens. +5. As an operator of telemetry (015), an alert that a class fired does not + ship my conversation or my keys. + +## Acceptance criteria (for this SDD record) + +- [x] This folder contains `spec.md`, `plan.md`, and `tasks.md` that name + the trust boundary, runtime monitoring as defense in depth, and the + five detection classes. +- [x] Status is specified; POC out of tree; not a `thelab-langchain` + feature; not wired into `get_agent()`. +- [x] Related specs 013 and 015 are cited (no rubber-stamp; content-free + alerts). +- [x] No CVE tables, CVSS scores, unverifiable stats, POC source, keys, + IPs, or chat-product routing in this folder. +- [ ] Sensors in this package — **not done**. +- [ ] Wiring into `get_agent()` — **not done** (out of scope for 017). + +## Seams this package must keep stable + +| Seam | Contract | +|------|----------| +| `get_agent(user_id)` | Unchanged. No MCP client. No sensor side-effect. | +| `ToolNode` / memory tools | Still 014: profile, recall, store; fail-open invoke. Not an MCP host. | +| Workstation MCP | Outside this package. Boundary still applies. | +| Sensor process | Out of tree. Not imported by this package. | +| Alert payload | 015 allow-list if anything is exported. | + +## Relationship to other specs + +- **013** — Tool/exec boundaries are security-sensitive. Reviewer does not + rubber-stamp them. This spec is *what* the boundary is; 013 is *how* + review treats it. +- **015** — Telemetry and alerts are content-free. Sensor output is not a + transcript archive and not a secrets store. +- **014** — Shipped graph. Memory `ToolNode` is the only tool runtime in + this package today. 017 does not add tools. +- **010** — Secrets stay off the board; same classes stay out of findings + and alerts. +- **012** — Fleet dispatch. This spec does not add a “security scanner” + roster slot or a dispatcher in this package. +- **008** — Voice I/O calls `get_agent()`. I/O does not become the MCP + monitor. + +## Open questions + +- Whether a later spec should run host sensors next to Hermes (workstation) + rather than inside this Python package. Default: **workstation / out of + tree, not `get_agent()`.** +- Whether a later spec should add MCP tools to the graph at all. Default: + **not in 017.** If it happens, 013 + this boundary apply first. +- Prevention (kill child, freeze config) vs detect-and-alert. Default: + **detection only**, until a later spec with an explicit operator model. diff --git a/specs/017-mcp-runtime-trust-boundary/tasks.md b/specs/017-mcp-runtime-trust-boundary/tasks.md new file mode 100644 index 0000000..1aac7bf --- /dev/null +++ b/specs/017-mcp-runtime-trust-boundary/tasks.md @@ -0,0 +1,80 @@ +# Tasks: MCP / tool-runtime trust boundary (017) + +**Feature**: 017-mcp-runtime-trust-boundary +**Spec**: [spec.md](./spec.md) · **Plan**: [plan.md](./plan.md) +**Status**: Specified. Linux POC out of tree. Not wired into `get_agent()`. + +Checkboxes are honest. Spec-only work can be marked done. Do not mark +sensors done because a POC exists on another machine. Do not mark +`get_agent()` monitoring done because memory `ToolNode` already runs. + +## Phase 0 — Specify the boundary (this folder) + +- [x] Write `spec.md` with trust boundary, runtime monitoring as defense + in depth, five detection classes, honesty about this package +- [x] Write `plan.md` (boundary diagram, class table as ideas, 013/015 + binding, no CVE/POC copy) +- [x] Write `tasks.md` (this file) +- [x] Status: specified; POC out of tree; not a `thelab-langchain` feature +- [x] Cite 013 (no rubber-stamp of tool/exec) and 015 (content-free alerts) +- [x] Refuse CVE tables, unverifiable stats, keys, IPs, chat-product + routing, and POC source in this tree + +## Phase 1 — Linux POC (out of tree; already exists) + +Out of tree. Listed so this package does not re-build or vendor it. + +- [x] Prototype of five detection classes on Linux: + shell metacharacters in child cmdline; unexpected subprocess vs + allow-list; config file integrity; new-server network watch; + dangerous env var changes +- [x] POC treated as detection, not as production prevention + +Do **not** copy POC Python, audit rule files, or test suites into this +repo to “complete” a checkbox. + +## Phase 2 — Workstation wiring (not done) + +Host-side watcher next to real MCP/tool processes. Not this package. + +- [ ] Run sensors beside the workstation tool runtime (outside + `thelab-langchain`) +- [ ] Content-free alerts (015): rule class, counts/flags, generic names; + no secrets, transcripts, tool bodies, config dumps, or lab IPs +- [ ] Fail-open: dead sensor does not break the desk loop + +Phase 2 is **out of scope for 017 delivery**. Leave unchecked. + +## Phase 3 — This package / `get_agent()` (**not done**) + +- [ ] MCP client in `thelab_langchain` +- [ ] Sensors or a monitor node in the graph +- [ ] Wiring the out-of-tree POC into `get_agent()` +- [ ] Prevention (kill child, freeze config) as a shipped control + +Phase 3 is **out of scope** for 017. Do not implement them under this spec. + +## Explicitly not tasks in thelab + +Do not open work in this package for: + +- Vendoring POC source or research-cycle SUMMARY/JSON dumps +- A CVE / KEV tracker, CVSS tables, or “instances affected” claims +- Slack or any chat product as an alert sink +- Pytest that asserts Hermes MCP process trees +- Changing 014 memory tools in order to look like a monitor +- Adding MCP or shell tools to `ToolNode` without a new spec and 013 review + +## Traceability + +| Want | Where it lives today | +|------|----------------------| +| Boundary + five classes | This folder | +| Linux POC | Out of tree (not this git repo) | +| Graph / `get_agent()` | Spec 014; memory tools only; **no** sensors | +| Review of tool/exec changes | Spec 013 | +| Alert/telemetry privacy | Spec 015 | +| Sensors in this package | **Not present** | + +This tasks file is only the checklist view. It does not claim the brain +monitors MCP. diff --git a/specs/README.md b/specs/README.md index 9a9179b..ca102e4 100644 --- a/specs/README.md +++ b/specs/README.md @@ -21,7 +21,22 @@ or partial. | [013](013-reviewer-quality-gate/spec.md) | Reviewer quality gate | Never implements; severity scale. No reviewer bot here. | | [014](014-memory-injection-graph/spec.md) | Memory-injection graph | **Shipped** in `get_agent()`. Raw context; fail-open; no extra summarizer. | | [015](015-content-free-telemetry/spec.md) | Content-free telemetry | Voice sibling implements turns; graph does not export OTEL. Hub is `lan-agent-otel`. | +| [016](016-mac-spark-hybrid-inference/spec.md) | Mac + Spark hybrid inference | Research contract. MCDMA watch-only; ds4 spike not executed. Not the agent path. | +| [017](017-mcp-runtime-trust-boundary/spec.md) | MCP runtime trust boundary | Tool processes are a host trust boundary. POC out of tree; not in `get_agent()`. | Hermes **operating manual** (CLI, gateway, profile files) stays at `~/.hermes/docs/agentic-workflow.md`. Specs 009–010 and 012–013 record *protocol*, not that file. + +### Research tracks not imported + +Workspace research that is **not** SDD in this package (wrong product, client, +or a dump we will not vendor): + +- Church captioning / ProPresenter pipelines +- Client marketing sites +- World-models / SITE-Bench eval clones (upstream academic bench) +- Skill-optimization bootstrap (no design locked) +- Multi-agent *literature* surveys (012 is our chosen dispatch model) +- CVE/KEV catalogs and cycle summaries (017 records the trust boundary only) +- Alternate observability compose experiments (015 points at `lan-agent-otel`)