···
1
1
+
# TDD Plan: Add sandbox dispatch for `covn sandbox <cmd>`
2
2
+
3
3
+
## Context
4
4
+
5
5
+
Enable `covn sandbox <cmd>` to run the target command (`group`, `serve`, `train`, `dash`, `store`) under the currently cached sandbox tool (`fence` or `srt`). The sandbox tool becomes the spawned process; the cached target tool becomes its first argument, followed by any extra CLI args.
6
6
+
7
7
+
**Current state:**
8
8
+
- `handle_sandbox_wrapper` in `cli.rs` intercepts `covn sandbox ___` before clap parsing
9
9
+
- It reads the sandbox cache and calls `spawn_detached(sandbox, args)` directly
10
10
+
- `dispatch`, `run_group`, `run_serve` never see sandbox commands
11
11
+
- `covn sandbox switch` toggles between `fence` and `srt` and prints the selection
12
12
+
- `covn sandbox` (bare) prints the current sandbox selection
13
13
+
14
14
+
**Goal:**
15
15
+
- `covn sandbox group` → spawns `fence exo` (or `srt exo`)
16
16
+
- `covn sandbox serve --model foo` → spawns `fence mesh-llm --model foo` (or `srt parallax ...`)
17
17
+
- `covn sandbox group switch` → runs `group switch` normally (no sandbox)
18
18
+
- `covn sandbox serve about` → runs `serve about` normally (no sandbox)
19
19
+
- `covn sandbox train repo myrepo` → spawns `fence drift repo myrepo` (or `srt drift repo myrepo`)
20
20
+
- `covn sandbox switch` → toggles and prints whether `fence` or `srt` is active
21
21
+
22
22
+
---
23
23
+
24
24
+
## TDD Cycle
25
25
+
26
26
+
```
27
27
+
RED -> GREEN -> REFACTOR -> REPEAT
28
28
+
```
29
29
+
30
30
+
### RED: Write failing tests first
31
31
+
32
32
+
**New test file: `tests/sandbox_dispatch_tests.rs`**
33
33
+
34
34
+
```rust
35
35
+
// Test 1: CLI parses covn sandbox group as SandboxCommand::Run
36
36
+
#[test]
37
37
+
fn test_cli_parses_sandbox_group() { ... }
38
38
+
39
39
+
// Test 2: CLI parses covn sandbox serve --model foo
40
40
+
#[test]
41
41
+
fn test_cli_parses_sandbox_serve_with_args() { ... }
42
42
+
43
43
+
// Test 3: CLI parses covn sandbox group switch as args=["switch"]
44
44
+
#[test]
45
45
+
fn test_cli_parses_sandbox_group_switch() { ... }
46
46
+
47
47
+
// Test 4: dispatch handles SandboxCommand::Run by spawning sandbox tool
48
48
+
#[test]
49
49
+
fn test_dispatch_sandbox_run_spawns_fence() { ... }
50
50
+
51
51
+
// Test 5: dispatch handles SandboxCommand::Run with srt cache
52
52
+
#[test]
53
53
+
fn test_dispatch_sandbox_run_spawns_srt() { ... }
54
54
+
55
55
+
// Test 6: dispatch handles SandboxCommand::Run with switch/about args (no sandbox)
56
56
+
#[test]
57
57
+
fn test_dispatch_sandbox_switch_runs_directly() { ... }
58
58
+
59
59
+
// Test 7: dispatch handles SandboxCommand::Run with about args (no sandbox)
60
60
+
#[test]
61
61
+
fn test_dispatch_sandbox_about_runs_directly() { ... }
62
62
+
63
63
+
// Test 8: handle_sandbox_wrapper returns false for sandbox group to let clap parse
64
64
+
#[test]
65
65
+
fn test_sandbox_wrapper_returns_false_for_group() { ... }
66
66
+
67
67
+
// Test 9: handle_sandbox_wrapper returns true for bare sandbox/about/switch
68
68
+
#[test]
69
69
+
fn test_sandbox_wrapper_returns_true_for_switch() { ... }
70
70
+
71
71
+
// Test 10: sandbox cache read is independent from target command cache
72
72
+
#[test]
73
73
+
fn test_sandbox_and_target_caches_are_independent() { ... }
74
74
+
```
75
75
+
76
76
+
### GREEN: Minimal implementation
77
77
+
78
78
+
**Changes to `src/cli.rs` — `handle_sandbox_wrapper`:**
79
79
+
80
80
+
- Keep handling bare `sandbox`, `sandbox about`, `sandbox switch` directly (return `true`)
81
81
+
- For `sandbox <valid_cmd>` (group, serve, train, dash, store), return `Ok(false)` instead of calling `spawn_detached`
82
82
+
- This lets clap parse the full command and route it to `dispatch`
83
83
+
84
84
+
**Changes to `src/commands.rs` — `dispatch`:**
85
85
+
86
86
+
- Add `Command::Sandbox` match arm
87
87
+
- For `SandboxCommand::Run { command: target_cmd, args }`:
88
88
+
1. Read sandbox cache → `sandbox` (`fence` or `srt`)
89
89
+
2. Read target command cache → `target_tool` (e.g., `exo`, `mesh-llm`, `drift`, `trackio`, `spacedrive`/`tahoe-lafs`)
90
90
+
3. If `args` is `["switch"]` or `["about"]`, run the target command normally via a helper (no sandbox)
91
91
+
4. Otherwise, spawn `sandbox target_tool [args...]`
92
92
+
- For `SandboxCommand::Switch`/`About`: handle directly as fallback (should be caught by `handle_sandbox_wrapper`)
93
93
+
94
94
+
**New helper in `src/commands.rs` — `run_command_directly`:**
95
95
+
96
96
+
```rust
97
97
+
fn run_command_directly(target_cmd: &str, args: &[String]) -> Result<()> {
98
98
+
// Maps (target_cmd, args) to the appropriate run_* call
99
99
+
// Handles subcommand enum construction for switch, about, repo, etc.
100
100
+
}
101
101
+
```
102
102
+
103
103
+
### REFACTOR: Improve while keeping tests passing
104
104
+
105
105
+
---
106
106
+
107
107
+
## Design Decisions
108
108
+
109
109
+
| Decision | Choice | Rationale |
110
110
+
|----------|--------|-----------|
111
111
+
| Sandbox interception | `handle_sandbox_wrapper` returns `false` for `sandbox <cmd>` | Lets clap parse normally; `dispatch` handles spawning |
112
112
+
| Spawn location | `dispatch` handles sandbox spawning | Centralized command routing; single place to read both caches |
113
113
+
| Non-sandboxed args | `switch`, `about`, `help` run directly | User intent is metadata, not execution |
114
114
+
| Cache independence | Sandbox cache and target cache read separately | `covn sandbox switch` doesn't affect `covn group` cache |
115
115
+
| Helper function | `run_command_directly` maps target_cmd+args to run_* | Avoids duplicating switch/about/repo parsing logic |
116
116
+
117
117
+
---
118
118
+
119
119
+
## Files to Modify
120
120
+
121
121
+
| File | Changes |
122
122
+
|------|---------|
123
123
+
| `src/cli.rs` | Modify `handle_sandbox_wrapper` to return `false` for `sandbox <valid_cmd>` |
124
124
+
| `src/commands.rs` | Add `Command::Sandbox` arm to `dispatch`; add `run_command_directly` helper |
125
125
+
| `tests/sandbox_dispatch_tests.rs` | ~10 new tests |
126
126
+
127
127
+
---
128
128
+
129
129
+
## Process Mapping
130
130
+
131
131
+
| Input | Sandbox | Target Tool | Spawned Command |
132
132
+
|-------|---------|-------------|-----------------|
133
133
+
| `covn sandbox group` | `fence`/`srt` | `exo` | `fence exo` / `srt exo` |
134
134
+
| `covn sandbox serve --model foo` | `fence`/`srt` | `mesh-llm`/`parallax` | `fence mesh-llm --model foo` |
135
135
+
| `covn sandbox train repo myrepo` | `fence`/`srt` | `drift` | `fence drift repo myrepo` |
136
136
+
| `covn sandbox group switch` | — | — | runs `group switch` directly |
137
137
+
| `covn sandbox serve about` | — | — | runs `serve about` directly |
138
138
+
| `covn sandbox` | — | — | prints current sandbox selection |
139
139
+
| `covn sandbox switch` | — | — | toggles and prints `fence`/`srt` |
140
140
+
| `covn sandbox about` | — | — | prints current sandbox selection |
141
141
+
142
142
+
---
143
143
+
144
144
+
## Flow Diagram
145
145
+
146
146
+
```
147
147
+
covn sandbox group
148
148
+
→ main.rs: handle_sandbox_wrapper returns false
149
149
+
→ clap parses as Command::Sandbox { Run { command: "group", args: [] } }
150
150
+
→ dispatch reads sandbox cache → "fence"
151
151
+
→ dispatch reads group cache → "exo"
152
152
+
→ args not ["switch"/"about"] → spawn_detached("fence", ["exo"])
153
153
+
→ exit
154
154
+
155
155
+
covn sandbox group switch
156
156
+
→ main.rs: handle_sandbox_wrapper returns false
157
157
+
→ clap parses as Command::Sandbox { Run { command: "group", args: ["switch"] } }
158
158
+
→ dispatch reads sandbox cache → "fence"
159
159
+
→ dispatch reads group cache → "exo"
160
160
+
→ args IS ["switch"] → run_command_directly("group", ["switch"])
161
161
+
→ run_group(Some(Switch)) → cycle_switch("group")
162
162
+
→ exit
163
163
+
164
164
+
covn sandbox switch
165
165
+
→ main.rs: handle_sandbox_wrapper returns true
166
166
+
→ cycle_switch("sandbox") → toggles fence/srt
167
167
+
→ prints "Switched to: fence" or "Switched to: srt"
168
168
+
→ exit
169
169
+
```
···
1
1
---
2
2
-
title: "Code of Conduct"
3
3
-
date-last-modified: 2026-06-05
2
2
+
title: "Code of Conduct Quick Reference"
3
3
+
date-last-modified: 2026-01-06
4
4
authors:
5
5
+
- { name: "Marinus Savoritias", pronouns: "fae/faer, ve/veirs" }
5
6
- { name: "˶𝞢⤬⫒ⵖsᐼ˶", pronouns: "she/her" }
6
6
-
- { name: "Marinus Savoritias", pronouns: "fae/faer, ve/veirs" }
7
7
license: CC0-1.0
8
8
-
version: 0.0.6
8
8
+
license-link: https://creativecommons.org/publicdomain/zero/1.0/
9
9
+
version: 1.0.1
9
10
---
10
11
11
12
# Code of Conduct Quick Reference
12
12
-
13
13
-
Version = 0.0.6_2026-06-05
14
13
15
14
## Behavior Guide
16
15
···
25
24
| Creepy Vibes... | Unacceptable. Words and flirts CAN hurt. End coercion. |
26
25
| Users vs Developers | Everyone involved, anywhere. Skill DIVERSITY, not division. |
27
26
28
28
-
\*More behavior guidelines
29
29
-
https://www.recurse.com/social-rules
30
30
-
31
27
## Constructive Criticism Guide:
32
28
33
29
- Ask consent first. Don't forget to wait for the answer!
···
44
40
- Use “I” instead of “You. Set aside expectations or assumptions of others.
45
41
- Express feelings and release them without dwelling. Let it be cathartic, and then over.
46
42
- Patience. Everyone feels ready to leave something behind at their own speed.
43
43
+
44
44
+
# Decision Making Quick Reference
45
45
+
46
46
+
## Step-By-Step Process
47
47
+
48
48
+
1. Decide together whether to act, or whether the matter is beyond the limits of the group.
49
49
+
2. Reach consensus on what the topic is before sharing opinions/ideas or making decisions.
50
50
+
3. Identify gaps in protection, knowledge, cohesion and fill them together with the community.
51
51
+
4. Prioritize understandings from knowledgable and affected groups or people.
52
52
+
5. Share considerations/concerns/ideas/objections/suggestions without judgement.
53
53
+
6. Invite as much plurality and diversity as possible before deciding.
54
54
+
7. Consider:
55
55
+
- how reframing the problem may address the issue more effectively than applying a proposal.
56
56
+
- how providing tools to affected folks might be more effective than applying a proposal.
57
57
+
- how restructuring the group might be more effective than applying a proposal.
58
58
+
8. As a last resort, combine as many ideas as you can into one proposal.
59
59
+
9. A proposal is a draft that includes EVERY voice, and focuses on general ideas.
60
60
+
10. Write the proposal in ELI5 (explain like I'm 5 years old) language. Avoid a patronizing tone.
61
61
+
11. Present the proposal. Ensure all participants comprehend it. Focus on 'ends', deferring 'means' for later.
62
62
+
12. Repeat from the top as the proposal is reviewed. Document each step if there is a proposal on the table.
63
63
+
64
64
+
## Notes
65
65
+
66
66
+
- Its okay to say "I don't know."
67
67
+
- Its okay to say as a community "We don't know."
68
68
+
- Its okay to share and hope for miracles, so long as our stated goals stay grounded and achievable.
69
69
+
- Expect limited structural, technical, and social means. Avoid heavily taxing the community.
70
70
+
- Take the time to do extra research. Reach for books and friends and stuff!
71
71
+
- This process can take some time. Be mindful of each other's needs as well as your own.
72
72
+
73
73
+
## Additional Guides:
74
74
+
75
75
+
[Recurse Social rules](https://www.recurse.com/social-rules)
76
76
+
[Inclusive Naming](https://inclusivenaming.org/word-lists/)
77
77
+
[Not Doing ](https://permacomputing.net/not_doing/)
78
78
+
[Observe First](https://permacomputing.net/observe_first/)
47
79
48
80
> Remember: Designs mirror creator relationships. Treat others to the same service you want to build.
···
323
323
-rm -rf {{_here}}{{_mesh_clone_dir}}
324
324
-rm -rf {{_here}}{{_nocturne_dir}}{{_cache}}{{_mesh_tmp_dir}}
325
325
-rm -rf {{_here}}{{_covn_build_dir}}{{_rust_build_dir}}
326
326
-
-rm -rf {{_here}}/drift{{_rust_build_dir}}
326
326
+
# -rm -rf {{_here}}/drift{{_rust_build_dir}}covn
327
327
-rm -rf {{_here}}/drift-upstream{{_rust_build_dir}}
328
328
-
-rm -rf {{_here}}{{_here}}/drift{{_rust_build_dir}}
329
328
-rm -rf {{_here}}/cake{{_rust_build_dir}}
330
329
331
330
# Show installed processes.
···
30
30
31
31
## Minimum Requirements
32
32
33
33
+
### Experience
34
34
+
35
35
+
- Familiarity with using Terminals/Command Line Interfaces (CLI).
36
36
+
33
37
### Hardware
34
38
35
39
- Any of:
···
50
54
- `zsh`.
51
55
- `nvcc` (NVIDIA CUDA Toolkit), HIP/ROCm libraries (AMD ROCm), or `glslc` (Vulkan) for GPU support
52
56
- Windows can be used, but support is unstable. It allows client-node only, and is NOT sandboxed. Requires [`git`](https://github.com/darkshapes/sdbx/wiki/_Setup-:-Git-%E2%80%90Windows-only%E2%80%90)
53
53
-
54
54
-
### Experience
55
55
-
56
56
-
- Familiarity with using Terminals/Command Line Interfaces (CLI).
57
57
58
58
## Setup
59
59
···
93
93
nocturne / / /
94
94
covn-------:--:--:--:
95
95
\ \ \
96
96
-
\ \ `----> drift -> pytorch distributed heterogenous training
96
96
+
\ \ `----> trackio training experiment tracking
97
97
\ \
98
98
-
\ `----> trackio training experiment tracking
98
98
+
\ `----> drift distributed heterogenous training
99
99
\
100
100
`---> fence / sandbox-runtime network/command isolation[optional]
101
101
```
···
119
119
120
120
### Included Libraries
121
121
122
122
-
- H4 Tolerance
122
122
+
- H4 Tolerance (see above)
123
123
- Distributed
124
124
- Optional Auth Layer
125
125
- Open Source
···
128
128
| Base | Library | Hardware | Framework | Notes | Purpose |
129
129
| ---- | ----------------------------------------------------------- | ----------- | --------------- | ---------------- | ----------------------------------------------------------------------- |
130
130
| [✓] | **[mesh-llm](<(https://github.com/Mesh-LLM/mesh-llm/)>)** | Any | llama.cpp | iroh, default | Serves inference using combined compute (GPU/CPU VRAM/Memory size). |
131
131
-
| [✓] | **drift (COVEN)** | Apple/Linux | any | extended support | Coordinates training runs and metrics at scale across peer network. |
131
131
+
| [✓] | **drift** | Apple/Linux | any | extended support | Coordinates training runs and metrics at scale across peer network. |
132
132
| [✓] | **[trackio](https://github.com/gradio-app/trackio)** | Any | python | wandb stand-in | Experiment tracking across training runs |
133
133
| [✓] | **[tahoe-lafs](https://github.com/tahoe-lafs/tahoe-lafs/)** | Any | python | storage | Shared, encrypted data storage split across nodes. Not yet implemented. |
134
134
| [_] | [cake](https://github.com/evilsocket/cake) | Any, Mobile | candle | cake/tcp | Consolidate physically close resources into a single local endpoint. |
···
158
158
| [pytorch](https://github.com/pytorch/pytorch) | H4 | Any | Centralized | Any | pytorch | `torchrun` | no |
159
159
| [aihorde-regen](https://github.com/Haidra-Org/horde-worker-reGen) | H4 | Inference | Centralized | AMD/NVIDIA | pytorch | "kudos" | mix |
160
160
| [ray](https://github.com/ray-project/ray) | H4 | Any | Centralized | Any | any | useful algos | mix |
161
161
-
| [drift (upstream)](https://github.com/selimozten/drift-train) | H2 | Training | Distributed | NVIDIA/Linux | python | homogenous h/w | no |
161
161
+
| [drift-train](https://github.com/selimozten/drift-train) | H2 | Training | Distributed | NVIDIA/Linux | python | homogenous h/w | no |
162
162
| [prime-rl](https://github.com/PrimeIntellect-ai/prime) | H2 | Any | Federated | NVIDIA | vllm | llm only | no |
163
163
| [mlx](https://github.com/ml-explore/mlx) | H2 | Any | Federated | Apple | mlx | MPI/RING/JACCL | no |
164
164
| [primus](https://github.com/AMD-AGI/Primus-SaFE) | H2 | Any | Hybrid | AMD | pytorch | amd datacenter | yes |
···
87
87
eprintln!("Cloning {} to {:?}", repo, target_repo_path);
88
88
89
89
let output = ProcessCommand::new("git")
90
90
-
.args(["clone", &format!("https://github.com/{}", repo), &target_repo_path.to_string_lossy()])
90
90
+
.args(["clone", &format!("{}", repo), &target_repo_path.to_string_lossy()])
91
91
.output()
92
92
.context("Failed to run git clone")?;
93
93
···
113
113
}
114
114
115
115
const SANDBOX_ABOUT: &str = "Limit network, file and command access for the next command.";
116
116
+
const VALID_COVN_COMMANDS: [&str; 6] = ["group", "serve", "train", "dash", "store", "sandbox"];
116
117
117
118
pub fn handle_sandbox_wrapper(args: &[String]) -> Result<bool> {
118
119
if args.len() < 2 || args[1] != "sandbox" {
···
141
142
Ok(true)
142
143
}
143
144
_target => {
145
145
+
if !VALID_COVN_COMMANDS.contains(&_target) {
146
146
+
eprintln!("Error: '{}' is not a valid covn command", _target);
147
147
+
eprintln!("Valid commands: group, serve, train, dash, store, sandbox");
148
148
+
return Ok(true);
149
149
+
}
144
150
let sandbox_idx = read_cache_index("sandbox")?;
145
151
let sandbox = ["fence", "srt"][sandbox_idx];
146
152
let pass_through: Vec<&str> = args.iter()
···
34
34
.output()
35
35
.expect("failed to execute process");
36
36
assert!(output.status.success());
37
37
+
}
38
38
+
39
39
+
#[test]
40
40
+
fn test_sandbox_invalid_command() {
41
41
+
let output = Command::new("cargo")
42
42
+
.args(&["run", "--", "sandbox", "invalidcmd"])
43
43
+
.output()
44
44
+
.expect("failed to execute process");
45
45
+
assert!(output.status.success());
46
46
+
let stderr = String::from_utf8_lossy(&output.stderr);
47
47
+
assert!(stderr.contains("not a valid covn command"), "Expected error for invalid command, got: {}", stderr);
48
48
+
}
49
49
+
50
50
+
#[test]
51
51
+
fn test_sandbox_valid_command() {
52
52
+
let output = Command::new("cargo")
53
53
+
.args(&["run", "--", "sandbox", "train"])
54
54
+
.output()
55
55
+
.expect("failed to execute process");
56
56
+
let stderr = String::from_utf8_lossy(&output.stderr);
57
57
+
assert!(!stderr.contains("not a valid covn command"), "Valid command should not be rejected, got: {}", stderr);
37
58
}