---
title: Admit a provider
description: Run the executable provider admission command that verifies a Supported Provider end to end against your deployment and its built image.
---

Provider admission is one command. It verifies a provider module against a deployment and reports a named reason for every check. Use it when you add a Supported Provider, when you change a Dockerfile or the wrapper, and before you trust a deployment with a new Sync Connection. The command replaces the static provider checklist; it is the executable form of that checklist.

The command lives in the Ads Sync Reference Deployment at [`packages/ads-sync/deploy/scripts/provider-admission.ts`](https://github.com/patronage/agentic-marketing-connectors/blob/main/packages/ads-sync/deploy/scripts/provider-admission.ts). It lives there, not in the `patronage` CLI, because every touchpoint it reads is a deployment file: Dockerfiles, `wrangler.jsonc`, and Container classes. You point it at your own deployment tree.

## run the static rung

```bash
pnpm --filter @patronage/ads-sync-deploy provider-admission -- --provider google_ads
```

The static rung needs no credentials and no Docker. It runs nine checks in order and prints one line per check. The reference deployment declares its connector containers in `wrangler.jsonc` and pins the wrapper version in `src/container-pins.ts`, so the default invocation passes for every Supported Provider. Point `--deployment` at your own container-runner deployment to admit a provider there.

| check | what passes |
| --- | --- |
| `catalog` | The provider id is registered in `@patronage/ads-sync/providers`; `supportedProviderDefinitions`, `providerDefinitions`, and `supportedImageVersions` agree with the module; `sourceImage` is digest pinned; the reporting stream requirement covers `streamName`; secret names do not collide with another provider. |
| `dockerfile` | Exactly one Dockerfile in the images directory pins `FROM` to the module `sourceImage`; `AIRBYTE_CONNECTOR_IMAGE` equals that pin; the file copies and runs `airbyte-source-server.py`; `.dockerignore` allowlists the Dockerfile and every `COPY` input. |
| `wrapper` | `airbyte-source-server.py` declares `WRAPPER_VERSION` in the form `ads-sync-wrapper-v<major>.<minor>`, and it equals `--expect-wrapper-version` when you pass one. |
| `wrangler-container` | `wrangler.jsonc` has a `containers[]` entry for the provider: either `image` is the admitted Dockerfile path, or `image` is a registry reference whose last repository segment names the provider (for example `registry.example.com/ads-sync/ads-sync-google-ads:0.3.0`). A registry entry passes with the detail `the running image pin is verified by the live rung through /metadata`; the consumer image digest never equals the upstream connector digest, so the static rung does not compare them. Failure text: `declares no containers[] entry whose image is <Dockerfile or name>`. |
| `wrangler-binding` | `durable_objects.bindings` has an entry for that container class. |
| `wrangler-migration` | A `migrations[]` entry lists the class in `new_sqlite_classes` or `new_classes`. |
| `container-class` | A `.ts` file in the deployment declares `class <name> extends Container` and a module exports it. `*.test.ts` files are not sources for this check. |
| `metadata-expectation` | Both halves of the pin. Wrapper version: every wrapper-version literal in the deployment `.ts` sources (tests excluded) equals the wrapper file version; a deployment with no literal fails. Connector image: the sources either pin the provider's `sourceImage` digest as a literal that equals the package pin, or read `supportedImageVersions` and so derive it from the package. A literal for the same repository with another digest fails with `<file> pins connectorImage <image> but the package pins <expected>`; no literal and no read fails with `no connectorImage pin found: no .ts source under <root> pins <image> or reads supportedImageVersions`. A Qualified Run needs an exact metadata pin. |
| `fixture` | `<fixtures>/<provider>/catalog.json` contains the provider stream with no reporting drift, and `source-config.json` yields a source identity. |

The default deployment is the reference deployment itself. Point the command at another deployment with `--deployment <dir>`; a relative path resolves from `packages/ads-sync/deploy` when you run the command through `pnpm --filter`. The command finds `wrangler.jsonc` at the deployment root or under `worker/`; pass `--wrangler <file>` for another location. It reads Dockerfiles from `<deployment>/images` when that directory exists, otherwise from the reference images; pass `--images <dir>` to override. Fixtures default to the package's `src/test-fixtures/provider-admission`; pass `--fixtures <dir>` for your own.

## run the live rung

```bash
pnpm --filter @patronage/ads-sync-deploy provider-admission -- \
  --provider google_search_console \
  --deployment ../../my-ads-sync-deployment \
  --live
```

`--live` needs Docker. It builds the admitted Dockerfile for `linux/amd64`, starts the image on a local port, and adds four checks:

| check | what passes |
| --- | --- |
| `image-metadata` | `GET /metadata` reports `connectorImage` and `wrapperVersion` that equal the package image and the wrapper file, verified through the same `assertContainerImagePin` the Qualified Run seam uses, with the seam's bounded cold-start retry. |
| `spec` | `GET /spec` succeeds and the connector emits a `SPEC` message. |
| `check` | `POST /check` with the fixture source config succeeds and the connector emits a `CONNECTION_STATUS` message. A `FAILED` status whose message is a config-shape rejection (for example `Config validation error`) fails the check, because the source config never reached the provider. |
| `bounded-read` | `POST /read-artifact` with the fixture config and catalog returns `stdout` and `stderr` inside 8 MiB and 50,000 lines, within a five-minute wall clock. |

With synthetic fixture credentials, `check` reports `CONNECTION_STATUS FAILED` with the provider's authentication error and passes. That proves the pinned connector accepted the config shape and reached the provider. Pass `--source-config <file>` with a real source config to require `SUCCEEDED` and a read that exits `0` with at least one `RECORD` or `STATE` message. Keep that file out of the repository.

Pass `--image <ref>` to admit an image you already built with `build-images.sh` instead of building again. Pass `--json` for a machine-readable report. The exit code is `0` when every check passes and `1` otherwise.

## read a failure

Every failure names the touchpoint and the reason. Three examples from the command's own test suite:

```text
FAIL catalog               no provider module registered for "linkedin_ads" (supported: google_ads, google_search_console, meta_ads)
FAIL dockerfile            no Dockerfile in <images> pins FROM airbyte/source-facebook-marketing:5.2.11@sha256:4d6c… (found: Dockerfile.meta FROM airbyte/source-facebook-marketing:5.2.11@sha256:ffff…; …)
FAIL metadata-expectation  src/runtime.ts expects ads-sync-wrapper-v0.2 but airbyte-source-server.py declares ads-sync-wrapper-v0.3
```

A failed `dockerfile` check blocks the Wrangler and Container checks; they report `blocked` and pass again once the Dockerfile pin is fixed. A failed `image-metadata` check blocks `spec`, `check`, and `bounded-read`, and the command still stops the container.

## add a provider

Admission is the done bar for a new provider. Work through the checks in order:

1. Add the provider module and register it in `@patronage/ads-sync/providers` (`catalog`).
2. Add a Dockerfile in the images directory with a digest-pinned `FROM`, the matching `AIRBYTE_CONNECTOR_IMAGE`, and the wrapper `COPY`; add the file to `.dockerignore` (`dockerfile`, `wrapper`).
3. Declare the container, its Durable Object binding, and a new migration in `wrangler.jsonc`, and add the exported `Container` subclass (`wrangler-*`, `container-class`, `metadata-expectation`).
4. Add `catalog.json` and `source-config.json` under the fixtures directory with synthetic values only (`fixture`).
5. Run the live rung and keep the output as admission evidence.
