Jadpo is experimental and not ready for production. These docs follow the compiler's main branch as of 30 Oct 2026. What's built · Install 0.1.0
jadpo
Reference: Tooling

Tooling

Declared deployables

On this page

An app can be built into several programs, for example a Worker that serves its public API and a server that runs its back office. application { deployables: { } } in app.jadpo names each one, what it serves, what it runs and what it is built for:

example
application {    apis: {        main: {            prefix: /        }        backstage: {            prefix: /backstage        }    }    connections: {        cache: {            driver: Driver.redis        }    }    rate_limits: {        store: cache    }    deployables: {        decline rate_limits.counted_in_memory: "One instance of `web` is enough until Redis's driver comes"        web: {            serves: [main]            runs: [Trigger.subscribers]            target: Target.wasm        }        back_office: {            serves: [backstage]            runs: [Trigger.schedules]            target: Target.native        }    }}
text
$ jadpo build  worker    build/target/web/wrangler.json  module    build/target/web/web.wasm  size      8.1 MB, 2.9 MB gzipped  build     b_1c5e09d2a7f3  serves    main  runs      subscribers  calls     pdf 2 at once in each instance  binary    build/target/back_office/back_office  build     b_e20fc10f5474  serves    backstage  runs      schedules  commit    8d113e6  settings  DATABASE_URL✓ built  web and back_office · wasm and native in 61.3 s→ jadpo migrate apply  they start only once their database has every migration

serves:, the APIs it answers

serves: lists APIs by name: those apis: declares, or main, and webhooks once a connection receives webhooks. A deployable answers only those APIs' routes, each API's openapi.json among them, and a webhook only when it serves webhooks. Every deployable answers its health checks. Two deployables may serve one API.

runs:, the background work it runs

runs: lists the background work it runs:

  • Trigger.schedules: the app's schedules, each entity's purge, each poll, and a batch run picked up again;
  • Trigger.subscribers: each delivery of an event or an email, after its action commits and from the outbox, and the queues receiving: reads.

An action it serves still writes its events to the outbox, for a deployable that runs them. A delivery in a deployable that doesn't run subscribers, or a batch run in one that doesn't run schedules, waits for one that does. Every deployable's tick deletes what has expired, which any instance may.

target:, what it is built for

target: is Target.native, a server, or Target.wasm, WebAssembly on Cloudflare Workers. A deployable with no target: has none, like the app of an app that declares no deployables, and is built natively (design notes, "A deployable's target when none is declared").

What each reaches

A deployable reaches everything its work can lead to. From the routes of the APIs it serves, the webhooks' replies, the actions its schedules and subscribers run and the batch runs it carries on, through every callable they call, to the connections they use: a store of files, a provider's operation, a package's action, ai:'s model. With them come its polls, the webhooks it receives and the queues receiving: reads. Where it runs subscribers, it also reaches email:'s connection, through which the app's emails and the sign-in email are delivered, unless magic_link: { send: } names an action that sends the sign-in email.

It also reaches what Jadpo runs by name: the action on_first_sign_in: names, where it serves an API; the action magic_link: { send: } names, and every email's templates, where it runs subscribers; and every provider package's decode: and encode:.

What each holds

A deployable's program holds only what it reaches: those callables, the routes of the APIs it serves, its schedules and the subscribers' actions only where it runs them, the connections it reaches with every database, and the settings those read. So it reads no other setting as it starts. A subscriber it doesn't run is still listed, so what it emits waits for the deployable that does.

An app's wording and its stores are kept whole, and so is who signs in. But a sign-in method's signing_key: is kept only where the deployable serves an API that takes that method, and origin: only where it serves one that takes browser sessions. So a Worker that only runs the subscribers starts without either.

Its target, checked

A deployable that declares a target is refused each connection it reaches whose driver that target can't run, and the error names the alternative (deployable.target_unsupported; External services):

Target Refused Use instead
Workers The disk A package over S3's API, such as Cloudflare R2
Workers A service Jadpo runs without host: host:, with where it is hosted
Workers SMTP on port 25 Port 587
Native A Cloudflare queue, which only a Worker receives A package over Cloudflare's HTTP pull API
Native Cloudflare's rate-limiting binding as the store of its rate limits Redis

A deployable built for Workers that serves an API needs a store for its rate limits, rate_limits: { store: } (Rate limits; rate_limits.store_required). Until Redis's driver comes, a Redis store there would leave each instance counting in its own memory. That is a judgement call, which a decline line answers, as web above does (rate_limits.counted_in_memory).

A deployable built for Workers is refused a client_address: source other than cloudflare, at its source: (Client addresses).

A deployable built for Workers that reaches a connection with max_concurrent: is listed under "On Workers" once jadpo check passes, and jadpo build says so on the Worker's calls line. Workers runs as many instances as traffic needs, and each counts the cap on its own (External services design notes).

Nothing left unserved

Once deployables: is declared, every API with routes needs a deployable that serves it, and the app's schedules and subscribers need one that runs them (deployable.unserved). A route with settle: is served only where subscribers run (deployable.settle_unrun).

The build

jadpo build builds every deployable, and jadpo build <deployable> one. Each goes into build/target/<deployable>/ with the documents of the APIs it serves, and its Rust into build/rust/<deployable>/. They are all compiled into a cargo target in your cache folder, shared by every project built for the same host and set of locales, so the runtime crates compile once for them all (design notes, "Where deployables are compiled").

A name the app doesn't declare is cli.build_arguments, with the nearest. A deployable built for Target.wasm is built for Workers (A Workers deployable).

Each binary's stamp names it, and its build id is its own. The id digests the deployable's program, so a change it doesn't reach leaves it alone (Records and manifest). jadpo check writes each deployable's target and build id in the manifest, with no build id while the project has errors. settings lists the variables the deployables read, each once.

Markdown for agents: /docs/declared-deployables.md.Built from Jadpo main at a707efb.