@backburner/cli 0.1.4 → 0.1.7
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
CHANGED
|
@@ -1,154 +1,138 @@
|
|
|
1
|
-
# Backburner
|
|
1
|
+
# Backburner
|
|
2
2
|
|
|
3
|
-
Backburner
|
|
3
|
+
Backburner turns GitHub issues into reviewable pull requests using coding agents running on your own machine.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Open an issue from your phone, get back to your day, and manage the work through GitHub comments, labels, and PR reviews. Your computer does the planning, coding, testing, committing, and pushing through the agent CLIs and credentials you already use. GitHub is the control plane; your computer is the worker.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
- `backburner run` for one orchestration cycle
|
|
9
|
-
- `backburner run-loop` for repeated cycles on a fixed interval
|
|
10
|
-
- `backburner journal` for reading persisted run and workflow events
|
|
11
|
-
- `backburner broker` for a local GitHub broker MCP endpoint
|
|
12
|
-
- a plan-first label workflow for product approval, plan approval, review requests, and parent-branch sync approval
|
|
13
|
-
- local provider CLIs for Antigravity, Codex, Gemini, Claude, and OpenCode
|
|
7
|
+
## How it works
|
|
14
8
|
|
|
15
|
-
|
|
9
|
+
1. You open an issue in a repository managed by Backburner.
|
|
10
|
+
2. A product agent can clarify the problem and propose a product direction in the issue.
|
|
11
|
+
3. Once product direction is approved, Backburner creates an isolated git worktree and a planning PR with a Product Spec and implementation plan.
|
|
12
|
+
4. You approve the plan with a GitHub label. Local agents implement it, run the repository's checks, commit, and push.
|
|
13
|
+
5. You review comments, request changes, and merge from GitHub—including from your phone.
|
|
16
14
|
|
|
17
|
-
|
|
18
|
-
- npm
|
|
19
|
-
- git
|
|
20
|
-
- GitHub CLI authenticated for the account and repositories you want Backburner to manage:
|
|
15
|
+
Backburner never silently merges the final PR. You remain in control of approvals and the merge.
|
|
21
16
|
|
|
22
|
-
|
|
23
|
-
gh auth status
|
|
24
|
-
```
|
|
17
|
+
## Quick start
|
|
25
18
|
|
|
26
|
-
|
|
19
|
+
You need Node.js 20 or newer, `git`, an authenticated [GitHub CLI](https://cli.github.com/), and at least one supported agent CLI on your `PATH`: `agy`, `codex`, `gemini`, `claude`, or `opencode`.
|
|
27
20
|
|
|
28
|
-
Backburner
|
|
21
|
+
Install Backburner from npm:
|
|
29
22
|
|
|
30
|
-
|
|
23
|
+
~~~sh
|
|
24
|
+
npm install --global @backburner/cli
|
|
25
|
+
gh auth status
|
|
26
|
+
backburner init
|
|
27
|
+
~~~
|
|
31
28
|
|
|
32
|
-
|
|
29
|
+
Onboarding walks you through:
|
|
33
30
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
31
|
+
- checking Node.js, `git`, GitHub authentication, and local write access;
|
|
32
|
+
- choosing the directories Backburner will use;
|
|
33
|
+
- creating a private `backburner-demo` repository or selecting existing repositories;
|
|
34
|
+
- detecting your installed agent CLIs and configuring models and roles;
|
|
35
|
+
- writing readable local configuration; and
|
|
36
|
+
- creating the configured Backburner labels in each selected repository.
|
|
39
37
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
```powershell
|
|
43
|
-
npm install
|
|
44
|
-
npm run build
|
|
45
|
-
node .\dist\src\cli\run.js init
|
|
46
|
-
```
|
|
38
|
+
When onboarding finishes:
|
|
47
39
|
|
|
48
|
-
|
|
40
|
+
1. Open the first configured repository—usually `backburner-demo` if you created it.
|
|
41
|
+
2. Follow the printed prefilled issue link or scan the QR code, then submit the issue.
|
|
42
|
+
3. Back on your Mac, start Backburner and leave it running:
|
|
49
43
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
To install and verify the locally packed package:
|
|
44
|
+
~~~sh
|
|
45
|
+
backburner run
|
|
46
|
+
~~~
|
|
56
47
|
|
|
57
|
-
|
|
58
|
-
npm run install:local
|
|
59
|
-
backburner journal --json
|
|
60
|
-
```
|
|
48
|
+
That is the handoff. Keep the Mac awake and online, and continue from GitHub on your phone while local agents do the work. The interactive terminal UI runs an initial cycle automatically and keeps polling; you do not need to press `r`.
|
|
61
49
|
|
|
62
|
-
|
|
50
|
+
For an unattended terminal without the interactive UI:
|
|
63
51
|
|
|
64
|
-
|
|
52
|
+
~~~sh
|
|
53
|
+
backburner run-loop --headless --interval-minutes 5
|
|
54
|
+
~~~
|
|
65
55
|
|
|
66
|
-
|
|
67
|
-
npm run install:local -- --prefix /tmp/backburner-local
|
|
68
|
-
/tmp/backburner-local/bin/backburner journal --json
|
|
69
|
-
```
|
|
56
|
+
The prefilled first issue includes the configured product-approval label, so it enters the Product Spec and planning path immediately. An ordinary unlabeled issue starts with Product Discovery instead.
|
|
70
57
|
|
|
71
|
-
|
|
58
|
+
## Before onboarding a real repository
|
|
72
59
|
|
|
73
|
-
|
|
74
|
-
npm run install:local -- --prefix "$env:TEMP\backburner-local"
|
|
75
|
-
& "$env:TEMP\backburner-local\backburner.cmd" journal --json
|
|
76
|
-
```
|
|
60
|
+
Backburner intentionally gives coding agents meaningful access to selected repositories. Before you run it:
|
|
77
61
|
|
|
78
|
-
|
|
62
|
+
- select only repositories whose code and issue content you trust;
|
|
63
|
+
- keep `allowedUsers` limited to GitHub users you trust to trigger agent work;
|
|
64
|
+
- understand the permission mode used by each provider CLI;
|
|
65
|
+
- expect prompts, repository content, and tool results to be sent to the configured model provider;
|
|
66
|
+
- treat `~/.backburner/outputs` as sensitive because state, journal entries, and logs can contain repository and provider output; and
|
|
67
|
+
- start with the private demo repository if you want to observe the workflow before granting access to production code.
|
|
79
68
|
|
|
80
|
-
|
|
81
|
-
npm install -g @backburner/cli
|
|
82
|
-
backburner init
|
|
83
|
-
```
|
|
69
|
+
Local execution means the worker and credentials stay on your machine; it does not mean the workflow is offline or that model providers never receive code. See [SECURITY.md](SECURITY.md) for the threat model, trust boundaries, and vulnerability-reporting process.
|
|
84
70
|
|
|
85
|
-
|
|
71
|
+
## What onboarding writes
|
|
86
72
|
|
|
87
|
-
|
|
73
|
+
By default, Backburner stores its data under `~/.backburner`:
|
|
88
74
|
|
|
89
|
-
|
|
75
|
+
- code root: `~/.backburner/code`
|
|
76
|
+
- management directory: `~/.backburner/management`
|
|
77
|
+
- outputs directory: `~/.backburner/outputs`
|
|
78
|
+
- state directory: `~/.backburner/outputs/state`
|
|
79
|
+
- journal directory: `~/.backburner/outputs/journal`
|
|
80
|
+
- log directory: `~/.backburner/outputs/logs`
|
|
81
|
+
- onboarding workspaces directory: `<code-root>/_worktrees`
|
|
90
82
|
|
|
91
|
-
|
|
83
|
+
Set `BACKBURNER_ROOT` to change the root used for the default code, management, and outputs directories.
|
|
92
84
|
|
|
93
|
-
|
|
94
|
-
backburner init
|
|
95
|
-
```
|
|
85
|
+
Backburner writes two human-readable configuration files in the management directory:
|
|
96
86
|
|
|
97
|
-
|
|
87
|
+
- `repos.json`: repositories, allowed GitHub users, and workflow labels
|
|
88
|
+
- `agents.json`: enabled provider CLIs, models, roles, and capabilities
|
|
98
89
|
|
|
99
|
-
|
|
100
|
-
node dist/src/cli/run.js init
|
|
101
|
-
```
|
|
90
|
+
If either file already exists, interactive onboarding asks before overwriting it. Repository label creation is best-effort; if GitHub rejects a write, onboarding prints the labels page and exact names to create manually.
|
|
102
91
|
|
|
103
|
-
Non-interactive
|
|
92
|
+
Non-interactive onboarding is available:
|
|
104
93
|
|
|
105
|
-
|
|
94
|
+
~~~sh
|
|
106
95
|
backburner init --yes
|
|
107
|
-
|
|
96
|
+
~~~
|
|
97
|
+
|
|
98
|
+
`--yes` accepts broad defaults: it selects discovered repositories, enables discovered providers, creates default agent configurations, and creates or updates labels. It does not create a private demo repository. Review the generated configuration before running Backburner.
|
|
108
99
|
|
|
109
100
|
Useful `init` flags:
|
|
110
101
|
|
|
111
|
-
|
|
102
|
+
~~~text
|
|
112
103
|
--code-root <path> Root where managed repos are cloned
|
|
113
104
|
--management-dir <path> Directory for configuration files
|
|
114
105
|
--outputs-dir <path> Directory for generated state, journal, and logs
|
|
115
106
|
--log-dir <path> Directory for execution logs
|
|
116
107
|
--workspaces-dir <path> Directory used by onboarding readiness checks
|
|
117
108
|
--yes, -y Accept defaults without prompts
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
`backburner init` checks local requirements, confirms `gh auth status`, discovers repositories, detects provider CLIs, attempts model-list discovery when supported by those CLIs, writes configuration, and creates the configured Backburner labels on each selected repository.
|
|
109
|
+
~~~
|
|
121
110
|
|
|
122
|
-
|
|
123
|
-
- `agents.json`
|
|
111
|
+
## GitHub control plane
|
|
124
112
|
|
|
125
|
-
|
|
113
|
+
Backburner uses configurable labels to turn GitHub into a small, phone-friendly workflow:
|
|
126
114
|
|
|
127
|
-
|
|
115
|
+
- `agentProductApproved` approves product direction and allows the worktree/PR flow to begin.
|
|
116
|
+
- `agentPlanApproved` approves the implementation plan and allows implementation to run.
|
|
117
|
+
- `agentReview` requests an explicit code review after the PR head SHA changes.
|
|
118
|
+
- `agentSyncParentBranch` authorizes syncing a blocked parent branch from the default branch.
|
|
119
|
+
- `agentInProgress`, `planBreakdownNeeded`, and `prepareForMerge` communicate other workflow states.
|
|
128
120
|
|
|
129
|
-
|
|
121
|
+
The default label names are:
|
|
130
122
|
|
|
131
|
-
|
|
123
|
+
- `backburner:product-approved`
|
|
124
|
+
- `backburner:plan-approved`
|
|
125
|
+
- `backburner:review`
|
|
126
|
+
- `backburner:sync-parent-branch`
|
|
127
|
+
- `backburner:in-progress`
|
|
128
|
+
- `backburner:plan-breakdown-needed`
|
|
129
|
+
- `backburner:prepare-for-merge`
|
|
132
130
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
- code root: `~/.backburner/code`
|
|
136
|
-
- management directory: `~/.backburner/management`
|
|
137
|
-
- outputs directory: `~/.backburner/outputs`
|
|
138
|
-
- state directory: `~/.backburner/outputs/state`
|
|
139
|
-
- journal directory: `~/.backburner/outputs/journal`
|
|
140
|
-
- log directory: `~/.backburner/outputs/logs`
|
|
141
|
-
- onboarding workspaces directory: `<code-root>/_worktrees`
|
|
142
|
-
|
|
143
|
-
Set `BACKBURNER_ROOT` to change the root used for the default code, management, and outputs directories.
|
|
144
|
-
|
|
145
|
-
## Configuration
|
|
146
|
-
|
|
147
|
-
Backburner configuration lives in the management directory.
|
|
131
|
+
Custom names are supported in `repos.json` and are used in generated plans and PR instructions.
|
|
148
132
|
|
|
149
133
|
Minimal `repos.json`:
|
|
150
134
|
|
|
151
|
-
|
|
135
|
+
~~~json
|
|
152
136
|
{
|
|
153
137
|
"allowedUsers": [
|
|
154
138
|
"your-github-user"
|
|
@@ -173,11 +157,15 @@ Minimal `repos.json`:
|
|
|
173
157
|
}
|
|
174
158
|
]
|
|
175
159
|
}
|
|
176
|
-
|
|
160
|
+
~~~
|
|
161
|
+
|
|
162
|
+
`allowedUsers` limits which GitHub users can feed issues, comments, reviews, and pull requests into task derivation. It is a trust allowlist, not a sandbox. `localPath` must be relative to `--code-root`.
|
|
163
|
+
|
|
164
|
+
## Agent configuration
|
|
177
165
|
|
|
178
166
|
Minimal `agents.json`:
|
|
179
167
|
|
|
180
|
-
|
|
168
|
+
~~~json
|
|
181
169
|
{
|
|
182
170
|
"agents": [
|
|
183
171
|
{
|
|
@@ -209,125 +197,103 @@ Minimal `agents.json`:
|
|
|
209
197
|
}
|
|
210
198
|
]
|
|
211
199
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
`allowedUsers` limits which GitHub users can feed issues, comments, reviews, and pull requests into task derivation. `localPath` must be relative to `--code-root`.
|
|
215
|
-
|
|
216
|
-
Agent selection filters enabled agents by required role and `modelClass`, then breaks ties by lowest agent `id`. Provider model names are passed through to the provider CLI and are not validated against vendor catalogs. During onboarding, model setup shows discovered model options when available, marks strong and cheap defaults, and pressing enter accepts the default. Backburner prefers discovered `sol` for strong, `terra` for balanced/default, and `luna` for cheap model names; Codex defaults are `sol` and `luna`. OpenCode may use discovered OpenRouter model IDs such as `openrouter/openai/gpt-5.6-sol` and `openrouter/openai/gpt-5.6-luna`.
|
|
200
|
+
~~~
|
|
217
201
|
|
|
218
|
-
|
|
202
|
+
Agent selection filters enabled agents by role and `modelClass`, then breaks ties by the lowest agent ID. Model names are passed through to the provider CLI and are not validated against vendor catalogs.
|
|
219
203
|
|
|
220
|
-
|
|
204
|
+
## Commands
|
|
221
205
|
|
|
222
|
-
|
|
223
|
-
- `agentPlanApproved`: approves an implementation plan and allows implementation tasks to run
|
|
224
|
-
- `agentReview`: requests an explicit code review; review tasks are dispatched when the PR has this label and its head SHA changed since the last review
|
|
225
|
-
- `agentSyncParentBranch`: authorizes Backburner to sync a blocked parent implementation branch from the repository default branch
|
|
206
|
+
Start the persistent interactive terminal UI:
|
|
226
207
|
|
|
227
|
-
|
|
208
|
+
~~~sh
|
|
209
|
+
backburner run
|
|
210
|
+
~~~
|
|
228
211
|
|
|
229
|
-
|
|
212
|
+
Run exactly one orchestration cycle:
|
|
230
213
|
|
|
231
|
-
|
|
214
|
+
~~~sh
|
|
215
|
+
backburner run --once
|
|
216
|
+
~~~
|
|
232
217
|
|
|
233
|
-
|
|
234
|
-
backburner run
|
|
235
|
-
```
|
|
218
|
+
Run continuously without the interactive UI:
|
|
236
219
|
|
|
237
|
-
|
|
220
|
+
~~~sh
|
|
221
|
+
backburner run-loop --headless --interval-minutes 5
|
|
222
|
+
~~~
|
|
238
223
|
|
|
239
|
-
|
|
240
|
-
node dist/src/cli/run.js run
|
|
241
|
-
```
|
|
224
|
+
`run-loop` also accepts `--interval-seconds <seconds>`. Its default interval is five minutes. `SIGINT` and `SIGTERM` request a graceful stop after the current cycle.
|
|
242
225
|
|
|
243
|
-
Common
|
|
226
|
+
Common path flags:
|
|
244
227
|
|
|
245
|
-
|
|
228
|
+
~~~text
|
|
246
229
|
--code-root <path>
|
|
247
230
|
--management-dir <path>
|
|
248
231
|
--outputs-dir <path>
|
|
249
232
|
--log-dir <path>
|
|
250
233
|
--broker-port <port>
|
|
251
|
-
|
|
252
|
-
--once
|
|
253
|
-
```
|
|
234
|
+
~~~
|
|
254
235
|
|
|
255
|
-
|
|
236
|
+
Read the persisted journal:
|
|
256
237
|
|
|
257
|
-
|
|
258
|
-
backburner run-loop --interval-minutes 5
|
|
259
|
-
```
|
|
260
|
-
|
|
261
|
-
`run-loop` accepts the same path and broker flags as `run`, plus either `--interval-seconds <seconds>` or `--interval-minutes <minutes>`. The default interval is 5 minutes. `SIGINT` and `SIGTERM` request a graceful stop after the current cycle.
|
|
262
|
-
|
|
263
|
-
Read the journal:
|
|
264
|
-
|
|
265
|
-
```sh
|
|
238
|
+
~~~sh
|
|
266
239
|
backburner journal --last 20
|
|
267
240
|
backburner journal --repo your-github-user-example-repo --json
|
|
268
|
-
|
|
241
|
+
~~~
|
|
269
242
|
|
|
270
|
-
|
|
243
|
+
Start the standalone local GitHub broker MCP endpoint:
|
|
271
244
|
|
|
272
|
-
|
|
273
|
-
--outputs-dir <path>
|
|
274
|
-
--repo <id>
|
|
275
|
-
--workstream <id>
|
|
276
|
-
--issue <number>
|
|
277
|
-
--pr <number>
|
|
278
|
-
--since <ISO timestamp>
|
|
279
|
-
--last <number>
|
|
280
|
-
--json
|
|
281
|
-
--verbose
|
|
282
|
-
```
|
|
283
|
-
|
|
284
|
-
Start the GitHub broker MCP:
|
|
285
|
-
|
|
286
|
-
```sh
|
|
245
|
+
~~~sh
|
|
287
246
|
backburner broker --port 4444
|
|
288
|
-
|
|
247
|
+
~~~
|
|
289
248
|
|
|
290
|
-
|
|
249
|
+
The normal orchestrator starts its own broker on loopback. The standalone broker supports `--host` and `--port` and serves `POST /mcp` and `GET /health`. Do not expose it to an untrusted network.
|
|
291
250
|
|
|
292
|
-
|
|
293
|
-
--management-dir <path>
|
|
294
|
-
--host <host>
|
|
295
|
-
--port <port>
|
|
296
|
-
```
|
|
297
|
-
|
|
298
|
-
The broker serves:
|
|
299
|
-
|
|
300
|
-
- `POST /mcp`
|
|
301
|
-
- `GET /health`
|
|
302
|
-
|
|
303
|
-
It exposes managed-repository tools for issue, PR, comment, check-run, code-search, PR creation, and PR review-comment reply workflows, plus plan-breakdown and wave-assessment tools used by Backburner providers.
|
|
251
|
+
## What a run does
|
|
304
252
|
|
|
305
|
-
|
|
253
|
+
Each cycle loads configuration and state, syncs enabled repositories, scans GitHub through `gh`, derives eligible tasks, starts a local GitHub broker for provider access, dispatches work to provider CLIs, and writes state, journal events, and execution logs.
|
|
306
254
|
|
|
307
|
-
|
|
308
|
-
npm run smoke:github-broker
|
|
309
|
-
npm run smoke:github-broker -- --repo your-github-user/example-repo --issue-number 123
|
|
310
|
-
```
|
|
255
|
+
Backburner uses isolated git worktrees as implementation units. Generated control-plane state is readable JSON under `<outputs-dir>/state`; execution logs and artifacts are under `<outputs-dir>/logs`; journal events are under `<outputs-dir>/journal`.
|
|
311
256
|
|
|
312
|
-
##
|
|
257
|
+
## Install from source
|
|
313
258
|
|
|
314
|
-
|
|
259
|
+
~~~sh
|
|
260
|
+
git clone https://github.com/eladav/backburner.git
|
|
261
|
+
cd backburner
|
|
262
|
+
npm install
|
|
263
|
+
npm run build
|
|
264
|
+
npm link
|
|
265
|
+
backburner init
|
|
266
|
+
~~~
|
|
315
267
|
|
|
316
|
-
|
|
268
|
+
Windows PowerShell:
|
|
317
269
|
|
|
318
|
-
|
|
270
|
+
~~~powershell
|
|
271
|
+
npm install
|
|
272
|
+
npm run build
|
|
273
|
+
npm link
|
|
274
|
+
backburner init
|
|
275
|
+
~~~
|
|
319
276
|
|
|
320
|
-
|
|
277
|
+
## Platform support
|
|
321
278
|
|
|
322
|
-
|
|
279
|
+
Backburner is intended for macOS, Linux, and Windows with Node.js 20 or newer. It is currently used on macOS. Automated CI runs the build, typecheck, tests, package dry-run, and local-install smoke checks on Ubuntu and Windows with Node.js 20 and 22.
|
|
323
280
|
|
|
324
|
-
|
|
281
|
+
## Development and verification
|
|
325
282
|
|
|
326
|
-
|
|
283
|
+
~~~sh
|
|
284
|
+
npm install
|
|
327
285
|
npm run build
|
|
328
286
|
npm run typecheck
|
|
329
287
|
npm test
|
|
330
288
|
npm pack --dry-run
|
|
331
|
-
|
|
289
|
+
~~~
|
|
290
|
+
|
|
291
|
+
The npm package includes the compiled CLI under `dist/src`, `README.md`, `SECURITY.md`, `LICENSE`, and package metadata.
|
|
292
|
+
|
|
293
|
+
## Security
|
|
294
|
+
|
|
295
|
+
Please read [SECURITY.md](SECURITY.md) before granting Backburner access to important repositories. Report suspected vulnerabilities privately using the process described there.
|
|
296
|
+
|
|
297
|
+
## License
|
|
332
298
|
|
|
333
|
-
|
|
299
|
+
[MIT](LICENSE)
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
Backburner coordinates coding agents that run on your machine with access to source code, local tools, GitHub credentials, and model-provider credentials. That is the product's core capability and its primary security boundary.
|
|
4
|
+
|
|
5
|
+
Read this document before granting Backburner access to an important repository.
|
|
6
|
+
|
|
7
|
+
## Reporting a vulnerability
|
|
8
|
+
|
|
9
|
+
Please do not disclose a suspected vulnerability in a public issue, discussion, pull request, or Hacker News thread.
|
|
10
|
+
|
|
11
|
+
Use GitHub's private vulnerability reporting for this repository when it is available. If the repository does not show a **Report a vulnerability** option on its Security page, open a public issue containing only a request for a private security contact channel—do not include exploit details, secrets, affected repository names, or logs.
|
|
12
|
+
|
|
13
|
+
Include, when possible:
|
|
14
|
+
|
|
15
|
+
- the affected Backburner version and operating system;
|
|
16
|
+
- the impact and the trust boundary that was crossed;
|
|
17
|
+
- minimal reproduction steps;
|
|
18
|
+
- whether credentials or private repository data may have been exposed; and
|
|
19
|
+
- any suggested mitigation.
|
|
20
|
+
|
|
21
|
+
You should receive an acknowledgement within seven days. This is a best-effort policy for a pre-1.0 open source project, not a service-level agreement.
|
|
22
|
+
|
|
23
|
+
## Supported versions
|
|
24
|
+
|
|
25
|
+
Backburner is pre-1.0. Security fixes are applied to the latest published version and the current default branch. Older versions are not supported unless a release note explicitly says otherwise.
|
|
26
|
+
|
|
27
|
+
## Threat model
|
|
28
|
+
|
|
29
|
+
### Assets to protect
|
|
30
|
+
|
|
31
|
+
- GitHub authentication available through `gh`
|
|
32
|
+
- provider credentials and any other environment variables inherited by local subprocesses
|
|
33
|
+
- private repository source, history, issues, comments, and pull requests
|
|
34
|
+
- the integrity of branches, commits, comments, labels, and pull requests
|
|
35
|
+
- the developer machine and files reachable by provider tools
|
|
36
|
+
- local Backburner configuration, state, journal entries, and execution logs
|
|
37
|
+
|
|
38
|
+
### Trusted components and people
|
|
39
|
+
|
|
40
|
+
Backburner assumes that you trust:
|
|
41
|
+
|
|
42
|
+
- the machine and operating-system account running it;
|
|
43
|
+
- the selected repositories and their existing build/test tooling;
|
|
44
|
+
- the GitHub users listed in `allowedUsers`;
|
|
45
|
+
- the installed `git`, `gh`, Node.js, npm, and provider CLIs;
|
|
46
|
+
- the configured model providers and the accounts used to reach them; and
|
|
47
|
+
- the npm package or source revision you installed.
|
|
48
|
+
|
|
49
|
+
If any of these are compromised, Backburner is not designed to contain that compromise.
|
|
50
|
+
|
|
51
|
+
### Untrusted input
|
|
52
|
+
|
|
53
|
+
Treat all of the following as potentially adversarial:
|
|
54
|
+
|
|
55
|
+
- issue bodies, comments, PR descriptions, reviews, code, and repository documentation;
|
|
56
|
+
- generated model output;
|
|
57
|
+
- dependency install scripts, test commands, git hooks, and other repository tooling;
|
|
58
|
+
- MCP/tool responses and provider output; and
|
|
59
|
+
- network traffic that can reach a broker bound outside loopback.
|
|
60
|
+
|
|
61
|
+
GitHub content can contain prompt-injection instructions. `allowedUsers` reduces who can feed content into task derivation, but it does not make that content safe and it is not an operating-system sandbox.
|
|
62
|
+
|
|
63
|
+
### Current security controls
|
|
64
|
+
|
|
65
|
+
- Repositories must be explicitly configured and enabled.
|
|
66
|
+
- GitHub actors are filtered through `allowedUsers` before their issues, PRs, comments, reviews, or label actions drive normal workflow decisions.
|
|
67
|
+
- Product approval, plan approval, review, parent-sync, and merge-preparation actions use configurable labels.
|
|
68
|
+
- Implementation happens in dedicated git worktrees instead of directly in the primary checkout.
|
|
69
|
+
- GitHub operations are routed through the authenticated `gh` CLI and constrained to configured repositories by Backburner's gateway and broker services.
|
|
70
|
+
- The orchestrator's broker binds to `127.0.0.1` by default.
|
|
71
|
+
- Read-only and workspace-write tasks are distinguished and mapped to provider-specific modes.
|
|
72
|
+
- Backburner creates and updates branches and pull requests, but does not silently perform the final merge.
|
|
73
|
+
- Configuration and state remain readable local JSON so operators can inspect them.
|
|
74
|
+
|
|
75
|
+
These are defense-in-depth controls, not a guarantee that an agent cannot make a harmful change.
|
|
76
|
+
|
|
77
|
+
### Important limitations
|
|
78
|
+
|
|
79
|
+
#### Coding-agent permissions
|
|
80
|
+
|
|
81
|
+
Workspace-write tasks are intentionally powerful. Depending on the provider, Backburner may select modes that allow unattended edits and command execution—for example Codex workspace-write, Claude `bypassPermissions`, or Gemini `yolo`. Provider implementations and semantics can change independently of Backburner.
|
|
82
|
+
|
|
83
|
+
An agent can run repository commands, and those commands can execute arbitrary code available to your user account. Backburner is not a virtual machine or a security sandbox. Use a dedicated machine, operating-system account, container, or VM when you need a stronger boundary.
|
|
84
|
+
|
|
85
|
+
#### Credentials and environment
|
|
86
|
+
|
|
87
|
+
Local subprocesses normally inherit the Backburner process environment. Avoid launching Backburner from a shell containing unrelated long-lived secrets. Prefer narrowly scoped GitHub and provider credentials, enable MFA, and periodically review active tokens and sessions.
|
|
88
|
+
|
|
89
|
+
#### Model-provider data
|
|
90
|
+
|
|
91
|
+
Local-first does not mean offline. Prompts can include source code and GitHub content, and provider CLIs may send them to third-party services under those providers' terms, retention settings, and account policies. Do not onboard a repository whose data policy forbids that transfer.
|
|
92
|
+
|
|
93
|
+
#### Local broker
|
|
94
|
+
|
|
95
|
+
The HTTP broker has no application-level authentication. Its default loopback binding is the expected trust boundary. Do not run `backburner broker --host 0.0.0.0`, publish its port, place it behind a public tunnel, or expose it to an untrusted local network.
|
|
96
|
+
|
|
97
|
+
Other processes running as your user may be able to reach a loopback service. If your local account is not trusted, use a separate account or stronger isolation.
|
|
98
|
+
|
|
99
|
+
#### Logs and persisted state
|
|
100
|
+
|
|
101
|
+
Files under `~/.backburner` can include repository paths, issue and PR content, agent output, errors, and command results. Restrict filesystem access to your user, do not commit this directory, redact logs before sharing them, and remove it securely when retiring the installation.
|
|
102
|
+
|
|
103
|
+
#### Generated code
|
|
104
|
+
|
|
105
|
+
Backburner does not guarantee that generated changes are correct or secure. Keep repository branch protection and CI checks enabled, review the diff, and own the final merge decision.
|
|
106
|
+
|
|
107
|
+
## Safe onboarding checklist
|
|
108
|
+
|
|
109
|
+
Before the first real run:
|
|
110
|
+
|
|
111
|
+
1. Start with the private demo repository.
|
|
112
|
+
2. Review the repositories and `allowedUsers` in `repos.json`.
|
|
113
|
+
3. Review the configured provider commands, models, and roles in `agents.json`.
|
|
114
|
+
4. Confirm what data each provider CLI sends remotely and what permission mode it uses.
|
|
115
|
+
5. Remove unrelated secrets from the launching shell environment.
|
|
116
|
+
6. Confirm the configured code, management, output, and log directories are private to your user.
|
|
117
|
+
7. Keep the broker on its default loopback address.
|
|
118
|
+
8. Keep branch protection, required CI, and human review on important repositories.
|
|
119
|
+
9. Leave final merging to a trusted human.
|
|
120
|
+
|
|
121
|
+
## Repository and package supply chain
|
|
122
|
+
|
|
123
|
+
For maintainers:
|
|
124
|
+
|
|
125
|
+
- require MFA for GitHub and npm maintainer accounts;
|
|
126
|
+
- use branch protection or repository rulesets for the default branch once public;
|
|
127
|
+
- enable private vulnerability reporting, dependency alerts, secret scanning, and push protection where available;
|
|
128
|
+
- review lockfile changes and keep `npm audit` results visible;
|
|
129
|
+
- publish releases from a protected GitHub Actions workflow using npm trusted publishing or `npm publish --provenance`;
|
|
130
|
+
- tag each published npm version and attach release notes to the same commit;
|
|
131
|
+
- keep workflow permissions minimal and pin or deliberately review third-party actions; and
|
|
132
|
+
- never publish from a dirty working tree.
|
|
133
|
+
|
|
134
|
+
npm provenance makes the package's source and build origin verifiable. It does not prove the source is benign or remove the need for review.
|
|
135
|
+
|
|
136
|
+
## Out of scope
|
|
137
|
+
|
|
138
|
+
The following are not security vulnerabilities by themselves:
|
|
139
|
+
|
|
140
|
+
- a model producing incorrect, insecure, or low-quality code without crossing an advertised security boundary;
|
|
141
|
+
- provider rate limits, exhausted budgets, or unavailable third-party services;
|
|
142
|
+
- actions performed by a GitHub user intentionally listed in `allowedUsers`;
|
|
143
|
+
- damage caused by intentionally running a malicious repository's own scripts with agent write permissions; or
|
|
144
|
+
- disclosure that is already inherent in sending configured repository context to the chosen model provider.
|
|
145
|
+
|
|
146
|
+
Reports showing a bypass of repository scoping, actor filtering, task permission gates, broker boundaries, or credential isolation are in scope.
|
|
@@ -28,7 +28,7 @@ export function buildAntigravityCliArgs(options) {
|
|
|
28
28
|
else {
|
|
29
29
|
args.push("--dangerously-skip-permissions");
|
|
30
30
|
}
|
|
31
|
-
args.push("--print",
|
|
31
|
+
args.push("--print", options.promptText);
|
|
32
32
|
return args;
|
|
33
33
|
}
|
|
34
34
|
export async function writeAntigravitySettings(settingsPath, options) {
|
|
@@ -64,7 +64,8 @@ export class AntigravityCliAgent {
|
|
|
64
64
|
...(agentConfig.model ? { model: agentConfig.model } : {}),
|
|
65
65
|
...(resumeConversationId ? { conversationId: resumeConversationId } : {}),
|
|
66
66
|
printTimeout: this.printTimeout,
|
|
67
|
-
logFilePath
|
|
67
|
+
logFilePath,
|
|
68
|
+
promptText
|
|
68
69
|
});
|
|
69
70
|
this.logger?.info(JSON.stringify({
|
|
70
71
|
component: "antigravity-launch",
|
|
@@ -102,7 +103,6 @@ export class AntigravityCliAgent {
|
|
|
102
103
|
}
|
|
103
104
|
const result = await this.options.commandRunner.run(agentConfig.command, args, {
|
|
104
105
|
cwd,
|
|
105
|
-
input: promptText,
|
|
106
106
|
env: getAntigravityEnvOverrides(),
|
|
107
107
|
timeoutMs: this.timeoutMs
|
|
108
108
|
});
|
|
@@ -68,19 +68,21 @@ export class ShowSummaryStep {
|
|
|
68
68
|
const issueTarget = buildFirstIssueTarget(ctx);
|
|
69
69
|
lines.push("");
|
|
70
70
|
lines.push("Next steps:");
|
|
71
|
-
lines.push(" 1. Start the orchestrator:");
|
|
72
|
-
lines.push(` ${buildRunCommand(ctx)}`);
|
|
73
71
|
if (issueTarget !== undefined) {
|
|
74
|
-
lines.push("
|
|
72
|
+
lines.push(" 1. Open and submit your first Backburner issue:");
|
|
75
73
|
lines.push(` ${issueTarget.prefilledIssueUrl}`);
|
|
76
|
-
lines.push("
|
|
74
|
+
lines.push(" 2. Start the orchestrator and leave it running:");
|
|
75
|
+
lines.push(` ${buildRunCommand(ctx)}`);
|
|
76
|
+
lines.push(" 3. Continue from GitHub on your phone while this machine does the work");
|
|
77
77
|
lines.push("");
|
|
78
78
|
lines.push("Repository issues QR:");
|
|
79
79
|
lines.push(renderQr(issueTarget.repositoryIssuesUrl));
|
|
80
80
|
}
|
|
81
81
|
else {
|
|
82
|
-
lines.push("
|
|
83
|
-
lines.push("
|
|
82
|
+
lines.push(" 1. Add a repository to repos.json");
|
|
83
|
+
lines.push(" 2. Open an issue on a managed repo with the configured product-approved label");
|
|
84
|
+
lines.push(" 3. Start the orchestrator and leave it running:");
|
|
85
|
+
lines.push(` ${buildRunCommand(ctx)}`);
|
|
84
86
|
}
|
|
85
87
|
}
|
|
86
88
|
else {
|
|
@@ -24,7 +24,7 @@ export class ImplementationWorkflowService {
|
|
|
24
24
|
const existing = input.existingWorktrees.find((worktree) => worktree.repoId === input.repo.id && worktree.issueNumber === input.issue.number) ??
|
|
25
25
|
input.existingWorktrees.find((worktree) => worktree.repoId === input.repo.id && worktree.branchName === branchName);
|
|
26
26
|
if (existing) {
|
|
27
|
-
const preparedPaths = await preparePlanDocuments(existing.path, input.issue, input.decision, existing.branchName);
|
|
27
|
+
const preparedPaths = await preparePlanDocuments(existing.path, input.issue, input.decision, existing.branchName, input.repo.labels.agentPlanApproved);
|
|
28
28
|
return {
|
|
29
29
|
worktree: {
|
|
30
30
|
...existing,
|
|
@@ -45,7 +45,7 @@ export class ImplementationWorkflowService {
|
|
|
45
45
|
const worktreePath = this.gitGateway.validatePathWithinCodeRoot(input.codeRoot, buildWorktreeRelativePath(input.repo, input.issue.number));
|
|
46
46
|
const startPoint = input.repo.defaultBranch ? `origin/${input.repo.defaultBranch}` : "origin/HEAD";
|
|
47
47
|
const createdPath = await this.gitGateway.createWorktree(input.repoPath, input.codeRoot, worktreePath, branchName, startPoint);
|
|
48
|
-
const preparedPaths = await preparePlanDocuments(createdPath, input.issue, input.decision, branchName);
|
|
48
|
+
const preparedPaths = await preparePlanDocuments(createdPath, input.issue, input.decision, branchName, input.repo.labels.agentPlanApproved);
|
|
49
49
|
const timestamp = this.now();
|
|
50
50
|
return {
|
|
51
51
|
created: true,
|
|
@@ -91,7 +91,7 @@ export class ImplementationWorkflowService {
|
|
|
91
91
|
headBranch: worktree.branchName,
|
|
92
92
|
baseBranch,
|
|
93
93
|
title: buildPullRequestTitle(issue),
|
|
94
|
-
body: buildPullRequestBody(issue, worktree)
|
|
94
|
+
body: buildPullRequestBody(issue, worktree, repo.labels.agentPlanApproved)
|
|
95
95
|
});
|
|
96
96
|
const timestamp = this.now();
|
|
97
97
|
return {
|
|
@@ -153,6 +153,7 @@ export class ImplementationWorkflowService {
|
|
|
153
153
|
packet,
|
|
154
154
|
waveIndex,
|
|
155
155
|
branchName,
|
|
156
|
+
planApprovalLabel: repo.labels.agentPlanApproved,
|
|
156
157
|
now: this.now,
|
|
157
158
|
existingPaths: {
|
|
158
159
|
planDocumentPath: existing.planDocumentPath,
|
|
@@ -186,6 +187,7 @@ export class ImplementationWorkflowService {
|
|
|
186
187
|
packet,
|
|
187
188
|
waveIndex,
|
|
188
189
|
branchName,
|
|
190
|
+
planApprovalLabel: repo.labels.agentPlanApproved,
|
|
189
191
|
now: this.now
|
|
190
192
|
});
|
|
191
193
|
const timestamp = this.now();
|
|
@@ -243,7 +245,12 @@ export class ImplementationWorkflowService {
|
|
|
243
245
|
headBranch: worktree.branchName,
|
|
244
246
|
baseBranch,
|
|
245
247
|
title: buildPacketPullRequestTitle({ parentWorkstream, packetId: packet.id }),
|
|
246
|
-
body: buildPacketPullRequestBody({
|
|
248
|
+
body: buildPacketPullRequestBody({
|
|
249
|
+
parentWorkstream,
|
|
250
|
+
packet,
|
|
251
|
+
worktree,
|
|
252
|
+
planApprovalLabel: repo.labels.agentPlanApproved
|
|
253
|
+
})
|
|
247
254
|
});
|
|
248
255
|
const timestamp = this.now();
|
|
249
256
|
return {
|
|
@@ -555,7 +562,7 @@ export function buildPacketWorktreeRelativePath(input) {
|
|
|
555
562
|
const { type, number } = input.parentWorkstream.origin;
|
|
556
563
|
return path.join(WORKTREE_ROOT_DIR, slugify(owner), slugify(name), `workstream-${type}-${number}`, `packet-${getPacketSlug(input.packetId)}`);
|
|
557
564
|
}
|
|
558
|
-
async function preparePlanDocuments(worktreePath, issue, decision, branchName) {
|
|
565
|
+
async function preparePlanDocuments(worktreePath, issue, decision, branchName, planApprovalLabel) {
|
|
559
566
|
const relativePaths = buildAgentDocumentRelativePaths(issue.number, branchName);
|
|
560
567
|
const planDocumentPath = path.join(worktreePath, relativePaths.planDocumentPath);
|
|
561
568
|
const productSpecDocumentPath = path.join(worktreePath, relativePaths.productSpecDocumentPath);
|
|
@@ -575,6 +582,7 @@ async function preparePlanDocuments(worktreePath, issue, decision, branchName) {
|
|
|
575
582
|
"",
|
|
576
583
|
"## Notes",
|
|
577
584
|
"- Planning document created by the orchestrator workflow.",
|
|
585
|
+
`- Implementation must not begin until the \`${planApprovalLabel}\` label is present.`,
|
|
578
586
|
""
|
|
579
587
|
].join("\n"), "utf8");
|
|
580
588
|
}
|
|
@@ -593,7 +601,7 @@ async function preparePlanDocuments(worktreePath, issue, decision, branchName) {
|
|
|
593
601
|
};
|
|
594
602
|
}
|
|
595
603
|
async function preparePacketPlanDocuments(input) {
|
|
596
|
-
const { worktreePath, parentWorkstream, packet, branchName, now, existingPaths } = input;
|
|
604
|
+
const { worktreePath, parentWorkstream, packet, branchName, planApprovalLabel, now, existingPaths } = input;
|
|
597
605
|
const relativePaths = existingPaths
|
|
598
606
|
? undefined
|
|
599
607
|
: buildPacketAgentDocumentRelativePaths(packet.id, branchName);
|
|
@@ -627,7 +635,7 @@ async function preparePacketPlanDocuments(input) {
|
|
|
627
635
|
"",
|
|
628
636
|
"## Notes",
|
|
629
637
|
`- This is a packet planning PR targeting \`${integrationBranch}\`.`,
|
|
630
|
-
|
|
638
|
+
`- Implementation must not begin until the \`${planApprovalLabel}\` label is present.`,
|
|
631
639
|
""
|
|
632
640
|
].join("\n"), "utf8");
|
|
633
641
|
}
|
|
@@ -658,7 +666,7 @@ function compareWorktrees(left, right) {
|
|
|
658
666
|
function buildPullRequestTitle(issue) {
|
|
659
667
|
return `agent: plan for issue #${issue.number} - ${issue.title}`;
|
|
660
668
|
}
|
|
661
|
-
function buildPullRequestBody(issue, worktree) {
|
|
669
|
+
function buildPullRequestBody(issue, worktree, planApprovalLabel) {
|
|
662
670
|
const planPath = toRepoRelativePath(worktree.path, worktree.planDocumentPath);
|
|
663
671
|
const productSpecPath = worktree.productSpecDocumentPath
|
|
664
672
|
? toRepoRelativePath(worktree.path, worktree.productSpecDocumentPath)
|
|
@@ -672,7 +680,7 @@ function buildPullRequestBody(issue, worktree) {
|
|
|
672
680
|
...(productSpecPath ? [`- Product Spec: \`${productSpecPath}\``] : []),
|
|
673
681
|
`- Plan document: \`${planPath}\``,
|
|
674
682
|
`- Agent log: \`${logPath}\``,
|
|
675
|
-
|
|
683
|
+
`- Implementation must not begin until the \`${planApprovalLabel}\` label is present.`
|
|
676
684
|
].join("\n");
|
|
677
685
|
}
|
|
678
686
|
function buildPacketPullRequestTitle(input) {
|
|
@@ -683,7 +691,7 @@ function buildPacketPullRequestTitle(input) {
|
|
|
683
691
|
return `agent: plan packet ${packetId} for ${source}`;
|
|
684
692
|
}
|
|
685
693
|
function buildPacketPullRequestBody(input) {
|
|
686
|
-
const { parentWorkstream, packet, worktree } = input;
|
|
694
|
+
const { parentWorkstream, packet, worktree, planApprovalLabel } = input;
|
|
687
695
|
if (!worktree.packet)
|
|
688
696
|
throw new Error("Missing packet metadata");
|
|
689
697
|
const planPath = toRepoRelativePath(worktree.path, worktree.planDocumentPath);
|
|
@@ -710,7 +718,7 @@ function buildPacketPullRequestBody(input) {
|
|
|
710
718
|
...packet.dependsOn.map((d) => `- ${d}`),
|
|
711
719
|
"",
|
|
712
720
|
"This PR was created by the orchestrator as a packet planning PR.",
|
|
713
|
-
|
|
721
|
+
`Implementation must not begin until the \`${planApprovalLabel}\` label is present.`
|
|
714
722
|
].join("\n");
|
|
715
723
|
}
|
|
716
724
|
function buildAgentDocumentRelativePaths(issueNumber, branchName) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backburner/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Local GitHub-based agent orchestration CLI",
|
|
6
6
|
"license": "MIT",
|
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
},
|
|
26
26
|
"files": [
|
|
27
27
|
"dist/src",
|
|
28
|
-
"README.md"
|
|
28
|
+
"README.md",
|
|
29
|
+
"SECURITY.md"
|
|
29
30
|
],
|
|
30
31
|
"bin": {
|
|
31
32
|
"backburner": "dist/src/cli/run.js"
|
|
@@ -39,7 +40,7 @@
|
|
|
39
40
|
"link:local": "npm run build && npm link",
|
|
40
41
|
"install:local": "node scripts/install-local-package.mjs",
|
|
41
42
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
42
|
-
"test": "npm run build && node
|
|
43
|
+
"test": "npm run build && node scripts/run-tests.mjs",
|
|
43
44
|
"run:backburner": "node dist/src/cli/run.js run",
|
|
44
45
|
"run:github-broker": "node dist/src/cli/run.js broker",
|
|
45
46
|
"smoke:github-broker": "node dist/src/cli/run.js broker-smoke",
|