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: External services

External services

Where packages come from

jadpo package add <name> looks for a package in a fixed order and records which step it used, so an agent never improvises where to look:

On this page

jadpo package add <name> looks for a package in a fixed order and records which step it used, so an agent never improvises where to look:

  1. The registry, the official package first.
  2. The provider's published specification (OpenAPI, Smithy, GraphQL), given a URL or found in its known places, imported once.
  3. An importer from the official SDK's type definitions, a Postman collection or a HAR capture.
  4. Written from the provider's documentation by an agent.

It also takes a git repository (jadpo package add github.com/acme/acme-pricing@v2.1.0, below) and another Jadpo app's OpenAPI document (jadpo package add ../orders, see Packages from other Jadpo apps). Each source is read once, when the package is added or updated. Then it is pinned in jadpo.lock with its source and digest.

The registry

The registry is Jadpo's own, and holds Jadpo packages of both kinds.

  • Jadpo source only. A package carries no native code and no install script, and nothing in it runs when it is installed. Its code runs only when started (What package code may do). A malicious package could still name the wrong host or mark an unsafe call idempotent, so hosts, services and every package change appear in the approval report, the summary a person signs off before a release.
  • Checked on upload. The registry repeats every install check. It adds discovery, curated packages marked official, publisher verification and signing. It renders each package's documentation website from its surface, so every package is documented the same way.
  • A few curated packages, Stripe, Twilio and GitHub, imported from their published OpenAPI, live in the Jadpo repository.

An importer

jadpo package import <format> <file> turns a published description into the app's own integration once, offline, in shared/providers/<name>/<version>/:

Format Typical source What it gives
openapi Most REST APIs (Stripe, Twilio, GitHub), and every Jadpo API Operations, types, error responses; from a Jadpo app, exact (its x-jadpo fields)
smithy AWS, one model per service The same, plus AWS's protocols and signing
postman A provider's published Postman collection Operations and example bodies; types inferred from examples
har A browser or proxy capture of real traffic Operations and real responses; values stripped, shapes kept
dts The provider's official TypeScript SDK (.d.ts files) Exact types and SDK method names; no HTTP method or path, so the agent adds those from the docs

openapi reads a local file: jadpo package import openapi ../orders/build/openapi/main.json, with --as Orders.v1 to name the package. "How the OpenAPI importer reads a document", further down this page, says how it reads one.

An agent then fills in what the source leaves out: which calls change something, idempotency, error names, and decoding (OpenAPI often says only integer for Unix seconds). A person reviews that diff. Corrections to a wrong specification live in the package and survive the next import, because the importer merges rather than overwrites, as an OpenAPI Overlay does.

Written by hand, when there is no specification

Many APIs publish only HTML docs. A package is Jadpo source, so for these an agent writes the package files directly from the docs, in exactly the format an importer produces. Nothing else changes: the same compiler checks, the same fakes, the same review. A package needs only the operations the app uses, so it can start with one operation and grow.

Mapping imported types to standard types

An imported description says little about what a field is: OpenAPI's integer may be pence, Unix seconds or a count. A package's public types use the standard types, such as Money and Instant, where one fits (type.standard_exists, Standard library). So an import maps fields to them in six layers, and nothing is guessed silently:

  1. Exact mappings. A description's own format hints map without judgement: OpenAPI's date-time to Instant, date to CalendarDate, email to Email, uri to Url, uuid to Uuid, ipv4 and ipv6 to IpAddress, ISO 4217 and ISO 3166 enumerations to Currency and Country.
  2. Shape and name detection, with the same matcher as type.standard_exists: amount with a sibling currency to Money, lat/lng to Point, an integer named like a time (created, *_at) as a candidate Instant from seconds. The provider's own shape becomes a named decoding at the edge (Stripe's amount: 1050, currency: "gbp" decodes to Money, with a field's wire_parts: under "Decoding"), so the app only ever sees the standard type.
  3. Findings on what was flagged. Every candidate the importer is not certain of is a declinable error on that field, package.standard_candidate, whose next: says what to check, marked (agent): confirm against the docs and swap, or decline with a reason that jadpo approval shows.
  4. One scoped review of the whole package. The import ends with one broad instruction to the agent: go through every imported type the app reaches against the provider's documentation, use the standard type wherever one fits, add a decoding wherever a format is undocumented (seconds, cents, comma-separated lists), and mark which operations change something and which are idempotent. The review covers the operations the app calls and every type they reach, never the whole description (Stripe's has 1,180 types), and grows when the app calls something new. Each imported type starts unreviewed; the agent marks it reviewed when it has gone through it; jadpo check lists reviewed and outstanding types for the operations the app calls, and jadpo approval shows a person any type shipped unreviewed.
  5. Confirmation by real responses. An operation the app calls stays unconfirmed until jadpo package record has checked a sandbox response against it ("Provenance and confirmation", below).
  6. The safety net. The package is Jadpo source, so type.standard_exists runs on it as on any code: a field that looks like a standard type and was not mapped blocks check until it is swapped or declined.

The import's own output lists what it mapped, the findings, the scoped review instruction and the operations still unconfirmed, with --json for agents. Corrections made in review survive the next import, because the importer merges rather than overwrites.

The agent marks a type reviewed by listing it in provider.jadpo's imported:, reviewed: [Charge, Shop], each one of the package's own types, once (package.provider_invalid). The review's scope is worked out from the calls on every check, so it grows when the app calls something new: the package's types each operation the app calls takes and gives reach, through fields, list items, payload variants and refinements. jadpo check, once it passes, lists the types still to review and the calls no recording confirms, counts both in its footer, and gives every call and type in --json as providers; neither stops it. With nothing left a passing check is one line; with something left it reads:

text
$ jadpo checkTo review  Go through each type against the provider's documentation, then add it to  `reviewed:` in `imported:` in its package's `provider.jadpo` (agent)    Tidepay.v2024_06_20  Charge, ChargesGetChargesInput, Shop; 0 of 3 reviewedNot yet confirmed  Record each with `jadpo package record`, which checks a sandbox answer  against its types (agent)    payments.charges.get_charges✓ checked  3 to review · 1 unconfirmed · 9 files

It writes the same to build/report/providers.md and .json, for jadpo approval to show a person what ships unreviewed or unconfirmed. The import ends with the scoped instruction: the types the app's calls reach still to review, or, while it calls none of the package's operations, when the review starts; and the calls not yet confirmed. Importing again keeps reviewed:, less any type the description added to, which the summary lists under "To review again". The choices are in "The scoped review and unconfirmed operations" below.

Provenance and confirmation

Every operation says where it came from, so a reviewer knows how far to trust it:

source: Meaning Also records
PackageSource.imported From a published description (OpenAPI, Smithy, Postman, .d.ts, GraphQL introspection later) provider.jadpo's imported: names the file and its digest
PackageSource.written Written from the provider's docs docs: with the page's URL

An operation is confirmed when a real response has been checked against it. jadpo package record <connection> <operation> calls the provider's sandbox once, then checks the operation's output type against the response, or writes the type if there is none yet. It saves the response, with values replaced by placeholders, as recordings/<resource>.<operation>.json in the package; every decoder must decode every recorded response, on every build of the app's own integration and at publish. An operation imported from a HAR capture is confirmed from the start. record refuses a connection whose environment is live.

Written operations that are not confirmed are allowed in every build. The approval report flags them, and CI requires a passing contract run against the sandbox for each one (see "Tests and fakes").

jadpo package record <connection> <resource>.<operation> calls the sandbox once through the connection, with its credential from .env.local and the operation's input, written as a route's body is, from --input. It checks the answer against the operation's output type and keeps it as the recording: along output_at:, only the members the type declares, each plain text or number replaced by a placeholder naming its built-in type ("{Text}"), and what the package's own encodings and decoders read kept as sent. The surface then marks the operation confirmed, and jadpo test decodes the recording ("Recording errors"):

text
$ jadpo package record couriers bookings.create --input '{"postcode": "N1 9GU"}'✓ Recorded `couriers.bookings.create` from the sandbox    sent    POST https://sandbox.acme-couriers.example/v1/bookings    answer  decodes as Booking    values  2 placeholders, 2 kept as sent    file    shared/providers/acme_couriers/v1/recordings/bookings.create.json✓ operation recorded  AcmeCouriers.v1 bookings.create · confirmed→ jadpo test  decodes the recording with every decoder
json
{  "data": {    "collectAt": "{Instant}",    "id": "{Text}",    "status": "booked",    "window": "PM"  }}

It refuses before anything is sent a connection whose environment is live, or whose credential nothing sets (recording.refused), and keeps nothing from an answer that doesn't decode or a refusal (recording.failed):

text
$ jadpo package record couriers bookings.create --input '{"postcode": "N1 9GU"}'error: The sandbox's answer to `couriers.bookings.create` doesn't decode as `Booking`  reason: Reading it found `variant` on `status`: the operation's types and the provider disagree, so a real call would fault. Nothing was recorded.  next: Correct `Booking` against the answer and the provider's documentation, then run it again (agent)  rule: recording.failed  help: https://jadpo.dev/docs/errors/recording.failed

The choices are in "Recordings" below.

Installing safely

  • Every package is verified at install, wherever it came from: jadpo package add and update regenerate the surface from the package's code and refuse one that differs from its committed surface.jadpo_record.
  • The lock records each package's version, digest and source: registry, git (the repository, the tag and the tag's commit), path (a folder in the same repository, such as another Jadpo app) or link (a live checkout during development, below). A public package can never stand in for a git one. jadpo check and jadpo build read the lock and the committed folders only.
  • Publisher trust. Your own company's packages from git install as any other. A third party's package from git, rather than the registry, is marked unverified in the approval report, and needs a person's acceptance to install. Registry packages carry their verified publisher, and curated ones are marked official.
  • Prose is unverified wherever it comes from, such as /// descriptions and GUIDE.md. Nothing safety-relevant relies on it.

jadpo package add from a folder (../postcodes) or from a git repository at a release tag (github.com/acme/postcodes@v2.1.0): the package is compiled as the app's own integration would be, the surface its code gives has to be its committed surface.jadpo_record byte for byte (install.surface_mismatch), and only then is it copied into packages/<name>/<version>/ and locked with its version, digest and source. update moves one to its newest release and shows what changed for review (Records and manifest). remove takes out one the app no longer uses (install.in_use), and link and unlink are under Developing and publishing a package. The choices are in "How packages are installed and linked" below.

text
$ jadpo package add github.com/acme/postcodes@v2.1.0✓ Added Postcodes.v2 2.1.0    folder   packages/postcodes/v2    source   git github.com/acme/postcodes tag v2.1.0    surface  matches its code✓ package added  Postcodes.v2 2.1.0 · 1 function · 2 types · 1 failure→ jadpo package show postcodes  shows what it offers
text
$ jadpo package update postcodes✓ Updated Postcodes.v2 from 2.0.0 to 2.1.0    folder   packages/postcodes/v2    source   git github.com/acme/postcodes tag v2.1.0    surface  matches its code  Changes since 2.0.0    + type OutwardCode✓ package updated  Postcodes.v2 2.1.0 · 1 added→ jadpo check  checks the app against 2.1.0

Not built yet: the hosted registry; importers other than openapi; adding another Jadpo app's OpenAPI document with jadpo package add; publisher trust; jadpo approval; writing an operation's output type from the sandbox's answer when it has none; and a HAR capture's operations.

Markdown for agents: /docs/where-packages-come-from.md.Built from Jadpo main at a707efb.