@nanobpm/nano-coder 0.3.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +91 -4
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -25,6 +25,7 @@ cargo install nano-coder # or build from source
|
|
|
25
25
|
- **Status line** pinned to the bottom of the terminal, plus manual and automatic context compaction
|
|
26
26
|
- **Task plans**: `plan_*` tools keep a plan with notes outside the conversation, so long tasks survive compaction, resume and a change of worker
|
|
27
27
|
- **Project instructions**: `AGENTS.md` (or `CLAUDE.md`, `.github/copilot-instructions.md`) from the repository is added to the system prompt
|
|
28
|
+
- **Safety**: built-in guards block destructive commands (`rm -rf /`, `DROP DATABASE`, force-pushing `main`, ...), user allow/deny rules, and an optional OS sandbox (Seatbelt on macOS, Landlock on Linux)
|
|
28
29
|
- **Skills**: `SKILL.md` folders from the repository, `~/.agents/skills`, and an spm `ai.lock`, loaded on demand with `load_skill`
|
|
29
30
|
|
|
30
31
|
## Two Execution Modes
|
|
@@ -127,6 +128,9 @@ src/
|
|
|
127
128
|
│ └── mock.rs # Offline scripted client
|
|
128
129
|
├── bash.rs # bash tool: timeout, file capture, bounded output
|
|
129
130
|
├── files.rs # read_file / write_file / edit_file tools
|
|
131
|
+
├── shell.rs # Bash parser used by the permission checks
|
|
132
|
+
├── permissions.rs # Allow/deny rules and built-in guards against destructive commands
|
|
133
|
+
├── sandbox.rs # Seatbelt (macOS) / Landlock (Linux) sandbox for shell commands
|
|
130
134
|
├── output.rs # Head/tail output bounding, spilling long output to disk
|
|
131
135
|
├── session.rs # Versioned append-only JSONL session log
|
|
132
136
|
├── context.rs # Token accounting, context-window heuristics, overflow detection
|
|
@@ -184,8 +188,76 @@ with the whole result saved under the temp directory (`nano-coder-<pid>/tool-<id
|
|
|
184
188
|
and its path in the marker. `read_file` pages instead.
|
|
185
189
|
|
|
186
190
|
Relative paths resolve against the working directory (ACP `session/new` `cwd`). Writes are
|
|
187
|
-
atomic (temp file + rename). There is no permission prompt
|
|
188
|
-
|
|
191
|
+
atomic (temp file + rename). There is no permission prompt; every call is checked against
|
|
192
|
+
the [permission rules and sandbox](#permissions-and-sandbox) instead.
|
|
193
|
+
|
|
194
|
+
## Permissions and Sandbox
|
|
195
|
+
|
|
196
|
+
nano-coder never stops to ask for approval (it runs headless in agent fleets). Every tool call
|
|
197
|
+
is checked before it runs instead, and a blocked call returns an error telling the model to
|
|
198
|
+
stop and ask the user rather than work around the block.
|
|
199
|
+
|
|
200
|
+
**Order of checks:** `deny` rules, then `allow` rules, then the built-in guards. Deny always
|
|
201
|
+
wins. An allow rule approves a shell command only when *every* command in it matches, so
|
|
202
|
+
`Bash(git *)` does not approve `git status && rm -rf /`.
|
|
203
|
+
|
|
204
|
+
**Rules** name a tool and an optional pattern:
|
|
205
|
+
|
|
206
|
+
| Rule | Matches |
|
|
207
|
+
|---|---|
|
|
208
|
+
| `Bash(rm -rf *)` | a shell command; `*` matches anything, including spaces and `/` |
|
|
209
|
+
| `Bash(git push:*)` | `git push` alone or with any arguments |
|
|
210
|
+
| `Read(~/.ssh/**)` | `read_file` paths; `**` crosses directories, `*` does not |
|
|
211
|
+
| `Edit(**/.env)` / `Write(...)` | `write_file` and `edit_file` paths (relative to the working directory, or absolute) |
|
|
212
|
+
| `write_file`, `bash`, any tool name | every call to that tool |
|
|
213
|
+
|
|
214
|
+
**Shell commands are parsed, not pattern-matched as text.** The command line is split on `;`,
|
|
215
|
+
`&&`, `||`, `|`, `&`, newlines and parentheses; quotes are removed; `$(...)`, backticks and
|
|
216
|
+
`<(...)` are parsed as further commands. The guards and rules then see through assignments
|
|
217
|
+
(`FOO=1 cmd`), wrappers (`sudo`, `env`, `timeout`, `nice`, `xargs`, `nohup`, `command`, ...),
|
|
218
|
+
`bash -c '...'`, `eval`, `ssh host cmd`, `find -exec`, and here-documents fed to a shell. A command
|
|
219
|
+
that cannot be parsed, or a script computed at run time (`bash -c "$CMD"`,
|
|
220
|
+
`eval "$(curl ...)"`), is blocked.
|
|
221
|
+
|
|
222
|
+
**Built-in guards** (`builtin_rules = true`) block:
|
|
223
|
+
|
|
224
|
+
- recursive `rm` (and `mv`, `find -delete`, `chmod -R`/`chown -R`) on `/`, your home directory
|
|
225
|
+
or its top-level folders, the working directory or its parents, top-level and system
|
|
226
|
+
directories, and `.git`. `rm -rf *` counts as the working directory. An unset variable counts
|
|
227
|
+
as empty, so `rm -rf "$DIR/"*` is blocked unless written `"${DIR:?}/"*`. Paths follow a `cd` and
|
|
228
|
+
variable assignments earlier in the same command (`cd .. && rm -rf project` is blocked)
|
|
229
|
+
- `mkfs`, `fdisk`, `wipefs`, destructive `diskutil`, `dd of=/dev/...` and redirects to
|
|
230
|
+
devices, fork bombs, `shutdown`/`reboot`
|
|
231
|
+
- destructive SQL (`DROP DATABASE|SCHEMA|TABLE`, `TRUNCATE`, `DELETE FROM` without `WHERE`,
|
|
232
|
+
`ALTER TABLE ... DROP`, `FLUSHALL`, `dropDatabase()`) in a command that uses a database client
|
|
233
|
+
(`psql`, `mysql`, `sqlite3`, `mongosh`, `redis-cli`, also via `docker exec`) or inline
|
|
234
|
+
interpreter code (`python -c`), plus `dropdb`, `rails db:drop`, `prisma migrate reset`,
|
|
235
|
+
`manage.py flush`
|
|
236
|
+
- `terraform destroy`, `pulumi destroy`, `kubectl delete namespace|--all`, `aws s3 rb`
|
|
237
|
+
- `git push --force` (or `+refspec`, `--all`, wildcard refspecs) to, or deleting, a protected
|
|
238
|
+
branch, and `git push --mirror`
|
|
239
|
+
|
|
240
|
+
Add an allow rule for anything legitimate they block, e.g.
|
|
241
|
+
`allow = ["Bash(sqlite3 test.db *)"]`, or set `builtin_rules = false`.
|
|
242
|
+
|
|
243
|
+
**These checks catch mistakes, not adversaries.** A model can write a script and run it, and
|
|
244
|
+
nothing inspects that. The boundary is the OS sandbox, plus credentials: don't give the agent
|
|
245
|
+
production database URLs or broadly scoped tokens.
|
|
246
|
+
|
|
247
|
+
**Sandbox** (`--sandbox workspace`, off by default) runs each shell command under Seatbelt
|
|
248
|
+
(`sandbox-exec`) on macOS or Landlock on Linux (6.2+). Commands can read everywhere, but
|
|
249
|
+
write only to:
|
|
250
|
+
|
|
251
|
+
- `workspace`: the working directory, its git directories (including a worktree's shared
|
|
252
|
+
one), temp directories, package-manager caches (`~/.cargo/registry`, `~/.npm`, `~/.cache`,
|
|
253
|
+
`~/Library/Caches`, `~/.gradle`, `~/go/pkg/mod`, ...) and `writable` paths
|
|
254
|
+
- `read-only`: temp directories and `writable` paths
|
|
255
|
+
|
|
256
|
+
`write_file` and `edit_file` are held to the same directories. `network = false` blocks
|
|
257
|
+
outbound connections (macOS: except to localhost; Linux: all TCP, which needs Linux 6.7+).
|
|
258
|
+
If the sandbox is enabled but cannot be applied, commands fail instead of running
|
|
259
|
+
unsandboxed. When a sandboxed command fails with a permission error, the result tells the
|
|
260
|
+
model where it may write.
|
|
189
261
|
|
|
190
262
|
## Commands
|
|
191
263
|
|
|
@@ -234,7 +306,8 @@ cargo run -- --resume sess-20260923T012518-7e7923f8
|
|
|
234
306
|
```
|
|
235
307
|
|
|
236
308
|
Flags: `--login github-copilot`, `--list-models PROVIDER`, `--acp`, `--model provider/model` (or `AGENTIC_HARNESS_MODEL`), `--resume SESSION_ID`,
|
|
237
|
-
`--config PATH`, `--verbosity LEVEL` (`-v`)
|
|
309
|
+
`--config PATH`, `--verbosity LEVEL` (`-v`), `--sandbox off|workspace|read-only` (or `NANO_CODER_SANDBOX`),
|
|
310
|
+
`--allow RULE` and `--deny RULE` (repeatable; added to the config's rules).
|
|
238
311
|
|
|
239
312
|
## Configuration
|
|
240
313
|
|
|
@@ -268,6 +341,18 @@ user_dirs = ["~/.agents/skills"]
|
|
|
268
341
|
ai_lock = true # load skills pinned in ai.lock
|
|
269
342
|
fetch = true # fetch ai.lock commits missing from the spm store
|
|
270
343
|
allowed_hosts = ["github.com"] # hosts ai.lock entries may be fetched from ("*" = any)
|
|
344
|
+
|
|
345
|
+
[permissions] # see Permissions and Sandbox
|
|
346
|
+
builtin_rules = true # block destructive commands unless allowed
|
|
347
|
+
allow = [] # e.g. ["Bash(sqlite3 test.db *)"]
|
|
348
|
+
deny = [] # e.g. ["Bash(git push:*)", "Edit(**/.env)"]
|
|
349
|
+
protected_branches = ["main", "master", "trunk", "develop"]
|
|
350
|
+
|
|
351
|
+
[sandbox]
|
|
352
|
+
mode = "off" # off | workspace | read-only (or --sandbox)
|
|
353
|
+
writable = [] # extra writable paths, e.g. ["~/.local/state/myapp"]
|
|
354
|
+
network = true # false blocks outbound connections
|
|
355
|
+
tool_caches = true # workspace mode: allow ~/.cargo/registry, ~/.npm, ~/.cache, ...
|
|
271
356
|
```
|
|
272
357
|
|
|
273
358
|
The default model is `gpt-4o-mini` on the `mock` provider, so the harness still works offline.
|
|
@@ -632,4 +717,6 @@ Retry classification, output bounding, bash result formatting and the session-lo
|
|
|
632
717
|
are adapted from [unreal-agent](https://github.com/unreallabsai/unreal-agent)
|
|
633
718
|
(MIT, Copyright (c) 2026 Unreal Labs). System reminders and the outcome tool follow ideas in
|
|
634
719
|
[grok-build](https://github.com/xai-org/grok-build)'s `<system-reminder>` notes and
|
|
635
|
-
`update_goal` tool.
|
|
720
|
+
`update_goal` tool. The permission model (deny wins, allow rules must cover every command in
|
|
721
|
+
a chain, wrappers stripped before matching) and the Seatbelt/Landlock sandbox profiles follow
|
|
722
|
+
grok-build's and Codex's designs.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nanobpm/nano-coder",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "A 6MB coding agent. Run a fleet on your laptop.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -31,9 +31,9 @@
|
|
|
31
31
|
"node": ">=18"
|
|
32
32
|
},
|
|
33
33
|
"optionalDependencies": {
|
|
34
|
-
"@nanobpm/nano-coder-darwin-arm64": "0.
|
|
35
|
-
"@nanobpm/nano-coder-darwin-x64": "0.
|
|
36
|
-
"@nanobpm/nano-coder-linux-arm64": "0.
|
|
37
|
-
"@nanobpm/nano-coder-linux-x64": "0.
|
|
34
|
+
"@nanobpm/nano-coder-darwin-arm64": "0.4.0",
|
|
35
|
+
"@nanobpm/nano-coder-darwin-x64": "0.4.0",
|
|
36
|
+
"@nanobpm/nano-coder-linux-arm64": "0.4.0",
|
|
37
|
+
"@nanobpm/nano-coder-linux-x64": "0.4.0"
|
|
38
38
|
}
|
|
39
39
|
}
|