← Paul X Lab · Product 02
Caravan
POC COMPLETE · MVP NEXTAn application-definition compiler.
A caravan is a group of services that travel together when that suits them and split apart for different destinations when it doesn't. The shape they take is one yaml line per target. The same source code runs every shape.
Proof of concept complete on real AWS. Python and Rust SDKs at 0.1.1. MVP next: external repos, TypeScript and Go SDKs, cost-optimization surface.
Why this matters.
The shiftToday, choosing between a monolith and a set of microservices is one of the largest architectural commitments a team takes. Senior architects and CTOs gate it because reversing the decision means rewriting code, redoing oncall, and re-wiring deploy. Caravan removes the irreversibility: the same source code compiles to either shape per target, and you can run both at once across environments.
The thesis.
Proven in codeAn application is a graph of components connected through the caravan-rpc SDK at each inter-component seam. Monolith or microservices is a yaml decision, not a code change.
Three orthogonal axes.
What each target chooses01
Packaging
Which services share a process.
Modular monolith or multi-container. Per target, each seam dispatches inproc, container, or lambda.
02
Placement
Where each service runs.
Local docker-compose, cloud long-running, or cloud function. The same source code moves between placements unchanged.
03
Composition
What each resource is bound to.
Local OSS engine, cloud managed service, or existing-cloud-resource binding. Mixing is first-class: local services can talk to real cloud resources in the same run.
One target: names a point in (packaging × placement × composition). A repo declares many: dev, staging, prod, pr-preview, and caravan up --target=<name> flips between them. Same source code everywhere.
What one YAML looks like.
ExampleFrom the invoice-parse test fixture.
name: invoice-parse
default_target: dev-bootstrap
entries:
processing:
path: services/processing
uses: [invoice_queue, invoice_db, invoice_blobs]
seams:
LLMExtraction:
impl: invoice_processing.extraction:GeminiExtractor
resources:
invoice_queue: { type: queue, composition: oss-local }
invoice_db: { type: db.sql, composition: oss-local }
targets:
dev-bootstrap:
runtime: docker-compose
entries: { processing: container }
seams: { LLMExtraction: container }
dev-inproc:
runtime: docker-compose
entries: { processing: container }
# seams omitted, LLMExtraction defaults to inproc What this gets you
One declaration. Many deployments.
Today, the same application lives in several hand-written deployment configurations: one for local dev, one for staging, one for prod, more for preview environments. Each is target-specific, drifts from the others, and rots between releases.
Caravan replaces them with one declaration. Splitting a modular monolith into separate containerized services, or moving a seam to a cloud function, becomes a target change, not a source rewrite.