9F1C · TERMINAL ID CAPK · RID/IDX CHECK-VALUE CONFIG GROUP 01 AID · A0000000031010
Paying.co · Foundry · Provisioning & Config Feed · deep dive

The feed. How parameters get
into the kernel — and stay correct.

Doc 02 sketched the hierarchy; this is the machinery. A versioned manifest describes the desired kernel state; an idempotent service reconciles the reader to it — set, verify, check-value — on first boot and every update alike. Processor-independent, deployment-specific by data, never by code.

status shipped source ⇄ service idempotent reconcile check-value verified
The split · description vs. application

Two concerns kept apart. The Configuration Source says what the reader should hold — a signed, versioned manifest. The Provisioning Service makes it so — the only code that touches kernel set* / retrieve* calls. Decoupling them means the same service applies any manifest, and the same manifest can come from any source.

◆ Configuration Source · data

Versioned Manifest

Desired-state document: terminal data, config groups, AIDs, CAPKs, CRL. Signed & version-stamped. Delivered however the deployment needs — host pull, MDM push, or local profile. Pure data.

apply

◆ Provisioning Service · core

Reconciler

Reads current kernel state, diffs against the manifest, applies only what changed, verifies by check-value. The only component that drives the SDK config surface. Identical on boot and update.

why split A transit kiosk and a retail terminal differ only in manifest content — different floor limits, maybe different AIDs — not in provisioning code. One reconciler, many manifests. And because the manifest is signed, the reader can refuse an untrusted parameter set: tamper-resistance for the config path.
The manifest · desired kernel state, as data

A shape, not a final schema — the point is that every parameter the L2 kernel enforces is declared here, version-stamped, and nothing about a processor appears. Card-brand & cert requirements shape the content; the structure is universal.

terminal-profile.v7.signed.json
// desired kernel state — signed, version-stamped, processor-agnostic
{
  "manifestVersion": "7",
  "profile": "transit-kiosk-mx",
  "terminal": {
    "countryCode": 484,        // MX
    "defaultCurrency": 484,    // MXN
    "language": ["es", "en"],
    "terminalType": "22"
  },
  "configGroups": [
    { "id": 1, "floorLimit": 0,
      "cvmRequiredLimit": 75000,   // minor units
      "contactlessTxnLimit": 200000 }
  ],
  "aids": [
    { "aid": "A0000000031010", "group": 1, // Visa
      "tacDefault": "…", "tacOnline": "…" },
    { "aid": "A0000000041010", "group": 1 }  // Mastercard
  ],
  "capks": [
    { "rid": "A000000003", "index": "95", "checksum": "…" }
  ],
  "crl": [ /* revoked cert entries */ ],
  "signature": "ed25519:…"
}
keys path CAPKs ride in the manifest as references + checksums, loaded via ctls_setCAPK. Secret key material (P2PE / DUKPT) never travels in a config manifest — it's injected separately through device_startRKI against device_setSymmetric_RKI_URL, or loaded as CA certs via device_loadCertCA. Config feed and key injection are separate channels by design.
The reconcile loop · set → verify → check-value

The service doesn't blindly push — it converges current state to desired state and proves it. Same loop whether the reader is blank (first boot) or already provisioned (update). Diff-driven, so an unchanged reader does almost nothing.

1
Read current kernel state
Enumerate what the reader holds now — terminal data, AID list, config groups, CAPK list. The baseline for the diff.
ctls_retrieveTerminalDatactls_retrieveAidListctls_getAllConfigurationGroupsctls_retrieveCAPKList
2
Diff against the manifest
Compute the delta: what to add, what to update, what to remove. Unchanged entries are left alone. A blank reader diffs to "add everything"; an up-to-date reader diffs to "nothing."
desired − current = Δ
3
Apply the delta — in hierarchy order
Push changes top-down (terminal → groups → AIDs → keys), and remove stale entries the manifest dropped. Order matters: AIDs reference groups, so groups land first.
ctls_setTerminalDatactls_setConfigurationGroupctls_setApplicationDatactls_setCAPKemv_setCRLctls_removeApplicationDatactls_removeCAPK
4
Verify — read back & check-value
Re-read the affected entries and compare the kernel's configuration check-value against the manifest's expected value. Provisioning is not "done" until they match. A mismatch fails the apply and rolls forward to retry, never silently proceeds.
emv_getEMVConfigurationCheckValuectls_retrieveApplicationDatactls_retrieveCAPK
Record provisioned version
Stamp the reader as holding manifest v7 with its check-value. Next update compares against this and converges only the difference. The audit trail logs what changed, not card data.
Idempotency · same call, any starting state

Because the loop is diff-driven and verified, applying a manifest is safe to run any number of times. The outcome depends on the gap between current and desired — not on how many times you press go.

ScenarioWhat the diff findsResult
First boot · blank reader Everything in the manifest is missing apply all
Re-run · already current No delta — kernel matches manifest v7 no-op
Update · new floor limit One config group changed; AIDs & keys unchanged apply Δ only
AID retired Manifest dropped an AID the reader still holds remove stale
Partial failure last run Some entries applied, check-value mismatched re-converge
Verify passes Check-value matches expected done
cert-killer The single most skipped step is step 4. A reader that accepted a set* call is not a reader that holds the right config — only the check-value proves that. Skipping verification is how a terminal reaches certification with subtly wrong parameters. Foundry treats an unverified apply as a failed apply.
Design rules the feed holds to

What keeps provisioning trustworthy across deployments and reruns.

Manifest is data, service is code
Deployments differ by manifest content only. One reconciler applies every profile — transit, retail, kiosk.
Diff, don't blast
Converge current → desired. An up-to-date reader is a near no-op; only the delta is written.
Verified or failed
No apply is complete without a matching check-value. Unverified = failed, never "probably fine."
Hierarchy order
Terminal → groups → AIDs → keys. References resolve because parents land before children.
Two channels
Config parameters and secret key injection travel separately. CAPKs in the manifest; DUKPT/P2PE via RKI.
Signed & versioned
Every manifest is stamped and signed. The reader can refuse an untrusted set; updates compare by version.