# synapse-cc
**Unified compiler toolchain for Plexus backends**
`synapse-cc` (synapse compiler collection) orchestrates the complete pipeline from backend schema discovery to compiled, ready-to-use client libraries.
## Features
- π§ **Unified Interface**: Single command to generate clients from any backend
- π **Smart Tool Discovery**: Finds local development builds or installed versions
- β‘ **Fast**: Incremental caching skips regeneration when schemas haven't changed (IR cache at `~/.cache/plexus-codegen/synapse/ir/`, per-file code cache via service-codegen)
- π― **Multi-Language**: TypeScript and Rust today (`rust` requires a service-codegen built with `--features rust`); `python` parses but has no generator yet
- π οΈ **Complete Pipeline**: schema β IR β generated code β installed deps β compiled artifact β smoke tests
- π **Credential lifecycle**: Per-backend defaults store shared with `synapse` β set, inspect, and rotate JWTs / cookies / headers via `synapse-cc _self <backend> β¦`. Values held as reference URIs (`literal:`, `env://`, `file://`, `keychain://`) so secrets live in the OS keychain or env vars, not plaintext. JWT decoding flags expired tokens at a glance.
## Installation
### From Source
```bash
cd synapse-cc
cabal build
cabal install
```
### Dependencies
`synapse-cc` requires these tools to be available:
- **synapse** - Schema discovery and IR generation
- **service-codegen** β₯ 0.5.0 - Code generation from IR. Earlier releases write a
parameter the server injects (`pdSource`: auth, header, derived, β¦) into the
generated methods as an optional `unknown` argument; synapse-cc says so when
it finds one (SYNAPSE-CC-2)
The tool will automatically find them if they're:
- In your `$PATH`
- In local development directories (`../synapse`, `../service-codegen`)
- In `~/.plexus/bin/`
**The generator is `service-codegen`** (0.5.1 on; NAMING-10 β a Plexus backend
is a *service*). synapse-cc 0.3.6 looks for it under that name first β on
`$PATH`, then `../service-codegen/target/β¦`, `~/.plexus/bin`, `~/.cargo/bin` β
and only then under its old name, saying the old name is deprecated. Its path
is `--service-codegen PATH` (the old flag still works for one release, hidden,
with a note). `synapse.lock` is written with `serviceCodegen`, the generator's
JSON is read as `serviceCodegenVersion`, a code cache's toolchain as
`tvServiceCodegen`; each old key is still read. NAMING-18 removes the old
names; `scripts/naming-gate.sh` (the `naming` test suite) fails on a new one.
synapse-cc 0.3.6 builds against `plexus-synapse` 3.14.3 up to 3.17.
## Usage
The CLI is subcommand-shaped:
```bash
synapse-cc init [BACKEND] # scaffold synapse.config.json
synapse-cc build # build every target in synapse.config.json
synapse-cc build TARGET BACKEND [OPTIONS] # one-off build, no config file
synapse-cc watch BACKEND [PLUGIN...] # rebuild on schema changes
synapse-cc wait [BACKEND] # block until backend(s) reachable
synapse-cc _self BACKEND VERB ... # credentials store (shared with synapse)
```
### Quickstart (config-driven)
A service scaffolded by `axon new` ships a `synapse.config.json` pre-pointed
at itself β `synapse-cc build` works unedited from the service directory
(server must be running). Real run:
```
$ synapse-cc build
==> Discovering tools...
[+] Found all required tools
[i] Building "client" β src/lib/plexus
==> Reading schema...
[+] Schema ready (2 plugins)
==> Generating code...
[+] Code generated (11 files)
==> Installing dependencies...
[+] Dependencies installed
==> Building...
[+] Build passed
==> Running tests...
[+] Tests passed
[+] client β src/lib/plexus/
```
Without a config file, `synapse-cc init <backend>` scaffolds one; with the
backend argument omitted it is **inferred** β from a co-located Plexus
service crate, then from the local registry β and the inference is
announced. There is no silent default backend:
```
$ synapse-cc init
[!] missing BACKEND argument β no co-located Plexus service crate found and
no backend discoverable on the registry at 127.0.0.1:4444.
Run: synapse-cc init <backend>
```
### Quickstart (one-off, no config)
```bash
synapse-cc build typescript substrate # TS client β ./generated/
synapse-cc build rust substrate -o ./client # Rust client, ends in cargo build
```
The backend is resolved **by name via the registry** (endpoint from
`PLEXUS_REGISTRY_URL`, default `ws://127.0.0.1:4444`; `-H`/`-P` set the
fallback). A real `rust` run:
```
$ synapse-cc build rust zechodev -o ./rust-client
==> Discovering tools...
[+] Found all required tools
==> Resolving zechodev via registry at ws://127.0.0.1:4444...
==> Reading schema...
[+] Schema ready (2 plugins)
==> Generating code...
[+] Code generated (5 files)
==> Building...
[+] Build passed
==> Running tests...
[+] Tests passed
[+] Client β ./rust-client/
```
`-t rust` ends in a real `cargo build` of the generated crate (Z2H-7) β the
artifact compiles or the command fails.
### Options (build / watch)
```
-o, --output DIR Output directory (default: ./generated)
--transport ws|browser ws (Node.js, default) or browser (native WebSocket,
for Tauri/WebView)
--no-install Skip dependency installation
--no-build Skip compilation step
--no-tests Skip running smoke tests
--cache-dir DIR Cache directory (default: ~/.cache/plexus-codegen)
--force Force regeneration (ignore cache)
--debug Enable debug logging
--synapse PATH synapse binary path (overrides discovery)
--service-codegen PATH service-codegen binary path (overrides discovery)
-t, --token JWT JWT auth token (see Credentials below)
--token-file PATH Read JWT from file
-H, --host HOST Registry/discovery host (default: 127.0.0.1)
-P, --port PORT Registry/discovery port (default: 4444)
```
### Rust target prerequisite
`build rust` requires a service-codegen binary compiled with the `rust` feature
(`cargo build --release --features all` in the service-codegen repo). A
typescript-only binary fails with:
```
[!] Error: service-codegen failed (exit code 1)
Error: Rust codegen not enabled. Rebuild with --features rust
```
Point `--service-codegen` at a rust-enabled build if the discovered one is not.
### Credentials & Headers
synapse-cc shares a **single defaults store** with synapse at `~/.plexus/<backend>/defaults.json`. Any JWT, cookie, or header you set via either CLI is immediately visible to the other. The file holds credential-reference URIs (`literal:`, `env://`, `file://`, `keychain://`), resolved at request time.
```bash
# Inspect what's stored + decode any JWTs + flag expired tokens
synapse-cc _self <backend> show
# Store a JWT (auto-wrapped as literal:)
synapse-cc _self <backend> set cookie access_token "eyJ..."
# Store a reference to an env var (preferred in CI)
synapse-cc _self <backend> set cookie access_token "env://MY_JWT"
# Import a JWT file
synapse-cc _self <backend> import-token ~/Downloads/jwt.txt
# Clear all defaults for a backend
synapse-cc _self <backend> clear --yes
```
The `_self` subcommand tree is identical to `synapse _self <backend> β¦` β they share the same library implementation. See synapse's README for the full verb list, URI scheme reference, and legacy `~/.plexus/tokens/<backend>` migration behavior.
Per-invocation overrides: `--token <jwt>`, `SYNAPSE_TOKEN` env, `--token-file <path>`, `--cookie KEY=VALUE`, `--header KEY=VALUE` β all flow through the same priority chain as synapse, with CLI values winning over stored defaults per key.
## Architecture
```
synapse-cc (Haskell)
β
βββ synapse (Haskell) β IR (JSON)
β
βββ service-codegen (Rust) β Generated Code
β
βββ Language Tools (npm, tsc, etc.) β Compiled Artifact
```
## Development
### Project Structure
```
synapse-cc/
βββ synapse-cc.cabal # Cabal project file
βββ app/
β βββ Main.hs # Entry point, subcommand dispatch
βββ src/
βββ SynapseCC/
βββ Types.hs # Core types, Options, synapse.config.json types
βββ CLI.hs # Subcommand + option parsing
βββ Config.hs # synapse.config.json load/init
βββ Detect.hs # Project detection for `init` inference
βββ Discover.hs # Tool discovery (synapse, service-codegen)
βββ RegistryResolve.hs # Backend-name resolution via the registry
βββ Pipeline.hs # Pipeline orchestration
βββ Process.hs # Subprocess helpers
βββ Cache.hs # Incremental IR caching
βββ Merge.hs # Three-way merge bookkeeping
βββ Dependency.hs # Plugin dependency resolution
βββ Language.hs # Per-language install/build/test steps
βββ Auth.hs # Token priority chain (wraps Synapse.Self)
βββ Watch.hs # watch subcommand
βββ Wait.hs # wait subcommand
βββ Logging.hs # Pretty output
```
### Building
```bash
cabal build
```
### Running from source
```bash
cabal run synapse-cc -- build typescript substrate --debug
```
### Testing
```bash
# Scaffold and start a throwaway backend (any free port)
axon new mysvc --port 4452 && cd mysvc && cargo run &
# Generate, compile, and smoke-test a client against it β the scaffold's
# synapse.config.json is already pointed at ws://127.0.0.1:4452
synapse-cc build
```
## Status
Shipped: tool discovery, pipeline orchestration, config-driven multi-target
builds (`synapse.config.json`), incremental IR + code caching with three-way
merge, dependency installation + compile + smoke-test steps (bun/npm for
TypeScript, cargo for Rust), watch mode, `wait`, registry-based backend
resolution (`PLEXUS_REGISTRY_URL`), shared credentials store (`_self`).
Not shipped: a `python` generator (the target parses; service-codegen has no
emitter), remote/distributed caching.
## Contributing
See [PLAN.md](./PLAN.md) for detailed implementation plan and design decisions.
## License
MIT