Introduction
Parallax is a universal polyglot execution runtime written in Rust.
Capture program state in one language, encode it as language-neutral PIR, and restore it in another — with real workers, measured timings, and honest semantic-loss reporting.
This site documents what exists today in the 0.1.x workspace: Python ↔ JavaScript state migration, snapshots, the plx CLI, and a constrained WASM executor.
| Resource | Link |
|---|---|
| Source | github.com/parallax-runtime/parallax |
| Docs (this site) | parallax-runtime.github.io/parallax |
| Changelog | CHANGELOG.md |
| Security | SECURITY.md |
| Privacy | PRIVACY.md |
| License | Apache-2.0 |
| MSRV | Rust 1.75 |
Start here
- What Parallax is — scope and non-goals
- Getting started — install and first commands
- Migration demo — Python → JavaScript in one command
- Architecture — crates, workers, pipeline
Design principles
- No fake demos — unsupported work returns structured
Unsupported/MigrationRejected - Measured timings — capture / analyze / convert / restore durations are real wall-clock microseconds
- Explicit capabilities — every adapter declares what it can and cannot do
- Process isolation — Python and JavaScript guests run in subprocess workers over a versioned NDJSON protocol
What Parallax is
Parallax is infrastructure for polyglot execution and state migration.
You write (or already have) a small program that defines data bindings. Parallax:
- Executes it in a host runtime (Python, JavaScript, or WASM)
- Captures named bindings into PIR (Parallax Intermediate Representation)
- Analyzes semantic compatibility for a target runtime
- Converts the PIR under an explicit policy
- Restores the bindings into the target runtime
- Optionally emits a source preview and/or a
.plxsnapshot
The headline path in 0.1 is:
Python program defining `state` → PIR → JavaScript object
JavaScript program defining `state` → PIR → Python dict
What Parallax is not
Be explicit about non-goals so expectations stay honest:
| Not this | Reality in 0.1 |
|---|---|
| A full cross-language VM that migrates running stacks | Stack / continuation migration is not supported |
| Transparent function / closure migration | Functions become Unsupported |
| A language transpiler | Emit is a preview of restored bindings, not a compiler |
| A sandbox for untrusted multi-tenant SaaS | Limits exist; network/FS isolation is policy-level, not a hardened jail |
| A drop-in replacement for gRPC / FFI | Different problem — Parallax moves values, not RPCs |
Who it is for
- Runtime / compiler engineers exploring polyglot state interchange
- Tooling authors who need a checked, language-neutral value layer
- Projects that want opt-in migration with loss reporting instead of silent coercion
Version posture
0.1.0 is an early but working release of the migration path. APIs and PIR schema may evolve; schema and protocol versions are versioned independently (PIR_SCHEMA_VERSION, PROTOCOL_VERSION, SNAPSHOT_FORMAT_VERSION).
Getting started
Prerequisites
| Dependency | Required for | Notes |
|---|---|---|
| Rust 1.75+ (stable) | Build / CLI | rustup |
| Node.js 18+ | JavaScript adapter | Detected as node |
| Python 3.10+ | Python adapter | Detected as python, python3, py, then common install paths |
| Git | Clone | Optional if you use a release binary later |
Windows tip: The Microsoft Store
pythonalias often fails. Install from python.org or ensure a real interpreter is on PATH.plx doctorwill say clearly when Python is missing.
Install from source
git clone https://github.com/parallax-runtime/parallax.git
cd parallax
cargo build -p parallax-cli --release
Binaries land at:
target/release/plx
target/release/parallax
plx and parallax are equivalent entry points.
Add the release directory to your PATH, or invoke via Cargo:
cargo run -p parallax-cli --bin plx -- doctor
First health check
plx doctor
Example healthy output (versions will vary):
Parallax 0.1.0
Host: windows x86_64
[javascript] READY
binary: node
version: 24.x
[python] READY
binary: .../python.exe
version: 3.12.x
[wasm] READY
binary: wasmtime (in-process)
Doctor: OK — at least one runtime is ready.
JSON form:
plx doctor --json
Run a program
plx run examples/demo.py
plx run examples/demo.js
plx run examples/hello.wat --entry run
Runtime is inferred from the file extension (.py, .js, .wat / .wasm) unless you pass --runtime.
Next steps
Migration demo
The canonical demo migrates a small Python object into JavaScript (and back).
Source (examples/demo.py)
state = {
"username": "Ada",
"score": 42,
"projects": ["compiler", "runtime", "vm"],
}
print("python state ready:", state)
Python → JavaScript
plx migrate examples/demo.py --to javascript -o examples/demo.migrated.js
What happens:
- Capture — Python worker executes the file and encodes
stateas PIR - Analyze — semantic-loss pass for JavaScript
- Convert — PIR rewritten under the default conversion policy
- Restore — JavaScript worker materializes the bindings
- Emit — optional JS source preview written to
-o
You should see measured timings similar to:
Migration python → javascript (OK)
timings:
capture: … µs
analyze: … µs
convert: … µs
restore: … µs
total: … µs
migrated bindings:
state: map{3}
Numbers are wall-clock measurements, not placeholders.
Emitted preview (shape):
const state = { "username": "Ada", "score": 42, "projects": ["compiler", "runtime", "vm"] };
JavaScript → Python
plx migrate examples/demo.js --to python -o examples/demo.migrated.py
Integer precision policy
examples/demo_bigint.py uses a value outside the JS safe integer range (9007199254740993).
| Flags | Result |
|---|---|
| (default) | Convert to BigInt (prefer_bigint=true) — migration OK, finding SAFE |
--no-prefer-bigint | Rejected as LOSSY / MigrationRejected |
--no-prefer-bigint --allow-lossy | Coerce to JS Number (opt-in precision loss) |
plx migrate examples/demo_bigint.py --to javascript --no-prefer-bigint
# → fails with structured MigrationRejected
plx migrate examples/demo_bigint.py --to javascript -o examples/demo_bigint.migrated.js
# → score becomes 9007199254740993n in the preview
Snapshot along the way
plx migrate examples/demo.py --to javascript \
--snapshot /tmp/demo.migrated.plx \
-o /tmp/demo.migrated.js
plx inspect /tmp/demo.migrated.plx
Machine-readable report
plx migrate examples/demo.py --to javascript --json
Includes the migration report (findings + timings) and the migrated PIR bindings object.
Transmute — project migration
Transmute migrates an entire software project from one language/ecosystem to another using a semantic pipeline — not file-by-file text translation.
SOURCE PROJECT
↓
Project Analysis → ProjectGraph + PUIR
↓
Migration plan (deps, frameworks, layout)
↓
Code generation
↓
Build → Test → Repair → Report
↓
TARGET PROJECT
First supported path
| Source | Target | Status |
|---|---|---|
| TypeScript / JavaScript (Node, Express) | Rust (Axum) | Implemented (weather-api demo) |
| Python | Rust | Analysis / planning only |
| Other pairs | — | Structured Unsupported |
CLI
# Analyze + migrate a project
plx migrate ./examples/weather-api --to rust --output ./examples/weather-api-rust --verify
# Plan only
plx migrate ./api --to rust --dry-run
# Quality gates
plx migrate ./api --to rust --require-build --require-tests --min-confidence 0.9
# Origin lookup (source maps)
plx origin src/service.rs:14 -C ./examples/weather-api-rust
plx migrate auto-selects Transmute when the path is a project directory (or --to is a language such as rust). Use --mode value for PIR state migration of a single guest program.
Representations
| IR | Meaning |
|---|---|
| PIR | Values / heap bindings |
| PUIR | Program semantics (functions, types, intent ops) |
| PCIR / UES | Suspended execution (Continuum) |
| ProjectGraph | Modules, deps, edges, entrypoints, tests |
PUIR is versioned independently (puir_schema on plx version).
Workspace
Migrations write .parallax/ under the source root (project.json, graph.json, puir.json, plan.json, …) for inspection and future incremental updates.
Demo
plx migrate examples/weather-api --to rust -o examples/weather-api-rust --require-build --require-tests
cd examples/weather-api-rust
cargo test
cargo run
See PARALLAX_MIGRATION.md and parallax-report.json in the output directory.
Honesty rules
- Compatibility percentages come from measured analysis (PUIR coverage, dep confidence, …) — never invented.
- Uncertain regions get
// PARALLAX REVIEW:markers and report entries. - Secrets in
.envare never copied; only.env.examplenames are emitted. - Cross-language project migration is not Continuum live-stack resume.
Mirror — continuous cross-language sync
Mirror keeps a migrated target project synchronized with an evolving source project. After Transmute produces a first target, link the pair and sync incrementally instead of remigrating the whole repository.
TypeScript changes
↓
semantic diff vs baseline PUIR
↓
regenerate affected regions
↓
build + differential tests
↓
Rust (or other target) updated
First supported path
| Source | Target | Tier |
|---|---|---|
| TypeScript / JavaScript | Rust | Tier 1 (weather-api demo) |
| Python | Rust | Tier 2 (planning / partial) |
| Rust → TypeScript | — | Experimental (reverse sync gated) |
Tiers reflect implemented conformance, not marketing claims.
Workflow
# One-time: migrate, then link
plx migrate ./examples/weather-api --to rust -o ./examples/weather-api-rust --require-build --require-tests
plx link ./examples/weather-api ./examples/weather-api-rust
# Ongoing
# edit TypeScript…
plx sync
plx sync --check # CI / no writes
plx status # drift summary
plx ci # sync --check + target verify
Link metadata lives under the target:
.parallax-link/
├── link.json
├── source-index.bin
├── semantic-map.bin
├── dependency-map.json
├── manual-regions.json
├── ownership.json
├── baselines/
└── history/
Default policy: source-authoritative.
Commands
| Command | Purpose |
|---|---|
plx link <src> <tgt> | Create Mirror link |
plx sync | Incremental translate + verify |
plx sync --check | Freshness only (fails if stale) |
plx sync --reverse | Target→source when node is ExactYes (else Unsupported) |
plx sync --patch | Preview regenerate without writing |
plx status / --json | Drift / machine-readable status |
plx ci | sync --check + differential verify |
plx history | Sync history |
plx rollback | Restore last pre-sync target snapshot |
plx explain <file:line> -C <tgt> | Source-map explanation |
plx why <file> -C <tgt> | Why a target file changed |
plx verify | Run differential / property notes |
What sync does (and does not)
Does
- Semantic diff of PUIR (not text-only)
- Stable semantic IDs (
plx:function:…) - Regenerate affected modules transactionally (snapshot → apply → build/test → commit or restore)
- Preserve files marked with manual
BEHAVIOR_CHANGEownership - Report testing confidence (migrated suite), never claim formal proof
Does not (yet / honest limits)
- Full bidirectional live migration without review gates
- Property-based equivalence as a complete fuzzer (
--propertynotes only) plx watchdaemon (not shipped)- Guaranteed idiom preservation for every manual refactor
- All language pairs as Tier 1
Manual edits
Ownership metadata is stored in .parallax-link/ownership.json and manual-regions.json (sidecar preferred over invasive markers). Behavior-changing target edits block overwrite under source-authoritative policy until you choose preserve / overwrite / inspect.
CI
- run: plx sync --check
- run: plx ci
Fails when the link is stale, the target fails build/tests, or verification mismatches.
Related
- Transmute — initial project migration
- CLI — command reference
- Limitations — capability honesty
Event Horizon
Event Horizon is Parallax's semantic reconstruction layer for migrations that look "impossible" on paper — dynamic Python, open dispatch, eval, native extensions, and other constructs with no direct target equivalent.
Philosophy: no direct equivalent ≠ migration impossible. Preserve behavior via native lowering, specialized capsules, polyglot islands, or (eventually) behavioral synthesis — never silent semantic drift.
Crate layout (consolidated)
Phase X originally scaffolded ~20 small crates; they are merged into one workspace member:
| Crate | Role |
|---|---|
parallax-horizon | Full Event Horizon stack — PVABI (pvabi/), semantics, behavior, IR, VCS stubs, and orchestration (analyze_impossible, debt, dissolve, detach, etc.) |
Inside parallax-horizon, modules are grouped as pvabi/, semantics/, behavior/, ir/, and vcs/. Public types are re-exported at the crate root (e.g. ProjectObserver, PreservationPolicy, SemanticPatch) and also available via submodule paths (e.g. parallax_horizon::pvabi::PvValue, parallax_horizon::behavior::ProjectObserver, parallax_horizon::vcs::SemanticPatch).
CLI commands
Top-level plx commands (Windows stack size is raised in parallax-cli/build.rs):
| Command | Purpose |
|---|---|
plx observe [path] | Static semantic inspection (languages, dynamic signals, effects) |
plx impossible [path] --to rust | Hard barriers + proposed preservation strategies |
plx dissolve [path] | One-step polyglot island shrink (scaffold) |
plx debt [path] | Compatibility debt / target purity score |
plx detach [path] | Whether source runtime can be dropped (threshold-based) |
plx reconstruct | Behavioral reconstruction status (scaffold) |
plx optimize-migration [path] | Propose native replacements for capsules/islands |
plx explain-barrier --id N [path] | Explain a barrier from plx impossible |
plx blame <file:line> | Semantic blame (scaffold — needs .plxmap.json) |
plx cherry-pick <commit> | Semantic cherry-pick (scaffold) |
plx patch --example | Print example .plxp semantic patch |
Use --json on any command for machine-readable output.
Example
plx observe examples/hostile-dynamic
plx impossible examples/hostile-dynamic --to rust
plx debt examples/hostile-dynamic --to rust
The hostile-dynamic fixture includes getattr, decorators, and asyncio signals.
Honest limits
- Observatory uses static heuristics, not dynamic tracing.
- Behavior synthesis / CEGIS / reconstruct are scaffolds — no end-to-end behavioral equivalence yet.
- Semantic git (
blame,cherry-pick) requires.plxmap.json+ git linkage (not wired). - Debt / dissolve / detach scores are estimates from preservation decisions — verify with tests before production detach.
- Transmute / Mirror / weather-api paths are unchanged; Horizon is additive analysis tooling.
See also: Transmute, Mirror, Limitations.
CLI reference
Binaries: plx and parallax (identical).
plx [GLOBAL FLAGS] <COMMAND>
Global flags
| Flag | Description |
|---|---|
--json | Prefer JSON on stdout for supporting commands; errors as JSON on stderr when set |
-v, --verbose | Richer human diagnostics |
--trace | Structured tracing to stderr (JSON subscriber) |
plx run <file>
Execute a guest program.
| Option | Default | Description |
|---|---|---|
-r, --runtime <name> | inferred | python / javascript / wasm (aliases: py, js, node, wat) |
--timeout-ms <n> | 30000 | Wall-clock timeout |
--entry <name> | run (WASM) | Exported WASM function |
--capture <names> | — | Comma-separated bindings to capture after execution |
Examples:
plx run examples/demo.py
plx run examples/demo.py --capture state
plx run examples/hello.wat --entry run
plx run app.js --runtime javascript --timeout-ms 5000
plx migrate <file> --to <runtime>
Capture → analyze → convert → restore.
| Option | Default | Description |
|---|---|---|
-t, --to <runtime> | required | Target runtime |
-f, --from <runtime> | inferred | Source runtime override |
--capture <names> | state | Bindings to migrate |
--allow-lossy | off | Permit known-lossy conversions |
--no-prefer-bigint | off | Do not auto-promote unsafe ints to BigInt |
-o, --output <path> | — | Emit target-language source preview |
--snapshot <path> | — | Write migrated .plx |
--pir-input | off | Treat file as a PIR JSON document (skip live capture) |
plx migrate examples/demo.py --to javascript -o out.js
plx migrate examples/demo.js --to python --json
plx snapshot <file> -o <out.plx>
Capture bindings into a validated snapshot.
| Option | Default | Description |
|---|---|---|
-o, --output | required | Output path |
-r, --runtime | inferred | Runtime override |
--capture | state | Binding names |
--label | — | Optional label stored in the snapshot |
plx inspect <file.plx>
Validate magic, format version, PIR schema, and content hash; print summary (or JSON).
plx restore <file.plx> --target <runtime>
Restore snapshot bindings into a fresh worker and report restored names / types.
plx runtimes
List registered adapters with readiness and host versions (production + scaffold connectors).
plx connectors
Full language connector catalog (60+ languages) with maturity and transmute roles.
plx connectors
plx connectors --pairs
plx connectors java
plx connectors --maturity scaffold --family managed_vm
plx connectors --json
See Language connectors.
Atlas (plx analyze / adapters / …)
Modular stack detection and adapter planning. Guide: Atlas architecture.
plx adapters
plx adapters info typescript
plx adapters capabilities python
plx adapters health
plx analyze examples/weather-api --to rust
plx stacks
plx mappings axios
plx compatibility python rust
plx unsupported examples/weather-api
plx explain-stack examples/weather-api --to rust
| Command | Notes |
|---|---|
adapters [list|info|capabilities|health|update|report] | Built-in Atlas registry |
analyze [path] [--to lang] [--write-lock] | Detect stack + estimate coverage |
stacks | Target stack presets |
mappings [query] | Dependency equivalence DB |
compatibility <src> <tgt> | Pair feature scores |
unsupported [path] | Scaffold / limited adapters |
explain-stack [path] [--to] | Why a target stack was chosen |
adapter new|validate | Third-party tooling stubs |
plx capabilities [runtime]
Print the capability matrix (YES / PARTIAL / EXPERIMENTAL / NO).
Optional positional filter: plx capabilities python.
plx doctor
Probe the host for Python, Node, and WASM readiness. Exit non-zero if no runtime is ready.
plx bench
Measured micro-benchmark of capture → migrate → restore.
| Option | Default | Description |
|---|---|---|
--iterations <n> | 5 | Sample count |
--file <path> | examples/demo.py | Source program |
--to <runtime> | javascript | Target |
plx bench --iterations 20 --json
Mirror (plx link / sync / …)
Continuous sync after a Transmute migration. Full guide: Mirror.
plx link ./api-ts ./api-rust
plx sync
plx sync --check
plx status --json
plx ci
plx history
plx rollback
plx explain src/service.rs:20 -C ./api-rust
plx why src/service.rs -C ./api-rust
plx verify
| Command | Notes |
|---|---|
link <src> <tgt> [--policy …] | Default policy: source-authoritative |
sync [--check|--reverse|--patch|--lint|--no-verify] | Incremental; --check is non-mutating |
ci | sync --check + differential verify |
status | Drift; --json for editors/CI |
plx version
Print product / schema versions (--format json or global --json).
Capabilities matrix
Adapters declare capabilities explicitly. Attempting an unsupported operation returns a structured error — it is never silently ignored.
Run plx capabilities on your machine for the live table.
Python
| Capability | Level |
|---|---|
| Values | YES |
| Globals | YES |
| Locals | PARTIAL |
| Functions | PARTIAL (encoded as function / unsupported for migrate) |
| Closures | PARTIAL |
| Stack capture | EXPERIMENTAL |
| Stack frames / control position | EXPERIMENTAL (explicit checkpoint only) |
| Continuation capture / restore | EXPERIMENTAL (same-runtime checkpoint) |
| Cross-runtime resume | NO |
| Async migration | NO |
| Execution | YES |
| Stdio capture | YES |
| Timeouts | YES |
| Resource limits | PARTIAL |
| Cancellation | YES |
JavaScript (Node.js)
| Capability | Level |
|---|---|
| Values | YES |
| Globals | YES |
| Locals | PARTIAL |
| Functions | PARTIAL |
| Closures | PARTIAL |
| Stack capture | NO |
| Stack frames / control position | EXPERIMENTAL (explicit checkpoint only) |
| Continuation capture / restore | EXPERIMENTAL (same-runtime checkpoint) |
| Cross-runtime resume | NO |
| Async migration | NO |
| Execution | YES |
| Stdio capture | YES |
| Timeouts | YES |
| Resource limits | PARTIAL |
| Cancellation | YES |
WebAssembly (wasmtime)
| Capability | Level |
|---|---|
| Values | PARTIAL |
| Globals / locals / closures | NO |
| Functions | PARTIAL (call zero-arg exports) |
| Stack / continuation / async | NO |
| Execution | YES |
| Stdio capture | NO |
| Timeouts / fuel / limits | YES |
| Cancellation | YES |
| State restore / migrate | NO |
Cross-runtime migration
| From \ To | Python | JavaScript | WASM |
|---|---|---|---|
| Python | restore OK | migrate OK | Unsupported |
| JavaScript | migrate OK | restore OK | Unsupported |
| WASM | Unsupported | Unsupported | Unsupported |
Language connectors
Run plx connectors for the live catalog. Summary:
| Maturity | Count (approx.) | Execute / migrate |
|---|---|---|
| production | 4 (py, js, ts analyze, wasm) | Real (wasm: execute only) |
| experimental | ruby, php, go (+ Rust target) | Workers when host present |
| scaffold / planned | 50+ | NO (registered Unsupported) |
Scaffold adapters appear in plx runtimes as DEGRADED (host found) or UNAVAILABLE (host missing). That is intentional.
Mirror (project sync)
| Capability | Level |
|---|---|
| Link TS/JS → Rust | YES (Tier 1 demo) |
| Semantic diff + incremental sync | YES |
sync --check / plx ci | YES |
| Manual-region preservation (sidecar) | PARTIAL |
| Three-way semantic merge | PARTIAL (conflicts reported; no silent guess) |
| Differential execution | PARTIAL (migrated test suite; not formal proof) |
| Reverse sync | EXPERIMENTAL / gated Unsupported |
| Watch daemon | NO |
| Property fuzz equivalence | EXPERIMENTAL notes only |
Limitations
This page is the honest list for 0.1. Prefer reading it over marketing claims.
Explicitly unsupported
- Migrating arbitrary live call stacks / instruction pointers across runtimes (Continuum does not claim this)
- Cross-runtime continuation resume (contract returns Unsupported; same-runtime explicit checkpoint is Experimental — see Continuum)
- Deterministic replay engine (journal schema only)
- Migrating functions / closures as callable values across runtimes
- Migrating in-flight async tasks / promises / coroutines
- WASM binding capture or restore / continuum
- Non-string map keys round-tripping cleanly to JS objects
- Shared object-identity graphs with cycles (bindings-first model;
refexists but restore is incomplete) - Hard multi-tenant isolation (no seccomp/Seatbelt/Windows job-object enforcement yet)
- Mirror reverse sync for arbitrary nodes (gated; returns Unsupported unless ExactYes)
- Mirror property-based equivalence as a complete fuzzer (
plx verify --propertyis notes / confidence only — not proof) - Mirror watch daemon (
plx watch) — not shipped - Treating all language pairs as equally mature (see pair tiers in Mirror and Connectors)
- Scaffold connectors (Go, Java, Ruby, C#, …): identity + host probe only — execute/restore/migrate return Unsupported until a real worker/codegen ships
Semantic edge cases
| Case | Default behavior |
|---|---|
Python int outside JS safe integer range | Promote to JS BigInt |
Same, with --no-prefer-bigint | Reject (MigrationRejected / LOSSY) |
Same, with --allow-lossy and no BigInt | Coerce to Number (lossy) |
Python tuple → JS | Becomes Array (SAFE) |
Python set → JS | Becomes Array (SAFE) |
bytes → JS | Uint8Array (SAFE) |
| Unknown host types | Unsupported PIR node |
Host discovery quirks
- Windows Store Python stubs are treated as unavailable
- Discovery order:
python,python3,py, then%LOCALAPPDATA%\Programs\Python\*\python.exeand Program Files trees - Node discovery:
node,nodejs, then%ProgramFiles%\nodejs\node.exe
Stability
- PIR schema, protocol, snapshot format, adapter interface, UES format, PCIR schema, PUIR schema, and Mirror link format are versioned independently (
1today unless noted) - Breaking changes will bump those constants; loaders reject mismatches
- CLI flag surface may grow; prefer
--jsonfor scripting - See Versioning for which constant to bump
Architecture
Parallax separates orchestration in Rust from guest execution in language-specific workers.
System overview
flowchart LR
CLI["plx / parallax CLI"] --> RT["parallax-runtime<br/>RuntimeManager"]
RT --> PY["Python adapter"]
RT --> JS["JS adapter"]
RT --> WASM["WASM adapter"]
PY --> PW["python worker.py<br/>NDJSON stdin/stdout"]
JS --> JW["node worker.js<br/>NDJSON stdin/stdout"]
WASM --> WT["wasmtime<br/>in-process + fuel"]
RT --> MIG["parallax-migrate"]
RT --> SNAP["parallax-snapshot"]
MIG --> PIR["parallax-ir PIR"]
SNAP --> PIR
Process model
sequenceDiagram
participant Core as Parallax Core
participant Worker as Runtime Worker
participant Guest as Guest program
Core->>Worker: hello
Worker-->>Core: hello ack + host version
Core->>Worker: execute + capture names
Worker->>Guest: exec / vm.run
Guest-->>Worker: bindings
Worker-->>Core: PIR-tagged JSON bindings
Core->>Worker: restore bindings
Worker-->>Core: restored summaries
Core->>Worker: shutdown
Python and JavaScript guests never share an address space with the core. WASM is the exception: it runs in-process via wasmtime with fuel limits.
Crate map
The workspace ships 22 Rust crates (Event Horizon is one crate — not a meta-workspace explosion).
| Layer | Crate | Responsibility |
|---|---|---|
| Core | parallax-core | Errors, IDs, capabilities, execution model, semantic-loss enums |
| IR | parallax-ir | PIR values, documents, hashing |
| IR | parallax-pcir | Continuation IR (Continuum) |
| IR | parallax-puir | Universal Program IR (Transmute) |
| IR | parallax-ues | Universal Execution State, safepoints |
| Protocol | parallax-protocol | Versioned NDJSON envelopes |
| Project | parallax-project | ProjectGraph for whole-repo migration |
| Security | parallax-security | Sandbox / limit policy |
| Diagnostics | parallax-diagnostics | Tracing helpers, doctor report types |
| Snapshot | parallax-snapshot | .plx format + integrity validation |
| Migrate | parallax-migrate | Analyze + convert PIR across runtimes |
| Transmute | parallax-transmute | Project analyze → plan → codegen → repair |
| Mirror | parallax-mirror | Linked sync, semantic diff, CI gates |
| Horizon | parallax-horizon | Impossible migration analysis (observe / debt / impossible) |
| Atlas | parallax-adapter-sdk | Adapter contracts, manifests, capabilities |
| Atlas | parallax-atlas | Registry, stack detection, parallax.lock |
| Connectors | parallax-connectors | 60+ language catalog + experimental workers |
| Runtime | parallax-runtime | Adapter trait, discovery, worker process, manager |
| Runtime | parallax-adapter-python | CPython subprocess adapter |
| Runtime | parallax-adapter-js | Node.js subprocess adapter |
| Runtime | parallax-adapter-wasm | wasmtime adapter |
| CLI | parallax-cli | plx / parallax binaries |
Product layers
flowchart TB
subgraph exec [Execution and value migration]
CLI1[plx run / migrate / snapshot]
RT[parallax-runtime]
PIR[parallax-ir PIR]
CLI1 --> RT --> PIR
end
subgraph project [Project migration]
TM[parallax-transmute]
AT[parallax-atlas]
PUIR[parallax-puir]
CLI2[plx migrate dir / analyze]
CLI2 --> AT --> TM --> PUIR
end
subgraph sync [Continuous sync]
MR[parallax-mirror]
CLI3[plx link / sync / ci]
CLI3 --> MR
end
subgraph horizon [Event Horizon]
HZ[parallax-horizon]
CLI4[plx impossible / observe]
CLI4 --> HZ
end
| Product surface | Primary crates | Tier-1 maturity |
|---|---|---|
| Transmute | transmute, puir, project, atlas | TypeScript/JS → Rust (weather-api demo) |
| Mirror | mirror, transmute | Linked TS ↔ Rust sync with CI gate |
| Continuum | ues, pcir, migrate | Same-runtime checkpoint only |
| Atlas | atlas, adapter-sdk | 120+ detectors; honest maturity |
| Connectors | connectors, runtime | 60+ languages; Ruby/PHP/Go workers experimental |
| Event Horizon | horizon | Dynamic/reflection debt analysis |
See Atlas adapter index and Horizon.
Migration pipeline
flowchart TD
A[Source program] --> B[Capture bindings]
B --> C[PIR document]
C --> D[Analyze semantic loss]
D --> E{Policy allows?}
E -->|no| F[MigrationRejected / Unsupported]
E -->|yes| G[Convert PIR]
G --> H[Restore into target worker]
H --> I[Report timings + findings]
I --> J[Optional emit / .plx]
- Capture — execute source; worker encodes named bindings as PIR JSON
- Analyze — classify loss for the target (
NONE…UNSUPPORTED) - Convert — rewrite PIR under
ConversionPolicy - Restore — target worker materializes values
- Report — findings + real microsecond timings
Concurrency
RuntimeManager enforces a configurable maximum concurrent adapter operations (default 4). Excess work fails with ResourceLimitExceeded rather than unbounded spawn.
PIR — Parallax Intermediate Representation
Schema version: 1 (PIR_SCHEMA_VERSION)
PIR is a tagged JSON value graph used for capture, snapshots, and migration. It is language-neutral and intentionally boring: dictionaries of typed nodes, not bytecode.
Document shape
{
"schema": 1,
"bindings": {
"state": { "t": "map", "entries": [ /* ... */ ] }
},
"objects": {},
"roots": [],
"metadata": {}
}
bindings— primary migration surface (name → value)objects/roots— reserved for richer heap graphs (reftargets)metadata— free-form; migration fillsmigrated_from/migrated_to
Value tags
t | Payload | Notes |
|---|---|---|
null | — | None / null / undefined |
bool | v: bool | |
int | v: { "decimal": "…" } | Arbitrary precision decimal text |
float | v: number | IEEE-754 binary64 |
string | v: string | UTF-8 |
bytes | v: base64 | |
list | v: [...] | Arrays |
tuple | v: [...] | Becomes list when targeting JS |
set | v: [...] | Becomes list when targeting JS |
map | entries: [{key,value}] | Ordered; string keys preferred |
bigint | v: decimal string | First-class in JS restore |
function | name, descriptor | Not migratable |
ref | id | Object-graph pointer |
unsupported | reason, repr, type_name? | Explicit failure node |
Example — demo state
{
"t": "map",
"entries": [
{ "key": { "t": "string", "v": "username" }, "value": { "t": "string", "v": "Ada" } },
{ "key": { "t": "string", "v": "score" }, "value": { "t": "int", "v": { "decimal": "42" } } },
{
"key": { "t": "string", "v": "projects" },
"value": {
"t": "list",
"v": [
{ "t": "string", "v": "compiler" },
{ "t": "string", "v": "runtime" },
{ "t": "string", "v": "vm" }
]
}
}
]
}
Validation
PirDocument::validate rejects unknown/future schema versions and dangling roots. Snapshots additionally hash a canonical payload and reject tampering.
Offline PIR input
plx migrate path/to/doc.json --to javascript --pir-input
Skips live capture; useful for fixtures and fuzz corpora.
Continuum (Phase VI)
Continuum moves beyond value/state PIR exchange toward suspended computation migration:
pause → capture continuation → Universal Execution State → translate → resume
This chapter describes what is implemented today versus what remains experimental or unsupported.
PIR vs UES vs PCIR
| Artifact | Models | Version constant |
|---|---|---|
| PIR | Portable values / object graphs | pir_schema |
| UES | Suspended execution (control, frames, heap, capabilities) | ues_format |
| PCIR | Portable control-flow subset for supported regions | pcir_schema |
These versions advance independently (see Versioning). Serialization alone is not migration.
What is real in this milestone
- Types + serde for
UniversalExecutionState,UniversalFrame, PCIR ops, binary/JSON envelopes, version rejection. - Safepoint model with machine-readable reports (
can_capture/snapshot/replay/migrate, targets, semantic loss). - Explicit checkpoint capture in Python and JavaScript workers via
parallax.checkpoint(label)(and@parallax.safepoint/parallax.safepointconceptually). - Same-runtime resume of the post-checkpoint source region with restored bindings (not a full program restart).
MigrationContractanalysis before continuation attempts; clear reject reports when unsatisfied.- Continuation capability matrix via CLI.
What is Explicitly Unsupported / Experimental
| Capability | Status |
|---|---|
| Arbitrary live stack frame migration | NO — not claimed |
| Cross-runtime continuation resume | NO (contract-gated) |
| Deterministic replay engine | UNSUPPORTED (journal schema / hooks only) |
| Async / await / yield migration | NO |
| WASM continuum | NO |
| Same-runtime checkpoint capture + resume | EXPERIMENTAL |
If a path is not truly implemented, Continuum returns structured Unsupported / capability levels (YES / PARTIAL / EXPERIMENTAL / NO) — it never pretends resume worked.
Safepoints
Supported boundary for this pass: explicit checkpoint.
x = 1
parallax.checkpoint("after_init")
x = x + 41 # runs only on resume
At the safepoint the worker reports whether it can capture / snapshot / replay / migrate, candidate targets, and semantic-loss notes.
CLI
# Continuation capability matrix
plx capabilities python --continuations
plx capabilities --continuations --json
# Capture UES at checkpoint (experimental)
plx continuum examples/checkpoint_demo.py -o demo.ues.json --json
# Same-runtime resume after capture
plx continuum examples/checkpoint_demo.py --resume --json
# Inspect a written UES
plx continuum demo.ues.json --inspect-ues
# Contract-only analysis
plx continuum examples/checkpoint_demo.py --analyze-only -t javascript
# Honest continuation migrate mode (rejects cross-runtime)
plx migrate examples/checkpoint_demo.py -t javascript --mode continuation
Value/state PIR migration remains the default:
plx migrate examples/demo.py -t javascript
Migration contracts
Before a live continuation attempt, Parallax builds a MigrationContract describing required surviving semantics (values, locals, control position, stack frames, same-runtime vs cross-runtime resume, …). Analysis runs first; unsatisfied contracts produce a readable reject report.
Crates
parallax-pcir— Continuation IR ops / programsparallax-ues— UES, frames, safepoints, deterministic hooks, continuation matrixparallax-migrate::contract—MigrationContract+ analysis
Related
Migration engine
Implemented in parallax-migrate.
Goals
- Move data bindings between runtimes through PIR
- Detect semantic incompatibilities before pretending success
- Keep policy explicit (
ConversionPolicy/ CLI flags) - Report measured phase timings
Loss taxonomy
| Level | Meaning | Default policy |
|---|---|---|
NONE | Equivalent | Allow |
SAFE | Representation differs, semantics preserved | Allow |
POTENTIALLY_LOSSY | Depends on contents | Allow (allow_potentially_lossy) |
LOSSY | Known corruption risk (e.g. unsafe int → Number) | Reject unless --allow-lossy |
UNSUPPORTED | Cannot represent | Keep as Unsupported node (or reject if configured) |
Conversion policy knobs
| Field / flag | Default | Effect |
|---|---|---|
prefer_bigint / (default on) | true | Unsafe ints → PIR bigint for JS |
--no-prefer-bigint | — | Disable BigInt promotion |
--allow-lossy | off | Permit LOSSY coercions |
allow_potentially_lossy | true | Allow amber findings |
reject_unsupported | false | Hard-fail on Unsupported |
Phase timings
MigrationReport.timings fields (microseconds):
| Field | Source |
|---|---|
capture_us | Live adapter execution (when used) |
analyze_us | Semantic walk |
convert_us | PIR rewrite |
restore_us | Target adapter restore |
total_us | Sum of measured phases |
Never fabricated — if a phase did not run, the optional field is omitted / zero as documented by the CLI JSON schema.
Typical findings
SAFE— tuple/set → JS array; BigInt promotion pathLOSSY— integer outside[−2^53+1, 2^53−1]without BigInt preferenceUNSUPPORTED— functions, host objects without encoders
API surface (library)
#![allow(unused)] fn main() { use parallax_migrate::migrate_document; use parallax_core::{ConversionPolicy, RuntimeKind}; let (pir_out, report) = migrate_document( RuntimeKind::Python, RuntimeKind::JavaScript, &pir_in, &ConversionPolicy::default(), )?; }
CLI users should prefer plx migrate — it wires capture and restore around this function.
Snapshots (.plx)
Deterministic JSON documents with integrity hashing. Implemented in parallax-snapshot.
Format
| Field | Description |
|---|---|
magic | Must be PARALLAX_PLX |
format_version | SNAPSHOT_FORMAT_VERSION (1) |
id | UUID |
created_at | UTC timestamp |
runtime | Origin / target runtime kind |
label | Optional |
state | ExecutionState shell (capabilities, heap JSON, metadata) |
pir | Full PirDocument |
content_hash | SHA-256 hex of canonical {format_version, runtime, state, pir} |
Loaders reject bad magic, unsupported versions, invalid PIR, and hash mismatches (InvalidSnapshot).
CLI
# Capture
plx snapshot examples/demo.py -o demo.plx --label demo
# Inspect
plx inspect demo.plx
plx inspect demo.plx --json
# Restore into a runtime
plx restore demo.plx --target javascript
During migrate:
plx migrate examples/demo.py --to javascript --snapshot migrated.plx -o migrated.js
What is stored
For the supported binding-capture path, the important payload is pir.bindings. Stack frames, instruction pointers, and async state are typically empty — capabilities say so.
Integrity model
Snapshots are not cryptographic signatures. The content hash detects accidental corruption and casual edits. Treat them like build artifacts: transfer over trusted channels if the binding data is sensitive.
Worker protocol
Versioned NDJSON over stdin/stdout. Implemented in parallax-protocol (PROTOCOL_VERSION = 1).
Envelope
Every line is one JSON object:
{
"v": 1,
"id": "<uuid>",
"op": "execute",
"ok": true,
"payload": { },
"error": null
}
| Field | Role |
|---|---|
v | Protocol version — mismatch → ProtocolViolation |
id | Correlation id (request/response) |
op | Operation name |
ok | Present on responses |
payload | Op-specific JSON |
error | { code, message, diagnostic? } on failure |
Operations
op | Direction | Purpose |
|---|---|---|
hello | req/resp | Negotiate version; report host/adapter versions |
execute | req/resp | Run source; optional capture list → PIR bindings |
restore | req/resp | Materialize PIR bindings in a fresh context |
ping | req/resp | Liveness |
shutdown | req/resp | Worker exits |
Execute request (abbrev.)
{
"source": "state = {'a': 1}",
"filename": "demo.py",
"capture": ["state"],
"limits": { "timeout": 30000, "max_output_bytes": 1048576 }
}
limits.timeout is milliseconds (serde of ExecutionLimits).
Execute response (abbrev.)
{
"stdout": "",
"stderr": "",
"duration_us": 1234,
"bindings": { "state": { "t": "map", "entries": [] } },
"exception": null,
"success": true
}
Worker locations
Workers are embedded in the adapter crates and materialized under the system temp directory at runtime:
| Runtime | Embedded source | Temp file |
|---|---|---|
| Python | adapters/python/worker.py | %TEMP%/parallax-workers/python_worker.py |
| JavaScript | adapters/js/worker.js | %TEMP%/parallax-workers/js_worker.js |
Timeouts
The core wraps each request in tokio::time::timeout. On expiry the worker process is killed and the caller receives ExecutionTimeout.
Versioning
Parallax uses several version numbers that can advance independently. Bumping the product version does not automatically imply a PIR or protocol break, and vice versa.
Inspect live values:
plx version
plx version --format json
Constants live in parallax-core (version.rs):
| Surface | Constant | Role |
|---|---|---|
| Parallax (product) | PARALLAX_VERSION | SemVer from workspace Cargo.toml (0.1.x today). CLI, crates, and release tags. |
| PIR schema | PIR_SCHEMA_VERSION | Language-neutral IR document schema. Loaders reject unsupported schema numbers. |
| Worker protocol | PROTOCOL_VERSION | NDJSON envelope version between host adapters and Python/JS workers. |
| Snapshot format | SNAPSHOT_FORMAT_VERSION | .plx container fields / hashing contract. |
| Adapter interface | ADAPTER_INTERFACE_VERSION | Host-facing adapter metadata / registration contract. |
| UES format | UES_FORMAT_VERSION | Universal Execution State wire format (execution, not values). |
| PCIR schema | PCIR_SCHEMA_VERSION | Continuation IR schema for supported control regions. |
| PUIR schema | PUIR_SCHEMA_VERSION | Program / project IR used by Transmute and Mirror. |
| Mirror link format | MIRROR_LINK_FORMAT_VERSION | .parallax-link/ metadata layout. |
As of 0.1.0, these integer surfaces are at 1 unless noted otherwise.
Compatibility expectations
- Product SemVer (
CHANGELOG.md): user-facing CLI and library behavior for the Parallax release line. - Integer schema/protocol/format versions: treat a bump as a potential breaking change for that surface. Readers should reject unknown or mismatched versions rather than guessing.
- Adapters: workers and host crates must agree on
PROTOCOL_VERSION. Capability matrices may grow without a protocol bump when messages stay compatible; incompatible message shapes require a protocol bump. - Snapshots:
plx inspect/ restore validate magic,format_version, PIR, and content hash. Older writers are not guaranteed to load in newer readers until migration rules are documented.
When to bump what
| Change | Bump |
|---|---|
| CLI flag, migrate policy default, crate API for users | Product SemVer (per SemVer once published; pre-1.0 may move faster) |
| PIR node shapes or document required fields | PIR_SCHEMA_VERSION |
| NDJSON request/response envelope or required fields | PROTOCOL_VERSION |
.plx top-level fields or hash canonicalization | SNAPSHOT_FORMAT_VERSION |
RuntimeAdapter method/metadata contract across crates | ADAPTER_INTERFACE_VERSION |
| UES document fields / envelope | UES_FORMAT_VERSION |
| PCIR op set or program schema | PCIR_SCHEMA_VERSION |
| PUIR item / program schema | PUIR_SCHEMA_VERSION |
.parallax-link/ layout | MIRROR_LINK_FORMAT_VERSION |
Record product-facing changes in the root CHANGELOG.md. Call out schema/protocol/format bumps explicitly in the same release notes.
Related chapters
Security & limits
Vulnerability reporting, supported versions, and a concise threat-model summary for operators: SECURITY.md in the repository root.
Implemented primarily in parallax-security and enforced by adapters / the runtime manager.
Threat model (0.1)
Parallax assumes developer-trusted guest code on a local or CI machine. It is not a hardened multi-tenant sandbox.
What exists today:
- Subprocess isolation for Python / JS
- Wall-clock timeouts
- Output / message size limits in
ExecutionLimits - WASM fuel budgets via wasmtime
- Bounded concurrent workers
- Explicit capability tokens recorded in state metadata
What does not exist yet:
- seccomp / Seatbelt / Windows job objects
- Network namespace isolation
- Filesystem jails
- Cryptographic attestation of snapshots
SandboxPolicy
| Field | Default | Notes |
|---|---|---|
limits.timeout | 30s | Wall clock |
limits.max_output_bytes | 1 MiB | Stdio capture budget |
limits.max_message_bytes | 16 MiB | Protocol message ceiling |
limits.max_memory_bytes | 256 MiB | Soft hint where supported |
limits.max_fuel | 10_000_000 | WASM |
allow_network | false | Policy flag (not fully enforced in MVP workers) |
allow_fs_read | true | Guests can read files the OS user can read |
allow_fs_write | false | Policy flag |
max_concurrent_workers | 4 | Manager hard limit |
SandboxPolicy::strict() tightens timeouts and memory for experimentation.
Error codes worth knowing
| Code | Meaning |
|---|---|
CapabilityViolation | Requested feature not available |
ResourceLimitExceeded | Concurrency / size / fuel |
ExecutionTimeout | Deadline exceeded |
AdapterCrashed | Worker died unexpectedly |
InvalidSnapshot | Tamper / schema failure |
Handling untrusted input
If you must evaluate untrusted code:
- Use
strict()limits and short timeouts - Run inside an external container / VM
- Do not pass secrets into guest globals
- Treat
.plxfiles as untrusted data — validate, but do not assume secrecy
Supply chain
CI runs cargo deny / advisory checks when configured (see repository workflows). Pin toolchain via rust-toolchain / Actions dtolnay/rust-toolchain.
Performance
Parallax optimizes for correct, measurable migration, not micro-benchmark theater.
How to measure
# End-to-end averages (real samples)
plx bench --iterations 20 --json
# Single migration with phase breakdown
plx migrate examples/demo.py --to javascript --json
JSON includes per-phase microseconds. Use those — do not invent numbers for blog posts.
Cost model (qualitative)
| Phase | Dominant cost |
|---|---|
| Capture | Process spawn + interpreter startup + encode |
| Analyze / convert | Usually tiny vs spawn for demo-sized graphs |
| Restore | Process spawn + decode |
| WASM execute | In-process; fuel accounting overhead |
On warm disks, demo migrations are typically dominated by worker spawn (tens of milliseconds), not PIR walks (tens of microseconds).
Guidance
- Prefer long-lived workers in future versions if you need lower latency (not in 0.1)
- Keep captured graphs small — migrate data, not whole heaps
- Use
--pir-inputoffline fixtures when benchmarking pure analyze/convert - Release builds (
cargo build --release) matter for CLI overhead
Benchmarks directory
See benchmarks/README.md. Criterion crates can be added later; the supported user-facing tool is plx bench.
Adapters overview
An adapter implements RuntimeAdapter in parallax-runtime:
probe— host readinessexecute— run aProgramSourcerestore— materialize aPirDocumentcapabilities/metadata— declarations for CLI and snapshots
Atlas (Phase IX) adds a second adapter surface for project migration: language/framework/build/test/ORM/deploy detectors and planners via parallax-adapter-sdk + parallax-atlas. Runtime adapters and Atlas adapters are complementary — see Atlas architecture.
Registration
The CLI registers adapters at startup:
parallax_adapter_python::register_lenient
parallax_adapter_js::register_lenient
parallax_adapter_wasm::register_lenient
parallax_connectors::register_all_lenient # 60+ language scaffolds
Lenient registration means a missing host binary still registers an adapter that probes UNAVAILABLE — plx doctor stays informative.
Scaffold connectors (Go, Java, Ruby, C#, …) register with honest Unsupported execute/restore until a real worker exists. Browse them with plx connectors. See Language connectors.
Program sources
| Variant | Use |
|---|---|
File | Path on disk |
Inline | Source text + filename hint |
CaptureBindings | Source + explicit capture names (used internally by migrate/snapshot) |
Bytes | Raw WASM module bytes |
Chapters
- Atlas architecture — modular adapter orchestration
- Adapter SDK
- Language connectors — full catalog
- Python
- JavaScript
- WebAssembly
Atlas adapter index
Parallax Atlas ships 120+ built-in adapters across languages, frameworks, tooling, and deployment surfaces. Maturity is honest: stable / beta / experimental / scaffold / parse-only.
Browse live inventory:
plx adapters # grouped by kind
plx adapters --json # machine-readable
plx adapters info <id> # manifest + capabilities
plx analyze <path> # detect stack for a project
Adapter kinds
| Kind | Examples | CLI group |
|---|---|---|
| Source / target language | TypeScript, Python, Rust, Go | LANGUAGE ADAPTERS / TARGET ADAPTERS |
| Framework | Express, FastAPI, NestJS, Axum, Quarkus | FRAMEWORK ADAPTERS |
| Web frontend | React, Vue, Svelte, Angular | WEB FRONTEND |
| Build system | npm, Cargo, pnpm, uv, Poetry, sbt | BUILD ADAPTERS |
| Test framework | Jest, Vitest, pytest, cargo test | TEST ADAPTERS |
| Database / ORM | PostgreSQL, Prisma, SQLAlchemy, SeaORM | DATABASE / ORM ADAPTERS |
| Deployment / CI | Docker, Fly.io, GitHub Actions, Lambda | DEPLOYMENT |
| Runtime | Node, CPython, WASM | RUNTIME |
| CLI framework | clap, commander, click, typer | CLI FRAMEWORKS |
| Validation / serialization | Zod, Pydantic, Serde | VALIDATION / SERIALIZATION |
| Formatter | Prettier, rustfmt, Black, Ruff, Biome | FORMATTERS |
| Linter | ESLint, Clippy, Ruff, mypy, golangci-lint | LINTERS |
| Codegen | OpenAPI/Swagger, Protobuf, GraphQL Codegen | CODEGEN |
| Desktop GUI | Tauri, Electron, Wails | DESKTOP GUI |
| Pair profile | TypeScript→Rust, Python→Rust | PAIR PROFILES |
Stack demo fixtures
Under examples/stacks/:
| Fixture | Stack signal |
|---|---|
nest-prisma/ | NestJS + Prisma + Vitest + ESLint/Prettier |
fastapi-sqlalchemy/ | FastAPI + SQLAlchemy + Ruff + mypy |
tauri-desktop/ | Tauri desktop shell + TypeScript |
plx analyze examples/stacks/nest-prisma --to rust
plx analyze examples/stacks/fastapi-sqlalchemy --to rust
plx analyze examples/stacks/tauri-desktop
Dependency mappings
Transmute uses DependencyMapDb for npm / PyPI / Maven / Go → multi-target equivalences:
plx mappings express
plx mappings prisma
plx mappings @tauri-apps/api
See Dependencies for confidence scoring and manual-review rules.
Related chapters
- Overview — runtime vs Atlas adapters
- Atlas architecture
- Frameworks
- Language connectors — 60+ runtime identities (separate catalog)
Atlas adapter architecture
Parallax Atlas makes language, framework, build, test, database, and deployment support a modular adapter problem — not a core rewrite.
Parallax Core / Transmute / Mirror
│
├── parallax-adapter-sdk formal contracts
└── parallax-atlas registry, detection, stack planning
└── built-in adapters (languages, frameworks, …)
Principles
- Core orchestrates — migration planning consumes normalized IR (
PUIR,ProjectGraph) produced or planned via adapters. - Capabilities are explicit — never assume an adapter supports a construct because it claims a language.
- Composition over hardcoding — TypeScript + NestJS + Prisma + Jest + Docker stack as cooperating adapters with ownership scopes.
- Honest maturity —
stable/beta/experimental/parse_only/target_only/scaffold. - Conflicts are visible — when two adapters of the same kind match, Atlas selects by priority and reports the resolution.
Crates
| Crate | Role |
|---|---|
parallax-adapter-sdk | ParallaxAdapter trait, manifests, capabilities, detection types |
parallax-atlas | AdapterRegistry, built-ins, analyze_stack, compatibility, lockfile |
parallax-connectors | Language identity catalog (roles, host tools) — complementary to Atlas |
parallax-transmute | Actual TS→Rust (etc.) migration execution |
Detection → plan
DISCOVER files/manifests
→ CLASSIFY project kind
→ DETECT adapters
→ RESOLVE conflicts (priority)
→ SUGGEST target stack (--to)
→ ESTIMATE coverage (from maturity + pair tier)
CLI: plx analyze . --to rust
Lockfile
parallax.lock records adapter ids/versions for reproducible migrations (plx analyze . --write-lock).
What Atlas does not claim yet
Scaffold adapters detect ecosystems (Java, Spring, Rails, …) but do not fully migrate them. Tier-1 execution remains TypeScript/JavaScript → Rust (Express→Axum pack) with expanding mappings.
Tooling adapters
Atlas detects formatters, linters, codegen inputs, and desktop GUI shells. These adapters inform stack analysis and dependency mapping — they do not auto-run external tools today.
Formatters (AdapterKind::Formatter)
| Adapter | Detection signal |
|---|---|
| Prettier | prettier dep, .prettierrc, prettier.config.js |
| Biome | @biomejs/biome, biome.json |
| rustfmt | Cargo.toml, rustfmt.toml |
| Black | [tool.black] in pyproject |
| Ruff format | [tool.ruff] + format section |
| gofmt | go.mod present |
| dart format | pubspec.yaml, analysis_options.yaml |
Linters (AdapterKind::Linter)
| Adapter | Detection signal |
|---|---|
| ESLint | eslint dep, eslint.config.js, .eslintrc.* |
| Clippy | Rust project (Cargo.toml) |
| Ruff lint | [tool.ruff.lint] |
| Pylint | pylint dep |
| golangci-lint | .golangci.yml |
| RuboCop | rubocop gem, .rubocop.yml |
| mypy | [tool.mypy], mypy.ini |
Codegen (AdapterKind::Codegen)
| Adapter | Detection signal |
|---|---|
| OpenAPI / Swagger | openapi.yaml, swagger.json, FastAPI, @nestjs/swagger |
| Protocol Buffers | *.proto, prost, tonic, protobuf deps |
| GraphQL Codegen | @graphql-codegen/*, codegen.yml |
| OpenAPI Generator | openapitools.json |
Mappings example: @nestjs/swagger → utoipa (Axum OpenAPI) with honest confidence.
Desktop GUI (AdapterKind::DesktopGui)
| Adapter | Detection signal |
|---|---|
| Tauri | @tauri-apps/api, src-tauri/tauri.conf.json |
| Electron | electron dep, electron-builder.yml |
| Wails | wails.json, Go + frontend bundle |
Try the fixture: plx analyze examples/stacks/tauri-desktop.
CLI grouping
plx adapters lists these under FORMATTERS, LINTERS, CODEGEN, and DESKTOP GUI — see Adapter index.
Adapter SDK
Crate: parallax-adapter-sdk
Base trait
#![allow(unused)] fn main() { trait ParallaxAdapter { fn manifest(&self) -> AdapterManifest; fn detect(&self, context: &ProjectContext) -> DetectionResult; fn capabilities(&self) -> AdapterCapabilities; } }
Specialized markers: SourceLanguageAdapter, TargetLanguageAdapter, FrameworkAdapter, DependencyAdapter, BuildSystemAdapter, TestFrameworkAdapter, DatabaseAdapter, ConfigurationAdapter, DeploymentAdapter, VerificationAdapter.
Manifest
Every adapter exposes AdapterManifest:
id— stable (parallax.typescript.source)version— independently versioned with the product today; package distribution lateradapter_type— source-language, framework, orm, …languages/ecosystemsmaturity/conformance(Bronze / Silver / Gold)priority— conflict resolutionowns— semantic nodes this adapter transformspermissions— capability sandbox for third-party adapterssdk_version—ADAPTER_SDK_VERSION
Capabilities
Machine-readable flags (FULL / PARTIAL / UNSUPPORTED), e.g. TypeScript source:
parsing...................FULL
types.....................FULL
decorators................PARTIAL
dynamic_eval..............UNSUPPORTED
Detection
ProjectContext carries root, relative files, manifests, package names, language mix, and CLI hints (to).
DetectionResult includes confidence, evidence, and optional owns_nodes.
Developing an adapter
- Read examples/custom-adapter
- Implement
ParallaxAdapter(+ specialized trait) - Register via
AdapterRegistry::registeror ship under.parallax/adapters/(discovery planned) - Aim for Bronze → Silver → Gold conformance (conformance)
Scaffold command (plx adapter new) is stubbed; use the example tree as the template today.
Source-language adapters
Source adapters normalize into shared semantic structures (functions, types, modules, control flow, async, …) without leaking language-specific ASTs into the planner.
| Language | Maturity | Notes |
|---|---|---|
| TypeScript / JavaScript | stable | Transmute frontend via TS compiler API |
| Python | beta | Expanding |
| Go, Java, Kotlin, C#, Ruby, PHP | experimental | Detection + connectors |
| Swift, Dart, Lua | scaffold | Identity / detect |
| C / C++ | parse_only | No claim of migration |
See also Language connectors.
Target-language adapters
Targets consume PUIR + ProjectGraph + MigrationPlan (+ style profile) and own:
- syntax emission
- module / file layout
- error & async conventions
- package manifests
- preferred formatter
| Language | Maturity |
|---|---|
| Rust | stable (Tier-1 packs) |
| Go | beta |
| Python, TypeScript | experimental |
| Java, Kotlin, C#, Ruby, Swift, Dart | scaffold |
Style profiles (idiomatic / minimal / …) via plx migrate --target-style and future --style.
Framework adapters
Frameworks are first-class Atlas adapters (AdapterKind::Framework).
Built-ins (detection)
| Adapter | Maturity | Notes |
|---|---|---|
| Express | stable | Pack: → Axum |
| FastAPI | stable | Pack path → Axum |
| Axum | stable | Target-side |
| NestJS, Fastify, Flask, Django, Gin, Chi, Hono, Koa, Fiber, Echo, Rocket, Litestar | beta | Detection + mapping hints |
| Spring Boot, ASP.NET, Rails, Laravel, Next.js, Ktor, Vapor, Sanic, Phoenix, Sinatra | experimental | Detect only |
| Quarkus, Micronaut, Symfony, Slim, Beego, Buffalo | experimental | JVM/PHP/Go detection |
Web frontends (AdapterKind::WebFrontend)
Compose with backend frameworks (e.g. Express + React). Detection only today:
| Adapter | Maturity |
|---|---|
| React, Vue, Svelte, Solid, Angular | experimental |
Preferred mappings
Express → Rust: Axum | Go: Chi | Python: FastAPI
FastAPI → Rust: Axum | Go: Chi | TypeScript: Fastify
Scores come from dependency knowledge (plx mappings) and stack suggestion (plx analyze --to / plx explain-stack).
Contract (intent)
Framework adapters should eventually:
- detect presence
- extract routes / middleware / services
- map to a target framework via a
MigrationPack
Today, Express→Axum remains the implemented Transmute pack; other frameworks contribute detection and planning honesty.
Dependency mappings
Atlas / Transmute share DependencyMapDb (parallax-transmute).
plx mappings
plx mappings axios
plx mappings --json
Mappings are capability-aware candidates, not rename tables:
- confidence
- API similarity
- feature overlap
- async model
- maturity notes
Example:
npm:axios → crates.io:reqwest (92%)
npm:express → crates.io:axum (90%), actix-web (85%)
npm:hono → crates.io:axum (85%)
npm:drizzle-orm → sqlx / sea-orm
pypi:litestar → axum
pypi:pydantic → serde + validator
npm:commander → clap
Deploy/CI hints: Fly.io (fly.toml), Railway, Netlify, GitLab CI, CircleCI, AWS Lambda (serverless/SAM/Pulumi hints).
ORM/DB additions: Drizzle, SeaORM, GORM, Eloquent, DynamoDB (detection + mapping candidates where known).
Multi-candidate selection prefers the highest confidence equivalent unless the user overrides (--framework, config — expanding).
Testing adapters
Test-framework adapters detect runners (Jest, Vitest, Mocha, pytest, unittest, cargo test / Criterion, Go testing, JUnit, Kotest, NUnit, XCTest, Dart test, RSpec, PHPUnit, …).
Build-system adapters also cover pnpm, Yarn, Bun, uv, Poetry, CMake, and Meson (manifest/lockfile detection).
Assertion IR (direction)
Future emission uses language-independent AssertionIR (Equal, Throws, Snapshot, …). Vitest/Jest → cargo test is the Tier-1 path used by weather-api.
Mocking
jest.mock, unittest.mock, Mockito, etc. often require manual review when no safe equivalent exists — Atlas reports maturity honestly rather than inventing mocks.
Adapter security
Third-party adapters must not get unrestricted host access.
Permissions (AdapterPermissions)
| Flag | Meaning |
|---|---|
read_project | Read source tree |
write_output | Write generated files |
execute_build | Run build/test tools |
network | Network I/O |
read_environment | Read env vars |
Built-ins use a fuller permission set. External adapters default to minimal (read_project only) once package loading lands.
Isolation goals
- Crash isolation (
ADAPTER_FAILUREwithout killing the migration) - Timeouts and memory budgets for untrusted adapters
- Deterministic hooks only
Telemetry from plx adapters report is local-only by default.
Adapter conformance
Levels:
Bronze
- Parses / detects project
- Emits valid IR or structured detection
- Basic fixtures pass
Silver
- Target compiles for supported packs
- Tests migrate where claimed
- Dependency mapping works
Gold
- Behavioral verification
- Edge-case suite
- Incremental sync where applicable
- High fixture coverage
plx adapters health exposes a heuristic score from maturity + conformance medals. Full fixture-driven scoring expands with parallax-adapter-testkit.
Publishing adapters
Today: adapters ship built-in with Parallax.
Planned distribution:
.parallax/adapters/ project-local
~/.parallax/adapters/ user-installed
package registries versioned adapter crates / packs
Lockfiles
parallax.lock pins adapter versions for reproducible migrations.
plx analyze . --write-lock
plx adapters update --check
Breaking mapping changes between adapter versions must surface as review-required diagnostics (not silent semantic drift).
Checklist before publishing
- Valid
adapter.toml/ manifest - Capability flags complete
- Bronze fixtures green
- Permissions minimized
- Determinism verified
- Document maturity honestly
Language connectors
Parallax catalogs dozens of languages as first-class connectors — not only the four production runtimes.
plx connectors
plx connectors --pairs
plx connectors go
plx connectors --maturity production
plx connectors --family scripting --json
What a connector is
| Role | Meaning |
|---|---|
| Runtime | RuntimeAdapter registered with plx runtimes / doctor |
| Value migrate | PIR capture/restore across runtimes |
| Transmute source | Project analysis → PUIR |
| Transmute target | Codegen backend |
Production / experimental execute today
| Connector | Execute | Value migrate | Notes |
|---|---|---|---|
| python | YES | YES | NDJSON worker |
| javascript | YES | YES | NDJSON worker |
| typescript | via JS | — | Analyze via tsc API |
| wasm | YES | NO | wasmtime in-process |
| ruby | YES (experimental) | PARTIAL | NDJSON worker |
| php | YES (experimental) | PARTIAL | NDJSON worker (when php on PATH) |
| go | EXPERIMENTAL | NO | NDJSON worker via go run |
plx run examples/demo.rb --runtime ruby --capture state
plx run examples/demo.go --runtime go
plx run examples/demo.php --runtime php --capture state
Maturity (honest)
| Level | Meaning |
|---|---|
| production | Real worker/engine + tests (Python, JavaScript, WASM; TypeScript analyze) |
| experimental | Partial path (Ruby/PHP/Go workers; Rust Transmute target; reverse sync gated) |
| scaffold | Identity registered; host probed; execute/restore return Unsupported |
| planned | Catalogued for roadmap; same scaffold behavior |
Scaffold connectors exist so every serious language has a stable id, extension map, pair matrix row, and contribution hook — not so Parallax pretends to migrate COBOL today.
Families covered
Systems (C, C++, Rust, Go, Zig, …), managed VM (Java, Kotlin, C#, Dart, …), scripting (Ruby, PHP, Perl, Lua, …), functional (Haskell, OCaml, Elixir, Erlang, Clojure, …), mobile (Swift, Objective-C), data science (R, Julia), shell, SQL/GraphQL, smart contracts (Solidity, Move, Cairo), HDL, and more.
See plx connectors for the live table (60+ entries).
Pair highlights
typescript → rust tier1
python → rust tier2
typescript → go tier2
java → rust scaffold
csharp → rust scaffold
solidity → rust scaffold
Full list: plx connectors --pairs.
Contributing a real adapter
- Pick a scaffold id from the catalog (
plx connectors <id>). - Add
adapters/<id>/worker speaking the NDJSON protocol or an in-process engine. - Raise maturity only when execute/capture/restore (as claimed) have tests.
- Follow Adapters overview — never claim
YESfor unsupported ops.
Dedicated crates remain for production: parallax-adapter-python, parallax-adapter-js, parallax-adapter-wasm. The catalog lives in parallax-connectors.
Python adapter
Crate: parallax-adapter-python
Worker: adapters/python/worker.py
Host discovery
Order:
pythonpython3py%LOCALAPPDATA%\Programs\Python\*\python.exe%ProgramFiles%\Python\*\python.exe(and x86)
Candidates that fail python -c "import sys; print(...)" or look like the Windows Store stub are skipped.
Execution model
- Subprocess:
python worker.py - Guest code runs via
compile+execinto a dedicated globals dict - Stdout/stderr of the guest are captured separately from the NDJSON control channel
- Named bindings are encoded with the PIR tagged JSON shapes
Supported value subset (encode)
| Python | PIR |
|---|---|
None | null |
bool | bool |
int | int (decimal string) |
float | float |
str | string |
bytes | bytes |
list | list |
tuple | tuple |
set | set |
dict | map |
| callables | function |
| other | unsupported |
Restore
PIR → Python values for the supported subset. bigint becomes int. Functions / unsupported nodes raise RESTORE_FAILURE.
Limitations
- No true local-frame capture beyond post-exec globals
- No continuation / async migration
- Guest
printis captured; it does not break the protocol channel
JavaScript adapter
Crate: parallax-adapter-js
Worker: adapters/js/worker.js
Host discovery
Order:
nodenodejs%ProgramFiles%\nodejs\node.exe
Execution model
- Subprocess:
node worker.js - Guest code runs in
vm.Script/vm.createContext - Capture works for top-level
let/const/varby appending a final expression that reads names from script scope console.log/error/warnare redirected into captured stdout/stderr buffers
Supported value subset (encode)
| JavaScript | PIR |
|---|---|
null / undefined | null |
boolean | bool |
safe integer number | int |
other number | float |
bigint | bigint |
string | string |
Buffer / Uint8Array | bytes |
Array | list |
Set | set |
plain object / Map | map |
function | function |
| other | unsupported |
Restore
- PIR
intwithin the safe integer range →number - Larger ints /
bigint→ JSBigInt mapwith string keys → plain objectlist/tuple/set→Array(set semantics not reified asSettoday)
Limitations
- No DOM / browser engine — Node.js only in 0.1
- No async migration
- Module
import/ ESM loader hooks are not provided inside the vm context
WebAssembly adapter
Crate: parallax-adapter-wasm
Engine: wasmtime (in-process)
What works
- Load
.wasmbytes or.wattext (wasmtimewatfeature) - Instantiate with fuel enabled
- Call a zero-argument exported function (default name:
run) - Return numeric results as JSON in
ExecutionResult.value
plx run examples/hello.wat --entry run
# runtime: wasm success: true value: [42]
What does not work
| Feature | Status |
|---|---|
| Binding capture | Unsupported |
| PIR restore | Unsupported (UnsupportedValue) |
| Cross-runtime migrate | Unsupported |
| Host imports / WASI | Not wired in 0.1 |
| Multi-arg entrypoints | Rejected with a clear error |
Limits
Fuel comes from SandboxPolicy.limits.max_fuel / request limits (default 10M). Traps surface as ExecutionFailure.
Why include WASM now?
To prove the adapter interface and fuel-limited execution path. State migration remains a Python/JS concern until a deliberate WASM value ABI exists.
Contributing
Thanks for helping build Parallax.
Canonical short guide (setup commands, DCO note, PR norms):
CONTRIBUTING.md in the repository root.
Also see the Code of Conduct, Security policy, and Privacy policy.
Development setup
git clone https://github.com/parallax-runtime/parallax.git
cd parallax
cargo build --workspace
cargo test --workspace
cargo run -p parallax-cli --bin plx -- doctor
Prerequisites: Rust 1.75+, Node.js 18+ (JS adapter), Python 3.10+ (Python adapter).
Docs site
cd docs
mdbook serve --open
# http://localhost:3000
Build static site:
mdbook build
# output: docs/book/
Requires mdBook (cargo install mdbook).
Project norms
- No fake capabilities — return
Unsupported/ structured errors - Prefer small, focused PRs
- Use conventional commits:
feat:,fix:,docs:,chore:,test:,ci: - Keep README and docs synchronized with real CLI behavior
- Add tests for migration / PIR / snapshot critical paths
- Update CHANGELOG.md for user-visible changes
- Bump the correct versioning surface when you break PIR, protocol, snapshots, or adapter contracts
Adapter conformance
See root CONTRIBUTING.md and Adapters overview. In short: honest capabilities, versioned protocol, structured errors, lenient probe/registration, tests for paths you touch.
Code layout
See Architecture. Adapters live under crates/parallax-adapter-* with worker scripts in adapters/.
Checks before opening a PR
cargo fmt --all
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspace
cd docs && mdbook build
CI runs these on Linux, Windows, and macOS (plus docs + audit jobs).
License
Contributions are accepted under the Apache-2.0 license. No CLA is required at this time; see the root contributing guide for the lightweight DCO note.
Changelog
User-facing changes are recorded in the repository root:
The project follows Keep a Changelog and Semantic Versioning for the Parallax product line. Independently versioned surfaces (PIR schema, protocol, snapshot format, adapter interface) are described in Versioning.
Snapshot of 0.1.0
Initial public workspace. Highlights:
- Rust workspace: core, PIR, protocol, security, diagnostics, snapshot, migrate, runtime, adapters, CLI
- Python and JavaScript NDJSON workers with execute / capture / restore
- WASM execution via wasmtime (zero-arg exports, fuel); no binding migration
plx migratePython ↔ JavaScript with semantic-loss analysis.plxsnapshots with content hashingplx doctor,runtimes,capabilities,bench,--json- mdBook site and GitHub Actions CI / Pages / release scaffolding
Limitations: Limitations.
Privacy
Parallax is a local CLI/runtime. The project privacy policy lives in the repository root:
Highlights for operators:
- No telemetry or analytics by default in this workspace
- Source, PIR, and
.plxsnapshots remain under your control - Snapshots may contain sensitive bindings — treat them like unencrypted artifacts
- Guest programs you run can still use the network/filesystem as your OS user allows
Security reporting: SECURITY.md · Security & limits.