How packages, connections, capabilities and services fit together
On this page
Seven terms name the ways an app reaches past its own code: package, provider, connection, driver, capability, built-in service and service run by Jadpo. This topic says what each one is and where it runs. The topics it links hold the rules.
Most apps use only two of them. A connection in app.jadpo opens a
database or a provider's API, and your actions call through it. You
don't need Docker or Podman for any of this: only a service run by Jadpo
uses a container.
What each one is
| Term | What it is | Declared in | Runs |
|---|---|---|---|
| Package | Jadpo source shared between apps, laid out as shared/ is. A library package holds types, failures and pure functions (Postcodes.uk.parse); a provider package describes one outside API: its operations, types, failures, webhook events and how it is reached (Kinds of package, The package format) |
packages/<name>/<version>/ when installed; the app's own in shared/providers/ |
Compiled into the app and run in its process. A package is never a process, a container or native code of its own |
| Provider | The outside API a provider package describes: Stripe, a courier, another Jadpo app, Gotenberg. The package's provider.jadpo says how it is reached |
Nowhere in the app: it is the other end of the call | Wherever its owner runs it: on the internet (hosts:), at an address a setting gives (host: on the connection), or in a container Jadpo starts (runs:) |
| Connection | One named entry in connections: in app.jadpo: a built-in driver (driver: Driver.x) or a provider package (package: X.vN), with its settings, its credential and the domains whose actions may call it. One connection is one resource: a database, a bucket, one account at a provider (Connecting in app.jadpo) |
app.jadpo, its values read from config.jadpo settings |
Opened by the runtime, inside the app |
| Driver | Native code in Jadpo's runtime that speaks one protocol: PostgreSQL, SMTP, the disk, Cloudflare Queues, OpenAI-compatible model servers; Redis, S3 and OpenID Connect to come. Drivers arrive only with a Jadpo release, never in a package (No presets, no dependencies) | driver: on a connection |
Inside the app, talking to a server the operator runs |
| Capability | A job Jadpo itself does, with one API in app code whatever fills it: database, cache, files, email, search, documents, ai and the rest. One connection fills it: a driver, or a provider package whose provides/<capability>.jadpo maps the job onto its operations (Capabilities) |
Chosen in app.jadpo: email: { connection: mail }, store: sheets on a File field |
Wherever the connection that fills it runs |
| Built-in service | A backend feature Jadpo provides, so the app installs nothing: feature flags, rate limits, files, email, audit, telemetry, search, documents, bot protection (Built-in services). Email, files, search and documents do their outside work through a capability; flags, rate limits and audit keep their state in the app's own stores | Where Built-in services says: flags.jadpo, rate_limit:, type X = EmailMessage, File fields |
Inside the app's runtime. It is a feature, not a process or a container |
| Service run by Jadpo | A provider whose API is a stateless container image, declared by runs: in its package's provider.jadpo (Gotenberg turning HTML into PDFs, ClamAV scanning uploads). Jadpo starts and supervises the container through Docker or Podman and hands its connection the address (Services) |
runs: in the package, named by a connection |
A container beside the app on the same machine |
Two words mean two things each:
- "Service". A built-in service is part of Jadpo's runtime, inside the app's process; a service run by Jadpo is a container beside it. No built-in service starts a container; one can use a service run by Jadpo through its capability, as documents can use Gotenberg.
runs:. In a package'sprovider.jadpoit declares a container; on a deployable inapp.jadpoit lists the background work that deployable runs,runs: [Trigger.schedules](Declared deployables).
How they relate
the app's process: jadpo dev's interpreter, or a native or Workers deployable│├── domain code ── calls ──► library package pure functions, in process│├── domain code ── calls ──► connection: provider package ── HTTPS ──► provider's API│ on the internet (hosts:), at host:,│ or in a container Jadpo runs (runs:)│└── built-in services: flags, rate limits, email, files, search, documents, ... │ outside work goes through a capability └──► capability: email, files, database, documents, ai, ... filled by one connection, either ├── a built-in driver ──────────── its protocol ──► a server the operator runs: │ PostgreSQL, SMTP, S3 └── a provider package's provides/ ── HTTPS ──────► provider's API: Resend, Postmark, Gotenberg
- Code calls a provider's operations only through a connection, from
an action in a domain the connection lists:
couriers.bookings.create(...)(Calling). - Code never names what fills a capability. It writes an
EmailMessagetype or aFilefield, andapp.jadpopicks the connection, so changing provider changesapp.jadpoand its settings, never domain code. - One provider package can be both. Postmark fills
emailthroughprovides/email.jadpo, and the domains its connection lists call its own operations (hosted templates, suppression lists) as any provider's. - Packages fill capabilities and never invent them, never bring a driver and never depend on another package.
- Anything that keeps data is the operator's. PostgreSQL, Redis, an S3 bucket and an SMTP server are reached through a driver at the address a setting gives; Jadpo never runs them. A service run by Jadpo holds no data, and each app instance runs its own copy.
Where each runs
jadpo dev |
jadpo test |
Native deployable | Workers deployable | |
|---|---|---|---|---|
| Library package | In process | In process | Compiled in | Compiled in |
| Provider package's calls | Faked by default, from its recordings or generated answers; operations named in development: { sandbox: } go to the sandbox, never live (Provider calls in development) |
Always faked; a test scripts answers with fakes: (Tests and fakes) |
HTTPS to the live or sandbox host the connection's environment: picks, or to its host: |
The same, through the Worker's fetch |
Database, Driver.postgres |
The address its setting gives, else a disposable PostgreSQL started from the machine's PostgreSQL tools | A copy per test on a disposable PostgreSQL, or on JADPO_TEST_DATABASE_URL's server |
The operator's PostgreSQL | The operator's PostgreSQL, through Hyperdrive |
| Email, by SMTP or a mail package | Caught in the dev inbox | Caught in email.sent |
The SMTP server, or the mail package's API | The same; SMTP on port 587 only |
| Files | The disk driver, in .jadpo/files/ |
An in-memory store | The disk (one server) or S3 | S3, R2 included |
Service run by Jadpo, runs: |
Started as a container by jadpo dev |
Never started: the package's fake answers | A container beside the app: jadpo build writes the configuration that runs it (Compose first), then the deployable supervises it itself |
None: Workers runs no containers, so the connection gives host: where the service is hosted elsewhere |
jadpo check refuses a deployable whose target can't run a connection
it reaches, naming what to use instead: on Workers the disk, a service
run by Jadpo without host:, and SMTP on port 25
(Capabilities, "Targets").
How Docker fits
- An app needs no Docker or Podman to be checked, tested, run in
jadpo devor built. Nothing above but a service run by Jadpo uses a container: the disposable PostgreSQL comes from the machine's own PostgreSQL tools, a native deployable is one binary (A native deployable) and a Workers deployable is a Worker. Neither is an image. - Docker or Podman comes in only for a service run by Jadpo. Jadpo
talks to whichever engine the machine has, through the
Docker-compatible API both serve. It creates each container itself. A
container has no outbound network until its connection grants it, is
never privileged and can't reach the host's disk. A new or changed image
runs only once a person accepts it in the approval report.
jadpo serviceslists, starts and stops each one and shows its logs (Tooling). You never write a Dockerfile or a Compose file, and an app with no such connection never needs an engine (Services). - The operator's infrastructure is their own. They may run PostgreSQL or an SMTP server in Docker, or anywhere else; the app sees only the address its setting gives.
In an app
One app with each kind of connection: a database on the PostgreSQL
driver, mail on the SMTP driver filling the email capability, and the
app's own provider package for a courier, which the shipping domain
calls:
application { connections: { main: { driver: Driver.postgres url: config.database_url } mail: { driver: Driver.smtp host: config.smtp_host credential: { username: config.smtp_username password: config.smtp_password } } couriers: { package: AcmeCouriers.v1 domains: [shipping] credential: config.couriers_api_key environment: config.couriers_environment } } email: { connection: mail from: config.mail_sender }}
use app { couriers }use shipping/failures { SlotGone }use shared/providers/acme_couriers/// Books a collection with the courier, through its connection.action book(postcode: AcmeCouriers.Postcode) fails SlotGone -> AcmeCouriers.Booking { return attempt couriers.bookings.create( AcmeCouriers.CreateBooking { postcode: postcode } ) { slot_taken: SlotGone }}
A service run by Jadpo: Gotenberg's package declares its container, and a
connection to it fills the documents capability. In jadpo dev and a
native deployable, Jadpo starts the container. On Workers the connection
would give host: instead:
// packages/gotenberg/v8/provider.jadpo/// Gotenberg's HTTP API, run by Jadpo as a container beside the app.provider { protocol: Protocol.multipart_http runs: { image: "gotenberg/gotenberg:8@sha256:…" port: 3000 asks: ["fonts.googleapis.com"] }}// app.jadpoapplication { connections: { pdfs: { package: Gotenberg.v8 domains: [billing] outbound: ["fonts.googleapis.com"] } } documents: { connection: pdfs }}
What is built of these
Built: library and provider packages, installed from a folder or git
and locked; connections and the network report; provider calls with
decoding, retries and idempotency keys; fakes in development and tests,
recordings and sandbox calls; webhooks, queues, polls and package
actions; the drivers for PostgreSQL, SMTP, the disk (in jadpo dev and
tests), Cloudflare Queues and OpenAI-compatible servers; the database,
email (SMTP, Resend and other mail packages), receiving,
schedules and ai capabilities; and the built-in services email,
rate limits and the first part of files.
Not built yet: services run by Jadpo (runs:, jadpo services,
and with them every use of Docker or Podman); the Redis and S3 drivers
and a deployable's disk; OpenID Connect sign-in providers and OAuth
grants; the cache, publishing, search, documents, bot_check
and scanning capabilities; the built-in services flags, audit,
telemetry's section, search, documents and bot protection; the hosted
registry; importers other than openapi; and choosing a provider when
deploying. Each page's "What is built" table is the record.
Markdown for agents: /docs/how-integrations-fit-together.md.Built from Jadpo main at a707efb.