No direct vendor SDKs โ route everything through the Aspire stack
Context
Before this rule, individual Aspire apps imported vendor SDKs directly: anthropic, openai, AWS SDKs, etc. This created:
- Credential sprawl โ each app had its own vendor API keys
- Vendor lock-in โ apps were coupled to specific vendor SDK shapes
- No central spend visibility โ vendor bills hit Aspire from N angles
- No central audit โ couldn't tell which agent/app made which call
The rule fixes this for the 3 most-used vendor categories: AI, storage, payments.
Detail
Decision
| Category | Direct vendor SDK? | Use instead |
|---|---|---|
| LLM / vision / OCR | โ NEVER | Aspire LLM Gateway at llm.aspiredigital.group/v1 (OpenAI-compat) |
| Object storage | โ NEVER | MinIO per-app via S3 SDK (works as S3-compat) |
| Marketplace payments | โ NEVER | Stripe Connect Express via Aspire patterns |
| GitHub | โ NEVER | Self-hosted GitLab at gitlab.dssc.co.th |
| Email transactional | โ SendGrid SDK OK | SendGrid is the org-standard; just use it directly |
| Twilio | โ Twilio SDK OK | Per-app, paid; central registry tracks deployments |
Why these specifically
Banned categories all have:
- High switching cost if vendor changes (Aspire wants to swap upstream painlessly)
- Centralized audit/spend value (gateway tracks all LLM spend, MinIO tracks all uploads, Stripe Connect tracks all merchant payments)
- Operational risk if mixed (per-app provider keys vs central admin)
Allowed categories have:
- Single-vendor by necessity (SendGrid for email = SendGrid, no point abstracting)
- Direct API simplicity (Twilio's send-SMS isn't worth wrapping in a "comms gateway")
What this prevents
| Previous pattern | New pattern |
|---|---|
import Anthropic from '@anthropic-ai/sdk' in alby-studio | fetch('https://llm.aspiredigital.group/v1/chat/completions', ...) |
import { OpenAI } from 'openai' in zinga-pms | Same โ gateway |
import { S3Client } from '@aws-sdk/client-s3' pointing to AWS | Same SDK pointing to MinIO https://s3.<app>.aspiredigital... |
Direct cloud-first.ai SaaS OCR API | KO worker-ocr through gateway |
Aspire-LLM-Gateway exclusion zones (per KNOWLEDGE_OS_SPEC ยง5.3)
The following endpoints were previously in design but have been REMOVED from the engine code (2026-05-12 cleanup, engine commit ced417b):
- Direct
llm.cloud-first.ai/v1 - Direct
saas.cloud-first.ai/api/v1/ocr(SaaSOCRClient class deleted) - Direct
saas.cloud-first.ai/api/v1/invoice - Direct Anthropic SDK (
anthropic.com/v1) - Direct OpenAI SDK
- Subprocess CLI calls (
codex,gemini,claudeCLIs)
If a future need arises (e.g., audit-grade OCR with calibrated confidence): add a new gateway alias, don't reintroduce direct client code.
Exception process
If a new vendor genuinely warrants direct SDK access, the operator must:
- Document why the abstraction layer doesn't apply
- Add the vendor + decision to this page
- Get reviewed in a
/specsession
So far, no exceptions have been granted.
Constraints we accepted
- Slightly more verbose code (fetch + JSON marshaling vs SDK methods) for some calls.
- New developers must know about the gateway/MinIO/Connect pattern before writing code.
Revisit trigger
- A regulated tenant requires per-app vendor SDK isolation for compliance reasons.
- A vendor offers SDK-only features (e.g., streaming responses) that the gateway can't proxy efficiently โ would need gateway feature work, not SDK exception.
Actions
- [x] LLM SDK direct usage removed from KO worker-ocr (engine commit
ced417b, 2026-05-12) - [x] All new Aspire apps default to gateway / MinIO / Connect patterns
- [ ] Audit existing zinga-* apps for any leftover direct Anthropic / OpenAI imports (graphify could surface these)
Related
- aspire-llm-gateway-only-egress โ the LLM-specific version of this rule
- stripe-connect-marketplace-default โ the payments-specific version
- minio-storage-per-app โ the storage-specific version
- gitlab-self-hosted-not-github โ the version control version
๐ Relationships
graph LR
no_direct_vendor_sdk["no-direct-vendor-sdk"]:::self
no_direct_vendor_sdk --> aspire_llm_gateway_only_egress["aspire-llm-gateway-only-egress"]
no_direct_vendor_sdk --> stripe_connect_marketplace_default["stripe-connect-marketplace-default"]
no_direct_vendor_sdk --> minio_storage_per_app["minio-storage-per-app"]
no_direct_vendor_sdk --> gitlab_self_hosted_not_github["gitlab-self-hosted-not-github"]
classDef self fill:#715EE3,color:#fff,stroke:#291F50;