Tier 2 overview
Summary
Section titled “Summary”Tier 2 is the same self-hosted stack as
Tier 1, with one difference: the
data-provider is not on your machine. Instead, your api and
worker point at a hosted endpoint.
You still run:
api(owns user integration credentials).worker(consumes BullMQ jobs).frontend-app(the SPA + nginx).- Postgres, Redis, S3-compatible storage.
You don’t run the data-provider container.
You still need your provider API keys
Section titled “You still need your provider API keys”The hosted endpoint
Section titled “The hosted endpoint”The official Scani Cloud data-provider is:
https://api.cloud.scani.xyzSmoke-test it before pointing your api/worker at it:
curl -fsS https://api.cloud.scani.xyz/health# {"status":"ok","timestamp":"…","version":"1.0.0"}A successful 200 with a JSON body means the endpoint is reachable
from your network and the TLS chain validates. You still need an
SCANI_CLOUD_API_KEY to actually call any route — sign in at
cloud.scani.xyz to mint one.
Operators running their own hosted data-provider can swap the URL for their own — the contract is identical.
When Tier 2 makes sense
Section titled “When Tier 2 makes sense”Note what is not on that list: Tier 2 is not a way to avoid managing provider API keys — see above.
What stays on your side
Section titled “What stays on your side”What’s hosted
Section titled “What’s hosted”Those four are the whole list, and they are the whole list because
they are the only adapters packages/clients/cloud-client/src/ has:
cloud-storage.ts, cloud-email-service.ts, cloud-og-client.ts,
and the tokens.search call in
packages/business/domain/src/services/tokens/TokenValidationService.ts.
The hosted data-provider sees only those four kinds of request. User
accounts are not visible to it; user credentials never leave your
api.
What happens without them
Section titled “What happens without them”None of the keyless branches throws at boot, so a stack with no provider keys comes up green on every health check and then returns bad data. What each missing key costs:
How to check, rather than guess
Section titled “How to check, rather than guess”Every one of those five reports its status at boot, and the registry emits one summary line whether or not anything is degraded:
docker compose -f docker-compose.prod.yml logs api worker \ | grep 'provider credentials:'Production logs are JSON (LOG_PRETTY is forced off when
NODE_ENV=production), so the line arrives as a msg field:
{… "mode":"direct","msg":"✅ provider credentials: 5/5 keyed · keyed: coingecko, etherscan, finnhub, openai, solana · degraded: none"}
{… "degraded":["COINGECKO_API_KEY","OPENAI_API_KEY"],"mode":"direct","msg":"⚠️ provider credentials: 3/5 keyed · keyed: etherscan, finnhub, solana · degraded: coingecko [COINGECKO_API_KEY unset → drops to the public rate-limited tier instead of the Pro host]; openai [OPENAI_API_KEY unset → throws on every call, so screenshot and document parsing fail]"}The degraded line also carries a degraded array of just the unset
variable names, which is the cheaper thing to alert on.
The healthy line prints too, so its absence is a signal — a provider quietly dropping out of the keyed set is visible as a change rather than as silence.
The api serves the same record over HTTP:
docker compose -f docker-compose.prod.yml exec api \ curl -fsS http://localhost:3001/health/deep | jq .providerCredentials{ "keyed": ["coingecko", "etherscan", "finnhub", "solana"], "degraded": [ { "provider": "openai", "envVar": "OPENAI_API_KEY", "behaviour": "throws on every call, so screenshot and document parsing fail" } ]}A degraded provider deliberately does not turn /health/deep
red: an unkeyed provider is a configuration choice, not an outage,
and an endpoint that is always red is one nobody reads. The worker
has no HTTP health endpoint — for the worker, the boot line above is
the signal.
How to switch from Tier 1 to Tier 2
Section titled “How to switch from Tier 1 to Tier 2”- Provision (or obtain) a hosted data-provider endpoint. The
official endpoint is
https://api.cloud.scani.xyz; mint an API key at cloud.scani.xyz. (Operators running their own hosted data-provider use their own URL.) - Update
.env:Keep your provider keys where they are.SCANI_CLOUD_URL=https://api.cloud.scani.xyzSCANI_CLOUD_API_KEY=<the issued key> - Comment out (or remove) the
data-providerservice block in yourdocker-compose.prod.yml. - Recreate api + worker:
Terminal window docker compose -f docker-compose.prod.yml up -d api worker - Your sync schedules and history are intact. What changes is where storage, email, OG metadata and token search go.
See Migrating Tier 1 → Tier 2 for the step-by-step, including rolling back if the migration doesn’t work out.
Trust model
Section titled “Trust model”- Your data-provider operator can read every request you send them. That is uploads and downloads through their bucket, the bodies of the emails you send, the URLs you fetch OG metadata for, and the symbols you search. They cannot read your user accounts, balances, or integration credentials — those never leave your api.
- They hold your object storage. Uploaded screenshots and imported statement files live in their bucket, not yours.
- You trust them to maintain availability. If their endpoint is
down, uploads, magic-link email and token search fail (BullMQ will
retry per the retry policy in
packages/business/jobs/src/retry-policies.ts). Pricing and chain syncs are unaffected, because they never went there.