# PiJS Proof Report: Security, Performance, and Determinism Without Node/Bun

> Self-contained evidence that the Pi extension runtime (QuickJS + capability-gated
> connectors) is more secure, deterministic, and performant than Node/Bun for
> running third-party extensions.

---

## 1. Executive Summary

Pi runs JS/TS extensions in an embedded QuickJS runtime with no dependency on
Node.js or Bun. This is not a limitation — it is a deliberate security and
performance advantage.

**Key claims, all backed by reproducible evidence:**

| Claim | Evidence |
|---|---|
| Certified must-pass extension set is fully green (`123/123`), with full-manifest health at `221/224` (`98.7%`) | `tests/ext_conformance/reports/gate/must_pass_gate_verdict.json`, `tests/ext_conformance/reports/health_delta/health_delta_report.json` |
| Scenario conformance suite is fully green (`25/25`) | `tests/ext_conformance/reports/scenario_conformance.json` |
| Differential parity triage sample has zero mismatches (`22` match, `0` mismatch, `3` skip) | `tests/ext_conformance/reports/parity/triage.json` |
| Cold load P95 = 106ms (debug build) | `ext_bench_baseline.json` |
| Warm load P99 < 1ms | Performance benchmarks (§5) |
| Event dispatch P99 = 616us | Performance benchmarks (§5) |
| No ambient OS access | Capability model (§2) |
| 30 negative security tests pass | `ext_conformance_negative.rs` |
| Deterministic execution under test | LabRuntime + event loop spec (§4) |

---

## 2. Security Argument

### 2.1 The problem with Node/Bun for extensions

Node.js and Bun give extensions **ambient authority** by default:

- Full filesystem access (`fs.*` with no path restrictions)
- Unrestricted network access (`http`, `net`, `tls`, `dgram`)
- Process spawning (`child_process.spawn`, `cluster`, `worker_threads`)
- Environment variable access (`process.env` — including API keys)
- Native addon loading (`process.dlopen`, `.node` files)
- Debugger/inspector access

A malicious or buggy extension running in Node/Bun can read your SSH keys,
exfiltrate API tokens, spawn background processes, and modify arbitrary files.
The only defense is trust in the extension author.

### 2.2 PiJS: capability-gated connectors

PiJS inverts the security model. Extensions have **zero ambient authority**.
Every side-effecting operation must go through an explicit host connector:

| Operation | Connector | Capability required |
|---|---|---|
| Read files | `pi.tool("read")` or `pi.fs.read()` | `read` |
| Write files | `pi.tool("write")` or `pi.fs.write()` | `write` |
| Execute commands | `pi.exec()` or `pi.tool("bash")` | `exec` |
| HTTP requests | `pi.http()` | `http` |
| Session metadata | `pi.session.*` | `session` |
| UI prompts | `pi.ui.*` | `ui` |

Each connector call is:
1. **Policy-checked** against the extension's granted capabilities
2. **Logged** in a structured audit ledger (`pi.ext.log.v1`)
3. **Scoped** (path restrictions, host allowlists) when applicable
4. **Timeout-enforced** with cancellation support

### 2.3 What is blocked

The following are blocked by construction (not by policy — they do not exist
in the runtime):

| Blocked capability | Reason |
|---|---|
| Raw filesystem access | No `fs` module; only connector-mediated reads/writes |
| Raw network sockets | No `net`/`tls`/`dgram` modules |
| Native addons | QuickJS has no `dlopen` or `.node` loading |
| Worker threads | QuickJS is single-threaded by design |
| `vm` module (code eval) | Not provided; `eval()` works only on bundled code |
| `inspector`/`repl` | Not provided |
| `process.binding()` | Not provided |

### 2.4 Security evidence

**30 negative tests** verify that hostile extensions are correctly rejected:

```bash
cargo test --test ext_conformance_negative --features ext-conformance -- --nocapture
```

These test: forbidden API usage, capability denial, path traversal attempts,
oversized payloads, and malformed registration payloads.

**Guard tests** verify capability enforcement:

```bash
cargo test --test ext_conformance_guard --features ext-conformance -- --nocapture
```

---

## 3. Compatibility Argument

### 3.1 The skeptic's concern

> "QuickJS is just a JS engine. Without Node APIs, nothing real will work."

This is the correct concern, narrowly stated. QuickJS alone cannot run
extensions that import `node:fs`, `node:path`, or `node:child_process`.

### 3.2 The answer: Node API shims

Pi provides targeted shims for the Node APIs that extensions actually use.
These are not full Node implementations — they are thin wrappers over Pi's
capability-gated connectors:

| Node API | PiJS shim | Coverage |
|---|---|---|
| `node:fs` | `readFileSync`, `writeFileSync`, `existsSync`, `readdirSync`, `statSync`, `mkdirSync`, `realpathSync`, promises API | Sufficient for 95%+ of extensions |
| `node:path` | `join`, `resolve`, `dirname`, `basename`, `extname`, `sep`, `delimiter` | Complete |
| `node:os` | `platform`, `homedir`, `tmpdir`, `hostname`, `type`, `arch`, `EOL` | Complete |
| `node:crypto` | `randomBytes`, `createHash`, `randomUUID` | Common subset |
| `node:url` | `URL`, `parse`, `fileURLToPath`, `pathToFileURL` | Complete |
| `node:child_process` | `spawn`, `exec`, `execSync` | Via `exec` capability |
| `node:readline` | Basic `createInterface` | Sufficient |
| `node:module` | `createRequire` stub | Sufficient |
| `node:util` | `format`, `inherits`, `types`, `stripVTControlCharacters` | Common subset |

16+ npm package stubs cover common third-party dependencies (`node-pty`,
`chokidar`, `jsdom`, `turndown`, `@opentelemetry/*`, etc.).

### 3.3 Existence proof: txiki.js

We are not the first to build a capable JS runtime on QuickJS without Node/Bun.
[txiki.js](https://github.com/saghul/txiki.js) (QuickJS-ng + libuv)
demonstrates that a non-Node event loop + OS wrapper layer around QuickJS is
feasible and practical. PiJS goes further by making the OS layer
capability-gated rather than ambient.

### 3.4 Conformance evidence

Current certified conformance status:
- Must-pass extension gate: `123/123` pass, `0` fail, `0` skip.
- Informational stretch set: `98/101` pass, `3` fail, `0` skip.
- Full-manifest health: `221/224` pass (`98.7%`), `0` regressions, `34` fixes vs the 2026-02-07 baseline.

Source artifact:
- `tests/ext_conformance/reports/gate/must_pass_gate_verdict.json` (`generated_at=2026-05-01T03:20:54.460Z`)
- `tests/ext_conformance/reports/health_delta/health_delta_report.json` (`generated_at=2026-05-01T04:10:28.479Z`)

Historical `224/224` and `187/223` figures are retained only as older archival context; they do not override the current must-pass/full-manifest split above.

**Reproduction:**

```bash
cargo test --test ext_conformance_generated --features ext-conformance -- conformance_must_pass_gate --nocapture --exact
cargo test --test ext_conformance_generated --features ext-conformance -- conformance_health_delta --nocapture --exact
```

---

## 4. Determinism Argument

### 4.1 Why determinism matters

Non-deterministic extension runtimes cause:
- Flaky tests (different results on different runs)
- Unreproducible bugs (extension works on one machine, fails on another)
- Security audit gaps (cannot prove what actually happened)

### 4.2 PiJS event loop: formal state machine

The PiJS event loop is specified as a deterministic state machine
(EXTENSIONS.md §1A.4.5):

- **One macrotask per tick** (no interleaving)
- **Microtask drain to fixpoint** after each macrotask
- **Total order** via monotone `seq` counter (deterministic tie-breaking)
- **Timer ordering** by `(deadline_ms, seq)` — stable under equal deadlines
- **No re-entrancy** — hostcall completions enqueue macrotasks, never
  synchronously re-enter JS

### 4.3 LabRuntime: deterministic testing

The `asupersync` runtime provides a `LabRuntime` that gives full control
over scheduling, time, and IO. Tests can:

- Advance time deterministically
- Control task scheduling order
- Inject hostcall completions at precise points
- Assert exact execution traces

```bash
# Run deterministic extension tests
cargo test ext_lab --features ext-conformance -- --nocapture
```

### 4.4 Determinism evidence

Given identical inputs (artifact bytes, event sequence, hostcall results,
clock), PiJS produces identical outputs. This is verified by:

- Golden fixture comparison (16 representative extensions)
- Differential parity triage sample (TS vs Rust): `22` match, `0` mismatch, `3` skip (`25` total)
- Property-based tests (13 proptest suites, 512 cases each)

---

## 5. Performance Argument

### 5.1 Why PiJS is faster

Node/Bun pay startup costs that PiJS avoids:

| Phase | Node.js | PiJS |
|---|---|---|
| Runtime initialization | 200-500ms | 0ms (embedded) |
| Module loading (require/import) | 100-300ms | <1ms (virtual modules) |
| JIT warmup | 50-100ms | 0ms (interpreter) |
| V8 heap allocation | 50-100MB | <5MB (QuickJS) |

For extensions (small code, brief execution), the JIT advantage of V8 is
irrelevant. Extensions register tools and respond to events — they do not
run tight computational loops.

### 5.2 Measured performance

Benchmarked on 103 safe extensions, 10 iterations each (debug build):

| Metric | Value |
|---|---|
| Cold load P50 | 77ms |
| Cold load P95 | 106ms |
| Cold load P99 | 134ms |
| Warm load P50 | 333us |
| Warm load P95 | 734us |
| Warm load P99 | 926us |
| Event dispatch P99 | 616us |
| Fastest cold load | 67ms (trigger-compact) |
| Slowest cold load | 126ms (hjanuschka-plan-mode) |

Release builds are 5-10x faster (expected ~5-10ms cold load).

### 5.3 Performance budgets

Enforced in CI via `tests/perf_budgets.rs`:

| Budget | Threshold | Status |
|---|---|---|
| Cold load P95 | < 200ms | PASS (106ms) |
| Warm load P95 | < 100ms | PASS (734us) |
| Event dispatch P99 | < 5ms | PASS (616us) |

### 5.4 Reproduction

```bash
# PR mode (10 diverse extensions, quick)
PI_BENCH_MODE=pr cargo test --test ext_bench_harness \
  --features ext-conformance -- --nocapture

# Full corpus (103 extensions, thorough)
PI_BENCH_MODE=nightly PI_BENCH_MAX=103 PI_BENCH_ITERATIONS=10 \
  cargo test --test ext_bench_harness --features ext-conformance -- --nocapture
```

---

## 6. Comparison Table

| Property | Node.js | Bun | PiJS (QuickJS) |
|---|---|---|---|
| **Security model** | Ambient authority | Ambient authority | Capability-gated |
| **Audit logging** | Manual | Manual | Built-in per hostcall |
| **Startup time** | 200-500ms | 50-100ms | <1ms (warm) |
| **Memory baseline** | 50-100MB | 30-50MB | <5MB |
| **Determinism** | Non-deterministic | Non-deterministic | Deterministic (formal) |
| **Test harness** | Manual setup | Manual setup | LabRuntime (built-in) |
| **Native addons** | Supported (risk) | Supported (risk) | Blocked (safe) |
| **WebAssembly** | Built-in | Built-in | Via wasmtime bridge* |
| **Compatibility** | 100% Node API | ~98% Node API | `123/123` certified must-pass extensions; `221/224` full-manifest health (harness scope) |
| **Dependencies** | node binary (80MB+) | bun binary (60MB+) | Embedded (0 bytes) |

*PiWasm bridge is available behind `wasm-host` for extensions that require
WebAssembly. It links only explicit host imports and bounded Emscripten
compatibility stubs; unsupported imports fail closed during instantiation. No
current corpus extension requires it as a runtime dependency.

---

## 7. Reproduction Steps

All evidence can be regenerated from scratch:

```bash
# 1. Conformance (all 224 current vendored extensions)
cargo test --test ext_conformance_generated --features ext-conformance -- \
  conformance_sharded_matrix --nocapture --exact

# 2. Performance (103 safe extensions)
PI_BENCH_MODE=nightly PI_BENCH_MAX=103 PI_BENCH_ITERATIONS=10 \
  cargo test --test ext_bench_harness --features ext-conformance -- --nocapture

# 3. Security (30 negative tests)
cargo test --test ext_conformance_negative --features ext-conformance -- --nocapture

# 4. Determinism (property-based)
cargo test extensions_property --features ext-conformance -- --nocapture

# 5. Budget compliance
cargo test --test perf_budgets --features ext-conformance -- --nocapture
```

**Generated artifacts:**

| Artifact | Location |
|---|---|
| Conformance shard report | `tests/ext_conformance/reports/sharded/shard_0_report.json` |
| Scenario conformance report | `tests/ext_conformance/reports/scenario_conformance.json` |
| Parity triage report | `tests/ext_conformance/reports/parity/triage.json` |
| Perf baseline | `tests/perf/reports/ext_bench_baseline.json` |
| Perf report | `tests/perf/reports/BASELINE_REPORT.md` |
| Budget summary | `tests/perf/reports/budget_summary.json` |
| Extension catalog | `docs/extension-catalog.json` |

---

## 8. References

1. **QuickJS** — Fabrice Bellard. https://bellard.org/quickjs/
   - Bytecode is version-coupled and not security-checked before execution.
   - Job queue (`JS_ExecutePendingJob`) must be driven by the embedder.

2. **txiki.js** — Saghul. https://github.com/saghul/txiki.js
   - Existence proof: QuickJS + libuv event loop + OS wrappers without Node.

3. **wasmtime** — Bytecode Alliance. https://wasmtime.dev/
   - Component model for WASM extensions (Tier A runtime).

4. **EXTENSIONS.md** — Pi Agent Rust extension system architecture.
   - §1A.4: PiJS Runtime Contract (event loop state machine).
   - §2A: Extc Compatibility Contract (rewrite rules, forbidden APIs).
   - §3.2A: Unified capability model.
