Shopify’s custom distribution binds an app to one store, or to one Shopify Plus organization. So a vendor who sells to many independent merchants, and wants each of them on a private, dedicated app, ends up operating one app per store: dozens of them, then hundreds. I designed and built DeploTeka to make that operable. It provisions, releases and hands over a dedicated app for each client store, and at any moment it can say which runs finished, which can safely resume and which stores have real evidence of an install. It runs in production as a hosted service, with my own Shopify measurement product, Fixel Pixel, as customer zero: a fleet of about 100 stores.
The task: why one app per store is hard
DeploTeka is for Shopify app vendors: a technical founder with a Shopify-connected product, or an agency with repeatable app or integration code, serving merchants who have nothing to do with each other. Each merchant gets its own custom-distribution app. Sharing one custom app across unrelated merchants is exactly what the product is built to avoid.
Per store, the work is always the same. Create the app. Select custom distribution. Take custody of the credentials and deliver them to the vendor’s backend. Release the vendor’s version. Send the merchant an install link, wait for approval, then find out whether the right store really ended up with the right installation and version. Done by hand, it is the same checklist for every client, and every step can fail in its own way. At a hundred stores the bottleneck is no longer the product. It is operations.
The obvious answer is a script that loops over the stores and calls the Shopify CLI. It fails on three questions:
- Did the run finish? Provisioning is a chain: create the app, select custom distribution, read back the credentials, apply branding, deploy the template, create the signed store-specific install agreement. A script that dies at step four leaves a half-built app behind and no record of where it stopped.
- Is it safe to resume? Starting again from the top after step four is not a retry. It is a second, conflicting run against a live store.
- Is the store actually installed? The script sees “install link created” and reports success. The merchant has not approved anything yet.
DeploTeka exists to answer those three questions with evidence.
What I built
DeploTeka is a tenant-scoped control plane in four layers: tenant-aware contracts and policies in the domain; orchestration and use cases in the application layer; PostgreSQL persistence, an encrypted vault and the Shopify adapters in infrastructure; Fastify routes and server-rendered views over HTTP. It works through the official Shopify APIs and CLI. The vendor’s code stays the vendor’s: DeploTeka reads the app configuration it needs without cloning, building or running the vendor’s source on its own servers.
Eight pieces carry the weight.
A tenant boundary on every key
Every database key carries the organization it belongs to, and every repository method requires a tenant id. A job id on its own authorizes nothing: knowing the id of someone else’s run does not let you read it or touch it. Isolation lives in the data layer, so a new screen or endpoint cannot forget it.
A secrets boundary at the API
The browser never receives Partner or Dashboard cookies, CSRF tokens, app client secrets, offline access tokens or raw connector responses, and none of them belong in logs or ordinary status payloads. App credentials are sealed in an encrypted vault. Install links are treated as secrets too. In the web app only an owner, admin or operator can reveal one, each reveal writes an audit event, and on the token-delivery path a fresh link is minted on every reveal instead of being stored.
Idempotent, resumable provisioning
A run is claimed durably, with an idempotency key, before the first Shopify mutation. The database holds the organization-plus-key pair unique, so the same request cannot start a second run. The run then executes persisted steps, each with its own status, evidence and audit event. If a step needs action outside DeploTeka (from an operator, the merchant or a deploy tool on the vendor’s own machine), the run pauses on a named manual action and later resumes in the same run and the same idempotency scope. After a crash it continues from its last persisted step. A provider effect whose outcome is unknown stays unknown and is reconciled, never retried blindly.
Versioned releases per store
The app DeploTeka generates for each store is a versioned asset of the vendor’s tenant, built from a template. The control plane deploys it but is not coupled to its code. In the next release, now in review, handoff mode adds a gate: the merchant’s install link is held back until the released version of that store’s app can be read back from Shopify. That gate came from a real run, where a link minted before any version existed was unusable while the run still reported “ready”.
Credential delivery, measured by the outcome
Credentials go from the vault to the vendor’s backend, never through the browser. Every delivery ends as applied, replayed or unknown, and unknown is neither a success nor a failure: it is reconciled through the status path. Delivery proves transport, not that the credential works. So in DeploTeka, activation means the first successful Shopify Admin API call made with the delivered credential.
I set that definition after the first live credential delivery, on my own store on 28 August 2026. Every internal signal was green and the delivery had completed, yet the receiving side still could not make a single Admin API call until a person told it which scopes had been granted. A green pipeline was not a solved problem. A working API call is.
Roles that keep secrets away from status
Four roles. Owner: billing, connector credentials, members, destructive actions. Admin: templates, clients, stores, provisioning, and members other than owners. Operator: clients, stores, provisioning, and revealing an install link. Viewer: read-only status and audit history, with no secret-bearing links. A viewer can follow a run from start to finish without ever being able to reveal the link it produced.
Install evidence, not install links
A store DeploTeka provisions counts as installed only when Shopify auth has produced a valid offline session for the exact store and app client id, and tenant, store and app ownership agree in the database. The design also requires the app’s required extensions to be read back as ready. In the released version that check is still a placeholder; the next release, now in review, makes it fail closed: without positive proof it answers “unknown”, and the store stays in provisioning instead of showing as installed. A verifier that cannot reach a store also returns “unknown”, with an explicit reason: no false negative, no quiet promotion to installed. A store DeploTeka provisions is never marked installed without evidence.
Fencing for stale workers
Each run is held under a durable lease with an owner, an expiry and a generation number that only ever goes up. When a lease runs out and another worker takes the run over, the old worker is fenced: its generation is stale, so it can no longer advance the run. Every external effect is guarded before and after the call, and an effect that outlives its lease is recorded as unknown, not as success. Scheduling runs through single-writer lanes. The fencing is covered by 99 focused tests, including two workers competing for the same run. It is deliberately not an exactly-once promise: the design makes uncertainty explicit instead of pretending it away.
How it runs today
I run DeploTeka’s live operations myself, with Fixel Pixel, my Shopify measurement product, as the reference integration.
- July 2026. I migrated Fixel Pixel’s store fleet into DeploTeka’s production service. It now holds about 100 Shopify stores.
- August 2026. Stale-worker fencing finished, covered by 99 focused tests, followed by single-writer scheduler lanes.
- 28 August 2026. First live credential delivery, on my own store.
- 7 and 8 September 2026. Production releases, with a release rehearsed across two stores of my own fleet on 8 September.
Every production release, and every action on a live store or a credential, needs my authorization. The next release, now in review, adds the fail-closed extension check and the install-link gate. It ships once review is complete and I sign it off.
Engineering decisions worth stealing
None of these are specific to Shopify. They apply to any system that runs long, multi-step jobs against APIs you do not control.
- Claim before you touch. Write the run and its idempotency key to your own database before the first call to someone else’s API. A unique constraint on tenant plus key turns “did we already start this?” into a database guarantee.
- Persist steps, not just jobs. A job marked “running” tells you nothing after a crash. A run made of persisted steps, each with a status and evidence, tells you where to resume, and lets a person step in on a named action without breaking the chain.
- Fence with a generation, not a timeout. Leases expire and slow workers wake up. A monotonic generation checked before and after every external effect stops an old worker from overwriting its successor.
- Make unknown a first-class state. Networks lose answers. Three outcomes (done, failed, and unknown with a reason) keep a lost answer from being rounded to green or retried into a duplicate. When a check cannot prove something, fail closed to unknown.
- Define done by the customer’s outcome. Activation is the first successful Admin API call with the delivered credential, not the last green step in your pipeline. By the same logic, never hand out a link to something that does not exist yet.
- Write down what backs each claim. DeploTeka’s product contract tags every statement about its behaviour by evidence: code, test, dated production record, official platform source or accepted decision. That keeps a passing test from being quoted as proof of production.
What’s next
Three pieces come next: positive proof that a store’s required extensions are ready, so that check can answer “yes” instead of “unknown”; a release across the whole fleet, with a per-store record of exactly what shipped where; and bringing the first outside app vendors through the full journey. None has completed it yet.
If you need something similar
DeploTeka is the kind of system I like building: many tenants, a platform you do not control, long multi-step runs, secrets, and a hard requirement to know what actually happened. The same design carries over to provisioning pipelines, integration platforms, back-office automation and any job runner where a half-finished run costs money.
I can build this kind of platform for you, or the piece you are missing: idempotent provisioning, a resumable job runner, a credential-delivery flow or a tenant boundary that holds. See how I approach API integrations and automation, or describe your task on Task for Daniel. If you want to write the brief first, the API integration brief lists what to settle before code, including idempotency keys.