phalus 0.6.0

Private Headless Automated License Uncoupling System — AI-powered clean room software reimplementation
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
# CLI Reference

PHALUS is CLI-first. Every capability is accessible from the command line; the web UI is a convenience wrapper over the same pipeline.

```
phalus [COMMAND] [OPTIONS]
```

Run `phalus --help` for a summary or `phalus [COMMAND] --help` for per-command help.

---

## Commands

| Command | Description |
|---------|-------------|
| [`plan`]#plan | Parse a manifest and show what would be processed |
| [`run`]#run | Run the full clean room pipeline from a manifest |
| [`run-one`]#run-one | Run the pipeline on a single package without a manifest |
| [`build`]#build | Run Agent B only from an existing CSP |
| [`scan`]#scan | Scan a project for dependency licenses and SBOM data |
| [`inspect`]#inspect | Inspect a completed job's output directory |
| [`validate`]#validate | Re-run validation on an existing output directory |
| [`config`]#config | Print the active configuration (API keys redacted) |
| [`serve`]#serve | Start the local web UI |

---

## plan

Parse a manifest file and display the packages that would be processed. No network calls to registries or LLMs are made. Use this to preview filtering before a long run.

```
phalus plan <MANIFEST> [OPTIONS]
```

### Arguments

| Argument | Description |
|----------|-------------|
| `MANIFEST` | Path to the manifest file (e.g. `package.json`, `requirements.txt`, `Cargo.toml`, `go.mod`) |

### Options

| Option | Description |
|--------|-------------|
| `--only <pkg1,pkg2,...>` | Process only these packages (comma-separated names) |
| `--exclude <pkg1,pkg2,...>` | Skip these packages (comma-separated names) |

### Example

```bash
phalus plan package.json

phalus plan package.json --only lodash,chalk

phalus plan requirements.txt --exclude boto3,botocore
```

**Output:**

```
Manifest: package.json (5 packages, 3 after filtering)

PACKAGE                        VERSION          ECOSYSTEM
-------------------------------------------------------
lodash                         ^4.17.21         npm
express                        ^4.18.2          npm
chalk                          ^5.3.0           npm
```

---

## run

Run the full clean room pipeline for all packages in a manifest. Packages are processed concurrently up to the configured limit.

```
phalus run <MANIFEST> [OPTIONS]
```

### Arguments

| Argument | Description |
|----------|-------------|
| `MANIFEST` | Path to the manifest file |

### Options

| Option | Default | Description |
|--------|---------|-------------|
| `--license <id>` | `mit` | SPDX license identifier for the generated code. Options: `mit`, `apache-2.0`, `bsd-2`, `bsd-3`, `isc`, `unlicense`, `cc0` |
| `--license-file <path>` || Path to a file containing full license text (overrides `--license`) |
| `--output <dir>` | `./phalus-output` | Directory to write generated packages |
| `--only <pkg1,pkg2,...>` || Process only these packages |
| `--exclude <pkg1,pkg2,...>` || Skip these packages |
| `--target-lang <lang>` | Same as source | Reimplement in a different language: `rust`, `go`, `python`, `typescript` |
| `--isolation <mode>` | `context` | Isolation strategy: `context`, `process`, `container` |
| `--similarity-threshold <f>` | `0.70` | Similarity score above which a package is flagged as FAIL |
| `--concurrency <n>` | `3` | Number of packages to process in parallel |
| `--dry-run` | false | Run Agent A only — produce CSP specs, skip Agent B and validation. Use with [`build`]#build for a split workflow. |
| `--verbose` | false | Enable verbose logging |

### Exit codes

| Code | Meaning |
|------|---------|
| `0` | All packages processed successfully |
| `1` | One or more packages failed |

### Examples

```bash
# Basic run, MIT license
phalus run package.json

# Apache-2.0 output into a specific directory
phalus run package.json --license apache-2.0 --output ./liberated/

# Process only two packages
phalus run package.json --only lodash,express

# Reimplement JavaScript packages in Rust
phalus run package.json --target-lang rust --license apache-2.0

# Dry run: trigger Agent A only, produce CSP specs without code generation
phalus run package.json --dry-run

# Dry run a single package, then build from the CSP later
phalus run-one npm/lodash@4.17.21 --dry-run
phalus build ./phalus-output/lodash/.cleanroom/csp/

# Stricter similarity threshold
phalus run package.json --similarity-threshold 0.50

# Stronger isolation mode
phalus run package.json --isolation process
```

---

## run-one

Run the clean room pipeline on a single package without needing a manifest file. Useful for quick experiments and testing a specific package.

```
phalus run-one <PACKAGE> [OPTIONS]
```

### Arguments

| Argument | Description |
|----------|-------------|
| `PACKAGE` | Package specification in the format `ecosystem/name@version`. For example: `npm/lodash@4.17.21`, `pypi/requests@2.31.0`, `crates/serde@1.0.193`, `go/github.com/gin-gonic/gin@v1.9.1` |

Supported ecosystems: `npm`, `pypi`, `crates`, `go`.

### Options

| Option | Default | Description |
|--------|---------|-------------|
| `--license <id>` | `mit` | SPDX license identifier |
| `--license-file <path>` || Path to a file containing full license text (overrides `--license`) |
| `--output <dir>` | `./phalus-output` | Output directory |
| `--target-lang <lang>` | Same as source | Target language: `rust`, `go`, `python`, `typescript` |
| `--isolation <mode>` | `context` | Isolation mode: `context`, `process`, `container` |
| `--similarity-threshold <f>` | `0.70` | Similarity threshold |
| `--verbose` | false | Enable verbose logging |

### Examples

```bash
phalus run-one npm/left-pad@1.1.3

phalus run-one npm/lodash@4.17.21 --license apache-2.0

phalus run-one pypi/requests@2.31.0 --output ./py-output/

phalus run-one npm/chalk@5.3.0 --target-lang rust --license mit

phalus run-one crates/serde@1.0.193 --isolation process
```

---

## build

Run Agent B (Builder) from an existing Clean Room Specification Pack without re-running Agent A. This is the second half of the split pipeline workflow:

1. Generate a CSP with `--dry-run` (Agent A only)
2. Optionally review or modify the CSP files on disk
3. Run `build` to implement from the CSP (Agent B only)

```
phalus build <CSP> [OPTIONS]
```

### Arguments

| Argument | Description |
|----------|-------------|
| `CSP` | Path to a CSP `manifest.json` file, **or** a directory containing one (e.g. `./phalus-output/lodash/.cleanroom/csp/`) |

### Options

| Option | Default | Description |
|--------|---------|-------------|
| `--license <id>` | `mit` | SPDX license identifier for the generated code |
| `--license-file <path>` || Path to a file containing full license text (overrides `--license`) |
| `--output <dir>` | `./phalus-output` | Output directory |
| `--target-lang <lang>` | Same as source | Target language: `rust`, `go`, `python`, `typescript` |
| `--isolation <mode>` | `context` | Isolation mode: `context`, `process`, `container` |
| `--similarity-threshold <f>` | `0.70` | Similarity threshold |
| `--verbose` | false | Enable verbose logging |

### Examples

```bash
# Build from a CSP directory (looks for manifest.json inside)
phalus build ./phalus-output/lodash/.cleanroom/csp/

# Build from a specific manifest.json
phalus build ./my-specs/lodash-csp/manifest.json --license apache-2.0

# Build in a different language
phalus build ./phalus-output/chalk/.cleanroom/csp/ --target-lang rust

# Full split workflow: generate CSP, review it, then build
phalus run-one npm/express@4.18.2 --dry-run
# ... inspect and optionally edit the CSP files ...
phalus build ./phalus-output/express/.cleanroom/csp/ --license mit
```

### Split pipeline workflow

The `build` command enables a split pipeline where Agent A and Agent B run as separate steps. This is useful for:

- **Reviewing the CSP** before committing to implementation
- **Injecting custom constraints** into the specification (e.g. security requirements in `03-behavior-spec.md` or `04-edge-cases.md`)
- **Reusing a CSP** to generate implementations in multiple languages
- **Iterating on the implementation** without re-running analysis

See the [Cookbook](cookbook.md) for detailed examples of these workflows.

---

## scan

Scan a project directory, manifest file, or SBOM file for dependency licenses. Identifies all dependencies, resolves their license metadata from package registries, normalizes license strings to SPDX identifiers, and classifies them into risk buckets.

Supports reading manifests (`package.json`, `requirements.txt`, `Cargo.toml`, `go.mod`) and SBOM files (CycloneDX JSON 1.4+, SPDX JSON 2.3+).

```
phalus scan <PATH> [OPTIONS]
```

### Arguments

| Argument | Description |
|----------|-------------|
| `PATH` | Directory to walk, manifest file, or SBOM file (e.g. `bom.json`, `sbom.json`) |

### Options

| Option | Default | Description |
|--------|---------|-------------|
| `--offline` | false | Skip registry lookups; report only what manifests and SBOMs declare locally |
| `--concurrency <n>` | `8` | Maximum concurrent registry lookups |
| `--output <format>` | `text` | Output format: `text` (human-readable table) or `json` |
| `--save` | false | Persist the scan result to `~/.phalus/scans/{id}.json` for later retrieval |

### Exit codes

| Code | Meaning |
|------|---------|
| `0` | Scan completed successfully |
| `1` | Scan failed (invalid path, parse error, etc.) |

### License classification

Each dependency license is normalized to an SPDX identifier and classified:

| Class | Examples | Risk |
|-------|----------|------|
| Permissive | MIT, Apache-2.0, BSD-2-Clause, ISC | Low |
| Copyleft (weak) | LGPL-2.1, MPL-2.0, EPL-2.0 | Medium |
| Copyleft (strong) | GPL-2.0, GPL-3.0, AGPL-3.0 | High |
| Proprietary | Commercial, All Rights Reserved | Review required |
| Unknown | Unrecognized license strings | Review required |

### Supported SBOM formats

| Format | Detection |
|--------|-----------|
| CycloneDX JSON (1.4+) | Files named `bom.json`, `cyclonedx.json`, or containing `bomFormat` key |
| SPDX JSON (2.3+) | Files named `sbom.json`, `spdx.json`, or containing `spdxVersion` key |

### Examples

```bash
# Scan a project directory (finds all manifests and SBOMs recursively)
phalus scan ./my-project

# Scan a single manifest
phalus scan package.json

# Scan a CycloneDX SBOM
phalus scan bom.json

# Offline scan (no registry calls)
phalus scan ./my-project --offline

# JSON output, saved for later retrieval via the API
phalus scan ./my-project --output json --save

# High concurrency for large projects
phalus scan ./monorepo --concurrency 16
```

**Sample text output:**

```
Scan: ./my-project (42 packages from 1 manifest, 0 SBOMs)

PACKAGE                VERSION     ECOSYSTEM  LICENSE         CLASS
---------------------------------------------------------------------------
lodash                 4.17.21     npm        MIT             permissive
express                4.18.2      npm        MIT             permissive
pg                     8.11.3      npm        MIT             permissive
react                  18.2.0      npm        MIT             permissive
node-forge             1.3.1       npm        BSD-3-Clause    permissive
sharp                  0.33.2      npm        Apache-2.0      permissive
readline-sync          1.4.10      npm        MIT             permissive

Summary: 40 permissive, 1 copyleft-weak, 0 copyleft-strong, 1 unknown
```

### Stored scans

When `--save` is used, the scan result is persisted to `~/.phalus/scans/{uuid}.json`. Stored scans can be listed and retrieved through the [REST API](api-reference.md#scan-endpoints).

---

## inspect

Display the contents of a completed job's output directory. With no flags, all sections are shown. Use individual flags to show only what you need.

```
phalus inspect <OUTPUT_DIR> [OPTIONS]
```

### Arguments

| Argument | Description |
|----------|-------------|
| `OUTPUT_DIR` | Path to the output directory written by `run` or `run-one` |

### Options

| Option | Description |
|--------|-------------|
| `--audit` | Show the audit log (timestamp, sequence number, event type per entry) |
| `--similarity` | Show similarity scores and verdict for each package |
| `--csp` | Show the CSP document list for each package |

If none of `--audit`, `--similarity`, or `--csp` are given, all three sections are shown.

### Example

```bash
# Show everything
phalus inspect ./phalus-output

# Show only similarity scores
phalus inspect ./phalus-output --similarity

# Show only the CSP inventory
phalus inspect ./phalus-output --csp

# Show only the audit log
phalus inspect ./phalus-output --audit
```

**Sample similarity output:**

```
=== Similarity Reports ===
  lodash@4.17.21:
    token_similarity: 0.1200
    name_overlap:     0.8900
    string_overlap:   0.1500
    overall_score:    0.2800
    verdict:          PASS
```

---

## validate

Re-run the validation stage on an existing output directory without regenerating any code. Useful for checking outputs against a different similarity threshold, or after manually reviewing generated files.

```
phalus validate <OUTPUT_DIR> [OPTIONS]
```

### Arguments

| Argument | Description |
|----------|-------------|
| `OUTPUT_DIR` | Path to the output directory to validate |

### Options

| Option | Default | Description |
|--------|---------|-------------|
| `--similarity-threshold <f>` | `0.70` | Similarity threshold for pass/fail |

### Exit codes

| Code | Meaning |
|------|---------|
| `0` | All packages passed validation |
| `1` | One or more packages failed |

### Example

```bash
phalus validate ./phalus-output

phalus validate ./phalus-output --similarity-threshold 0.50
```

**Sample output:**

```
PASS lodash (similarity: 0.2800, license: ok)
PASS chalk (similarity: 0.1900, license: ok)
FAIL express (similarity: 0.7200, license: ok)
```

---

## config

Print the active configuration, merging `~/.phalus/config.toml` with any `PHALUS_*` environment variable overrides. API keys are always redacted (shown as `***`).

```
phalus config
```

No options or arguments.

### Example

```bash
phalus config
```

**Sample output:**

```toml
[llm]
agent_a_provider = "anthropic"
agent_a_model = "claude-sonnet-4-6"
agent_a_api_key = "***"
agent_a_base_url = ""
agent_b_provider = "anthropic"
agent_b_model = "claude-sonnet-4-6"
agent_b_api_key = "***"
agent_b_base_url = ""

[isolation]
mode = "context"

[limits]
max_packages_per_job = 50
max_package_size_mb = 10
concurrency = 3
...
```

---

## serve

Start the local web UI. The server binds to `127.0.0.1:3000` by default and is accessible only from the local machine.

```
phalus serve [OPTIONS]
```

### Options

| Option | Default | Description |
|--------|---------|-------------|
| `--host <host>` | `127.0.0.1` | Address to bind to |
| `--port <port>` | `3000` | Port to listen on |

### Example

```bash
# Default: localhost only
phalus serve

# Custom port
phalus serve --port 8080

# Bind to all interfaces (use with caution — no authentication)
phalus serve --host 0.0.0.0 --port 3000
```

After starting, open `http://127.0.0.1:3000` in a browser.

See [Web UI](web-ui.md) for a description of the interface and [API Reference](api-reference.md) for the backing REST endpoints.