← Paul X Lab · Product 02

Caravan

POC COMPLETE · MVP NEXT

An 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 shift

Today, 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 code

An 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 chooses

01

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.

Example

From 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.