frutebox-node 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- frutebox/adminheader.py +59 -0
- frutebox/attest.py +43 -0
- frutebox/audiogen.py +364 -0
- frutebox/config.py +367 -0
- frutebox/deltasync.py +211 -0
- frutebox/economics.py +270 -0
- frutebox/engine_guard.py +718 -0
- frutebox/envelope.py +77 -0
- frutebox/envvars.py +167 -0
- frutebox/gpuprices.py +641 -0
- frutebox/gpuproviders.py +4489 -0
- frutebox/gui.py +270 -0
- frutebox/imagegen.py +152 -0
- frutebox/inference.py +624 -0
- frutebox/manifest.py +218 -0
- frutebox/mediaserving.py +603 -0
- frutebox/packaging.py +542 -0
- frutebox/pqctransport.py +660 -0
- frutebox/provider.py +1867 -0
- frutebox/providers.py +942 -0
- frutebox/registry/__init__.py +189 -0
- frutebox/registry/generate.py +311 -0
- frutebox/registry/generate_providers.py +120 -0
- frutebox/registry/models.yaml +1759 -0
- frutebox/registry/providers.yaml +518 -0
- frutebox/registry/registry.gen.json +2043 -0
- frutebox/registry_gen.py +3 -0
- frutebox/runpod_templates.py +143 -0
- frutebox/serving.py +691 -0
- frutebox/signedconfig.py +259 -0
- frutebox/signing.py +208 -0
- frutebox/thinkbudget.py +155 -0
- frutebox/videogen.py +118 -0
- frutebox_node-0.1.0.dist-info/METADATA +100 -0
- frutebox_node-0.1.0.dist-info/RECORD +36 -0
- frutebox_node-0.1.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,518 @@
|
|
|
1
|
+
# 28 §A4 / §A9 — the provider catalog.
|
|
2
|
+
#
|
|
3
|
+
# WHAT THIS FILE IS, AND IS NOT (§A9.4)
|
|
4
|
+
# This is the SCHEMA and the SEED: it defines the shape of a provider row and the defaults a
|
|
5
|
+
# fresh database starts from. It is NOT the live source of truth. Once seeded, the live
|
|
6
|
+
# enable/region/credential state lives in the DATABASE, so adding a provider, turning one on in
|
|
7
|
+
# a region, or rotating a key is a row change — no redeploy, no key copied to N services.
|
|
8
|
+
#
|
|
9
|
+
# PHASE 1 (launch) IS DELIBERATELY TINY (readme LAUNCH PHASING, 2026-09-07):
|
|
10
|
+
# RunPod (US) + ONE EU provider (Verda/DataCrunch). Gateways EU + US only.
|
|
11
|
+
# Everything else in the geo roster is Phase 2 (plan 29) — defined here so the shape is settled,
|
|
12
|
+
# but `enabled: false` so nothing provisions until launch is live and stable.
|
|
13
|
+
#
|
|
14
|
+
# CREDENTIALS ARE NOT IN THIS FILE, EVER. `env` names which variables a cold-start/local-dev
|
|
15
|
+
# fallback would read; the real values live encrypted in the DB under STEVAI_DATA_KEY
|
|
16
|
+
# (readme rule 10 — never a cloud KMS). `providers doctor` reports presence, masked.
|
|
17
|
+
|
|
18
|
+
version: 1
|
|
19
|
+
|
|
20
|
+
# `class` routes work to capacity built for it: an AI job never lands on a CPU-only host, and a
|
|
21
|
+
# plain compute job never overpays on a GPU cloud (§A4).
|
|
22
|
+
# `weights` is not a compute class and never enters the race — it is a model SOURCE
|
|
23
|
+
# (plan 28 §A5 lists Hugging Face in the same credential table as the compute providers),
|
|
24
|
+
# so its token lives in the same encrypted store rather than on a service that never uses it.
|
|
25
|
+
classes: [gpu, compute, tee, weights]
|
|
26
|
+
|
|
27
|
+
# ROSTER TIER 3 (owner ruling 2026-09-24): Alibaba, OVHcloud, Gcore, Cloudflare R2.
|
|
28
|
+
# Z1 was in this tier when it was first recorded and was DROPPED hours later the same day;
|
|
29
|
+
# OBJECT STORAGE STAYS ON CLOUDFLARE R2, which is now the only storage member.
|
|
30
|
+
#
|
|
31
|
+
# RECORDED HERE BECAUSE IT DID NOT EXIST ANYWHERE. A search of the whole repo found no provider
|
|
32
|
+
# roster tiering at all — the geo roster in plan 28 §A6 is organised by continent, and the only
|
|
33
|
+
# "tier 1/2/3" language in the code is `routing.py`'s COMPUTE tiers (tier 1 GPU, tier 2/3 CPU),
|
|
34
|
+
# which is a different axis entirely. So this is a NEW list, not an edit to an existing one, and
|
|
35
|
+
# two things about it are flagged rather than smoothed over:
|
|
36
|
+
#
|
|
37
|
+
# 1. THE NAME COLLIDES. "Tier 3" already means "CPU pool" in `routing.py` and in the planner
|
|
38
|
+
# tests. A reader who meets both will assume they are the same scheme. If this roster tiering
|
|
39
|
+
# is going to persist it wants a name that does not collide — `roster_tier` rather than
|
|
40
|
+
# `tier` — and the owner should say which.
|
|
41
|
+
# 2. IT MIXES CLASSES. Alibaba, OVHcloud and Gcore are compute; Cloudflare R2 is OBJECT
|
|
42
|
+
# STORAGE. This file's `classes` list has no storage class at all, and the merit-order race
|
|
43
|
+
# is class-gated precisely so a job never lands on capacity built for something else. A
|
|
44
|
+
# storage provider in a tier alongside three compute providers cannot be expressed as a row
|
|
45
|
+
# here without either a new class or a separate roster.
|
|
46
|
+
#
|
|
47
|
+
# So it is written down as the owner stated it, and NOT modelled as rows or as a `tier:` field,
|
|
48
|
+
# because modelling it wrong is worse than not modelling it yet. Neither Cloudflare R2 nor
|
|
49
|
+
# OVHcloud has a row, a class or an adapter today.
|
|
50
|
+
roster_tier_3: [alibaba, ovhcloud, gcore, cloudflare_r2]
|
|
51
|
+
|
|
52
|
+
providers:
|
|
53
|
+
# ── PHASE 1 ────────────────────────────────────────────────────────────────────────────
|
|
54
|
+
# ── BEING REPLACED (owner decision 2026-10-16) ───────────────────────────────────────────
|
|
55
|
+
#
|
|
56
|
+
# Verda + Nebius replace RunPod. This row is deliberately UNCHANGED in every field that affects
|
|
57
|
+
# behaviour — `enabled: true`, `phase: 1`, `regions: [us, eu]` — because the cutover is a
|
|
58
|
+
# SEQUENCE and this is step zero of it, not step one. Disabling RunPod before Verda and Nebius
|
|
59
|
+
# have each proven a live boot would remove the only funded capacity there is, which is exactly
|
|
60
|
+
# the mistake the 2026-10-13 EU ruling made and the 10-14 reversal undid.
|
|
61
|
+
#
|
|
62
|
+
# The replacement is PREFERENCE-DRIVEN and needs no field here: `regionmap._DEFAULT` already asks
|
|
63
|
+
# Verda first and Nebius second for EU, and RunPod answers only what they cannot. As they are
|
|
64
|
+
# funded they take the traffic with no code change — the same property that made Verda's
|
|
65
|
+
# preference safe while it was empty.
|
|
66
|
+
#
|
|
67
|
+
# WHAT REMOVES THIS ROW is written down and is not a judgement call:
|
|
68
|
+
# `docs/provider-cutover-verda-nebius.md`, which also names the owner approval that teardown
|
|
69
|
+
# needs. DO NOT disable it here first; the pods currently serving EU media are on this provider.
|
|
70
|
+
- id: runpod
|
|
71
|
+
class: gpu
|
|
72
|
+
status: being_replaced
|
|
73
|
+
enabled: true
|
|
74
|
+
phase: 1
|
|
75
|
+
hq_country: US
|
|
76
|
+
# US ONLY (owner rule, 2026-10-21): "EU GPU work runs ONLY on Verda + Nebius. Never Google
|
|
77
|
+
# GPUs, never RunPod (RunPod pods are terminated and not used in the EU)."
|
|
78
|
+
#
|
|
79
|
+
# THE HISTORY MATTERS, BECAUSE THIS ROW HAS SAID BOTH THINGS. On 2026-10-13 `eu` was removed to
|
|
80
|
+
# stop "EU traffic being served by RunPod"; the owner REVERSED that on 10-14 with a reason —
|
|
81
|
+
# "RunPod must remain a valid EU provider because the other EU systems aren't funded yet" —
|
|
82
|
+
# and at the time it was the only funded EU capacity there was. That reason has now expired
|
|
83
|
+
# rather than been overruled: Verda and Nebius are both funded, both authenticate, and both
|
|
84
|
+
# start instances (an L40S reached RUNNING in 78s on 2026-10-20). So the capacity argument that
|
|
85
|
+
# justified the reversal no longer applies, and the owner has closed it.
|
|
86
|
+
#
|
|
87
|
+
# WHY THE ROW AND NOT ONLY `regionmap`. There are THREE EU candidate sets and they must agree:
|
|
88
|
+
# `regionmap._DEFAULT` (dispatch), `nodewake.warmpool_providers` (pod creation), and
|
|
89
|
+
# `providers.eligible(region="eu")` — which reads THIS field. Leaving `eu` here would have kept
|
|
90
|
+
# RunPod eligible EU capacity while the other two refused it, and a gate has to go where the
|
|
91
|
+
# grant happens. `tests/test_eu_is_verda_and_nebius_only.py` pins all three together.
|
|
92
|
+
#
|
|
93
|
+
# The RESIDENCY lock is unchanged and was never the thing letting RunPod in: it is derived from
|
|
94
|
+
# `hq_country` and has always been Verda (now Verda + Nebius) with no fallback.
|
|
95
|
+
regions: [us]
|
|
96
|
+
api_base: https://rest.runpod.io/v1
|
|
97
|
+
env: [RUNPOD_API_KEY]
|
|
98
|
+
# §A5: account-scoped resource ids are DISCOVERED from the key, never hand-entered.
|
|
99
|
+
derived: [RUNPOD_NETWORK_VOLUME_ID, RUNPOD_VOLUME_DATACENTER]
|
|
100
|
+
capabilities:
|
|
101
|
+
gpu_types: [A40, A100, H100, H200, RTX4090]
|
|
102
|
+
network_volume: true
|
|
103
|
+
|
|
104
|
+
# VERDA (ex-DataCrunch). FIRST CHOICE FOR EU, and as of owner decision 2026-10-16 one of the two
|
|
105
|
+
# providers REPLACING RunPod.
|
|
106
|
+
#
|
|
107
|
+
# THE ID STAYS `datacrunch` AND THAT IS DELIBERATE. The company renamed itself to Verda in
|
|
108
|
+
# Nov 2025, but this id is a primary key: it is the key in `gpuproviders.PROVIDERS`, the key in
|
|
109
|
+
# `gpuprices._FETCHERS`, the `providerId` on every credential row already stored in the database,
|
|
110
|
+
# and the prefix `regionmap.provider_of_node` attributes a running node by. Renaming it would
|
|
111
|
+
# orphan the stored credential and un-attribute every live EU node — a key rename is a migration,
|
|
112
|
+
# not a tidy-up. Operator-facing text says Verda; the id says datacrunch.
|
|
113
|
+
- id: datacrunch
|
|
114
|
+
class: gpu
|
|
115
|
+
status: active
|
|
116
|
+
enabled: true
|
|
117
|
+
phase: 1
|
|
118
|
+
hq_country: FI
|
|
119
|
+
regions: [eu] # the Phase-1 EU-sovereign provider (Verda / DataCrunch)
|
|
120
|
+
# `api.verda.com` since 2026-10-16, following the vendor's OWN SDK default
|
|
121
|
+
# (`verda.VerdaClient.base_url`). The old `api.datacrunch.io` host still answers, so this was
|
|
122
|
+
# never an outage — only a host one vendor decision away from being one.
|
|
123
|
+
api_base: https://api.verda.com/v1
|
|
124
|
+
# BOTH NAMINGS, NEW ONE FIRST — and the adapter reads them in this order.
|
|
125
|
+
#
|
|
126
|
+
# The 2026-09-07 discrepancy (plan 28 §A6 said `DATACRUNCH_API_KEY`; the SUPERVISOR_LOG said
|
|
127
|
+
# client id + secret) is SETTLED: the vendor's own SDK authenticates with an OAuth2
|
|
128
|
+
# client-credentials id and secret, so the log was right and there is no API-key form.
|
|
129
|
+
#
|
|
130
|
+
# The rebrand then added a second question — which name the stored credential is under. That is
|
|
131
|
+
# not knowable from the code, so both are declared and `DataCrunchProvider._creds` resolves
|
|
132
|
+
# each naming WHOLE, taking the first complete pair. Half of a rotated pair and half of a
|
|
133
|
+
# retired one authenticate as neither, and the resulting 401 reads as a revoked key rather
|
|
134
|
+
# than a seeding mistake.
|
|
135
|
+
env: [VERDA_CLIENT_ID, VERDA_CLIENT_SECRET, DATACRUNCH_CLIENT_ID, DATACRUNCH_CLIENT_SECRET]
|
|
136
|
+
# EITHER PAIR IS A COMPLETE CREDENTIAL — see `ProviderRow.env_groups`. Without this the doctor
|
|
137
|
+
# reads `env` as a conjunction and reports a WORKING deployment as unconfigured, missing two
|
|
138
|
+
# names it does not need. New naming first, so that is the one an empty account is told to set.
|
|
139
|
+
env_groups:
|
|
140
|
+
- [VERDA_CLIENT_ID, VERDA_CLIENT_SECRET]
|
|
141
|
+
- [DATACRUNCH_CLIENT_ID, DATACRUNCH_CLIENT_SECRET]
|
|
142
|
+
capabilities:
|
|
143
|
+
gpu_types: [V100, A100, H100]
|
|
144
|
+
network_volume: false
|
|
145
|
+
notes: >-
|
|
146
|
+
Verda (ex-DataCrunch), Finland. EU locations FIN-01/02/03 only — the vendor also offers
|
|
147
|
+
ICE-01, which is in ICELAND (EEA, not EU), and `DataCrunchProvider.create` refuses it.
|
|
148
|
+
`location_code` is REQUIRED on create since 2026-03-23 (absent is HTTP 400, not a default to
|
|
149
|
+
FIN-01). A delete that names no volumes LEAVES THE OS VOLUME BILLING — "by default, no
|
|
150
|
+
storage is selected for deletion" — so `destroy` names the volume ids with
|
|
151
|
+
delete_permanently. A shut-down instance keeps charging, which is why `offline` counts as
|
|
152
|
+
billing-but-not-serving. HDD volumes were decommissioned 2026-09-21: create/resize return
|
|
153
|
+
400, NVMe only.
|
|
154
|
+
MULTI-REGION (measured live 2026-10-18): the account's EU locations are FIN-01, FIN-02 and
|
|
155
|
+
FIN-03, all Finland, so Verda covers the eu-north geography only - ICE-01 is Iceland and is
|
|
156
|
+
refused by the adapter. An image boot REQUIRES at least one SSH key on the account ("SSH keys
|
|
157
|
+
must be provided if image is not an OS volume"), which the spec derivation reads rather than
|
|
158
|
+
assumes.
|
|
159
|
+
|
|
160
|
+
- id: huggingface
|
|
161
|
+
class: weights
|
|
162
|
+
status: active
|
|
163
|
+
enabled: true
|
|
164
|
+
phase: 1
|
|
165
|
+
hq_country: US
|
|
166
|
+
regions: [eu, us]
|
|
167
|
+
api_base: https://huggingface.co
|
|
168
|
+
env: [HF_TOKEN]
|
|
169
|
+
notes: >-
|
|
170
|
+
Model weight source for plan 28 §B (pinned hf_repo + revision). NOT a compute provider —
|
|
171
|
+
never enters the merit-order race. §B is not built yet, so nothing reads this token on the
|
|
172
|
+
request path today; it is stored here so §B finds it and so it is off the gateways.
|
|
173
|
+
|
|
174
|
+
# OWNER RULING 2026-09-22: vast PARTICIPATES. Four things said so — it is in the launch pool
|
|
175
|
+
# (tier 1 of the roster), it has a credential row, it is live-configured, and it is in the
|
|
176
|
+
# `WARMPOOL_PROVIDERS` default — and one field said otherwise. That field was the lie, so that
|
|
177
|
+
# field moved. `phase: 1` moves WITH `enabled: true`: enabling a phase-2 row on its own would
|
|
178
|
+
# have broken the "no phase-2 provider is enabled" invariant and swapped one inconsistency for
|
|
179
|
+
# a newer, less obvious one. `status: backup` STAYS — it describes vast's ROLE, a marketplace
|
|
180
|
+
# stock-out fallback, which is exactly what it is and is not what enabled/phase are for.
|
|
181
|
+
- id: vast
|
|
182
|
+
class: gpu
|
|
183
|
+
status: backup
|
|
184
|
+
enabled: true
|
|
185
|
+
phase: 1
|
|
186
|
+
hq_country: US
|
|
187
|
+
# US ONLY, and this is the SAME finding as RunPod's above with a sharper edge (2026-10-13).
|
|
188
|
+
#
|
|
189
|
+
# Vast is a MARKETPLACE, and `VastProvider._cheapest_offer` sends no location filter at all —
|
|
190
|
+
# its `/bundles/` query asks for rentable + verified + gpu_ram and nothing about WHERE the
|
|
191
|
+
# machine is. So an `eu` claim on this row could not be honoured even in principle: a job placed
|
|
192
|
+
# on "vast, region eu" lands on whichever stranger's host is cheapest, anywhere in the world.
|
|
193
|
+
# That is weaker than RunPod's claim was, not stronger, because RunPod at least has named
|
|
194
|
+
# datacentres.
|
|
195
|
+
#
|
|
196
|
+
# TO RESTORE `eu` HERE, `_cheapest_offer` must filter offers by geography first — the row is a
|
|
197
|
+
# promise about placement and the adapter is what keeps it. Until then the registry says what is
|
|
198
|
+
# true. (US capacity is unaffected; this is the EU claim only.)
|
|
199
|
+
regions: [us]
|
|
200
|
+
api_base: https://console.vast.ai/api/v0
|
|
201
|
+
env: [VAST_API_KEY]
|
|
202
|
+
|
|
203
|
+
# OWNER RULING 2026-09-21: lambda is IN the launch pool, and the key is already on the
|
|
204
|
+
# directory. It was previously in `on_hold` while `gpuproviders.LambdaProvider` resolved it and
|
|
205
|
+
# `nodewake.warmpool_providers()` defaulted to `vast,runpod,lambda,datacrunch` — the resolver
|
|
206
|
+
# returns {} for a provider the seed does not know, so a Lambda credential could never come
|
|
207
|
+
# from the database however it was stored. With this row the warmpool default is correct as
|
|
208
|
+
# written. `enabled: true` follows "in the launch pool"; until the credential is PUT it
|
|
209
|
+
# resolves as unconfigured and the race skips it, so nothing launches on an absent key.
|
|
210
|
+
- id: lambda
|
|
211
|
+
class: gpu
|
|
212
|
+
status: active
|
|
213
|
+
enabled: true
|
|
214
|
+
phase: 1
|
|
215
|
+
hq_country: US
|
|
216
|
+
regions: [us]
|
|
217
|
+
api_base: https://cloud.lambdalabs.com/api/v1
|
|
218
|
+
env: [LAMBDA_API_KEY]
|
|
219
|
+
# Account-scoped resources, not secrets — §A5: the owner supplies the credential, the code
|
|
220
|
+
# discovers the rest.
|
|
221
|
+
derived: [LAMBDA_REGION, LAMBDA_SSH_KEYS, LAMBDA_INSTANCE_TYPE]
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
# ── PROMOTED TO PHASE 1 AND ENABLED — OWNER 7 IS DONE, owner ruling 2026-10-20 ───────────
|
|
225
|
+
#
|
|
226
|
+
# THIS REPLACES THE 2026-09-21 RECORD that "Phala = Phase 2 / deferred". That record was true
|
|
227
|
+
# when it was written: the row described an API nobody had called and there was no key, so a
|
|
228
|
+
# tenant asking for the `tee` rung got an empty candidate set. `PHALA_API_KEY` now exists in the
|
|
229
|
+
# estate and is loaded into the encrypted provider-credential store, and the doctor's probe
|
|
230
|
+
# reports `configured: true, auth: accepted, reachable: true` — PROVED with the key ABSENT from
|
|
231
|
+
# the environment, so the only possible source was the store.
|
|
232
|
+
#
|
|
233
|
+
# `phase: 1` MOVES WITH `enabled: true`, following the vast and nebius precedents above:
|
|
234
|
+
# enabling a phase-2 row on its own would break the "no phase-2 provider is enabled" invariant
|
|
235
|
+
# and trade one inconsistency for a less obvious one.
|
|
236
|
+
#
|
|
237
|
+
# ⚠️ ENABLED IS NOT DEFAULT-ON. `class: tee` keeps this provider out of the ordinary merit-order
|
|
238
|
+
# race entirely — the TEE lane is reachable only by a request that asks for it (`require_tee`),
|
|
239
|
+
# which is plan 08's own rule and is asserted in `tests/test_phala_tee_provider.py`. So
|
|
240
|
+
# `enabled: true` here means "available to a tenant who requires attestation", not "available to
|
|
241
|
+
# everyone's next chat". `status: emerging` STAYS: it describes maturity, which this ruling did
|
|
242
|
+
# not change.
|
|
243
|
+
- id: phala
|
|
244
|
+
class: tee
|
|
245
|
+
status: emerging
|
|
246
|
+
enabled: true
|
|
247
|
+
phase: 1
|
|
248
|
+
hq_country: SG
|
|
249
|
+
regions: [eu, us]
|
|
250
|
+
# CORRECTED 2026-10-07 (owner). `https://api.phala.network` was wrong and nothing had ever called it,
|
|
251
|
+
# so nothing failed — the row simply described an API that is not there. `cloud.phala.com` is the
|
|
252
|
+
# DASHBOARD, not the API. The real base is below, and it matches the paths in Phala's own OpenAPI
|
|
253
|
+
# (`/api/v1/cvms/provision`, `/api/v1/cvms`, `/api/v1/cvms/{cvm_id}`); Phala's own SDK calls the
|
|
254
|
+
# override `PHALA_CLOUD_API_PREFIX`.
|
|
255
|
+
api_base: https://cloud-api.phala.com/api/v1
|
|
256
|
+
env: [PHALA_API_KEY]
|
|
257
|
+
notes: >-
|
|
258
|
+
TEE lane, Phase 1 since 2026-10-20 (Owner 7 done, key loaded). DEFAULT-OFF BY CLASS: a `tee`
|
|
259
|
+
provider never enters the merit-order race, so it serves only requests that set `require_tee`
|
|
260
|
+
(plan 08). A tenant requiring the rung now gets attested capacity instead of an empty set.
|
|
261
|
+
|
|
262
|
+
# ── PHASE 2 — defined so the shape is settled; provisioned only after launch (plan 29) ──
|
|
263
|
+
- id: alibaba
|
|
264
|
+
class: gpu
|
|
265
|
+
status: backup
|
|
266
|
+
enabled: false
|
|
267
|
+
phase: 2
|
|
268
|
+
hq_country: CN
|
|
269
|
+
# INTERNATIONAL ONLY, and REGION-LOCKED because the company is PRC-incorporated (2026-10-12).
|
|
270
|
+
#
|
|
271
|
+
# `ap-southeast` is the PRODUCT's region label (the deployment serves eu-west, eu-central,
|
|
272
|
+
# eu-north, uk, us-east, us-west, ap-southeast); Alibaba's own region id is `ap-southeast-1`
|
|
273
|
+
# (Singapore) and lives in `ALIBABA_REGION_ID`. Two vocabularies, deliberately not conflated.
|
|
274
|
+
#
|
|
275
|
+
# `region_locked` is what actually fences it. `regions` alone does NOT: `providers.eligible()`
|
|
276
|
+
# skips region narrowing entirely when no region is given — the warm pool reconciles that way by
|
|
277
|
+
# design — so before this field an enabled row was eligible for every unlabelled launch. It was
|
|
278
|
+
# measured that way before the field existed. Unstated is not permitted.
|
|
279
|
+
regions: [ap-southeast]
|
|
280
|
+
region_locked: true
|
|
281
|
+
api_base: https://ecs.ap-southeast-1.aliyuncs.com
|
|
282
|
+
# The SDK's OWN names. `ALIBABA_ACCESS_KEY_ID`/`_SECRET` were seeded here and are not
|
|
283
|
+
# what any Alibaba Cloud SDK reads — it looks for the `ALIBABA_CLOUD_` prefix. Verified
|
|
284
|
+
# against SDK source 2026-09-05; it had regressed to the short form since. Nothing
|
|
285
|
+
# reads these today (no account, `enabled: false`), which is exactly why a wrong name
|
|
286
|
+
# survives here unnoticed until the day it is needed.
|
|
287
|
+
env: [ALIBABA_CLOUD_ACCESS_KEY_ID, ALIBABA_CLOUD_ACCESS_KEY_SECRET]
|
|
288
|
+
notes: >-
|
|
289
|
+
Alibaba Cloud INTERNATIONAL (Singapore), ECS gn-series. RAM user credential, never the root
|
|
290
|
+
account. Region-locked: it may serve ap-southeast traffic only and is excluded from every
|
|
291
|
+
EU/UK/US job and from any launch whose region is unstated. `enabled: false` until a key
|
|
292
|
+
exists. Launch spec is the operator's (FRUTEBOX_ALIBABA_ECS_SPEC: ImageId, InstanceType,
|
|
293
|
+
SecurityGroupId, VSwitchId); `create` refuses without it. Terminate never stop —
|
|
294
|
+
DeleteInstance with Force=true, because Force=false requires a Stopped instance first and a
|
|
295
|
+
stopped-but-unreleased instance still bills for its disks. US export controls apply to the
|
|
296
|
+
hardware available (H20-class).
|
|
297
|
+
|
|
298
|
+
# NEBIUS — PROMOTED TO PHASE 1 AND ENABLED (owner decision 2026-10-16: Verda + Nebius replace
|
|
299
|
+
# RunPod). It was `phase: 2, enabled: false` while its API shape was unverified and its
|
|
300
|
+
# credential absent; the shape is now read from the vendor's published OpenAPI document and
|
|
301
|
+
# pinned in `gpuproviders`, so the row no longer has to hold the lane shut.
|
|
302
|
+
#
|
|
303
|
+
# IT STILL CANNOT LAUNCH WITHOUT A CREDENTIAL, and that is the right kind of gate: the authorized
|
|
304
|
+
# key is an owner action (Owner 34), `configured()` is False without it, and the race skips an
|
|
305
|
+
# unconfigured provider. `enabled: true` says "this lane is open"; the credential says "and it
|
|
306
|
+
# can serve". Those are different claims and only the second one is missing.
|
|
307
|
+
- id: nebius
|
|
308
|
+
class: gpu
|
|
309
|
+
status: active
|
|
310
|
+
enabled: true
|
|
311
|
+
phase: 1
|
|
312
|
+
# Nebius Group N.V. is DUTCH (Amsterdam) — verified 2026-10-14 — so `hq_country: NL` makes it
|
|
313
|
+
# EU-incorporated and `regionmap.residency_providers` would derive it as EU-sovereign.
|
|
314
|
+
hq_country: NL
|
|
315
|
+
regions: [eu]
|
|
316
|
+
# SECOND EU PROVIDER, BEHIND VERDA (owner decision 7). Its place in the preference order is in
|
|
317
|
+
# `FRUTEBOX_REGION_PROVIDERS` / `regionmap._DEFAULT`, not here: this row says what it IS, the map
|
|
318
|
+
# says who is asked first.
|
|
319
|
+
#
|
|
320
|
+
# HELD FROM RESIDENCY-LOCKED WORK, and that is a decision recorded rather than taken. Nebius was
|
|
321
|
+
# spun out of Yandex N.V. (Russian assets sold July 2024, international business renamed August
|
|
322
|
+
# 2024). It is Dutch-incorporated, so the hq_country derivation alone would admit it to
|
|
323
|
+
# EU-sovereign work — and whether a Yandex-descended entity may serve legally pinned EU traffic
|
|
324
|
+
# is the same class of question the owner reserved for themselves on `kandinsky-5.0`. The
|
|
325
|
+
# owner's own instruction said strict-EU-residency prefers Verda and Nebius takes non-residency
|
|
326
|
+
# overflow and the frontier/large models.
|
|
327
|
+
#
|
|
328
|
+
# CLEARED BY THE OWNER, 2026-10-20: "Nebius may run EU-locked work; remove the residency hold."
|
|
329
|
+
# The question the hold was holding for has been answered, so the flag goes rather than being
|
|
330
|
+
# left `false` with the prose still arguing for it — a held provider nobody can tell is held is
|
|
331
|
+
# worse than either state. What still gates placement is the thing that is checked rather than
|
|
332
|
+
# declared: `_assert_eu_project` reads the project's own region from IAM before any create, and
|
|
333
|
+
# eu-north2 stays excluded because it is Iceland (EEA, not EU).
|
|
334
|
+
api_base: https://api.nebius.cloud
|
|
335
|
+
# A SERVICE-ACCOUNT AUTHORIZED KEY, as JSON — not an API token. Nebius IAM issues a key file
|
|
336
|
+
# holding a key id, a service-account id and a PRIVATE KEY; the client signs a JWT with it and
|
|
337
|
+
# exchanges that for a short-lived IAM token. So this is ONE env var holding a JSON document,
|
|
338
|
+
# and `AuthorizedKeyError` is what `configured()` reports when it will not parse.
|
|
339
|
+
# TWO NAMES, AND THE SECOND IS NOT A SECRET. The authorized key is the credential; the project
|
|
340
|
+
# id is an account-scoped resource — but it is NOT `derived:`, because an account holds one
|
|
341
|
+
# project PER REGION and choosing one for the operator would be choosing the jurisdiction the
|
|
342
|
+
# data lands in. So it is required configuration, read through `_provider_credential` like
|
|
343
|
+
# everything else, and its region is CHECKED against IAM before any create.
|
|
344
|
+
env: [NEBIUS_SERVICE_ACCOUNT_KEY, NEBIUS_PROJECT_ID, NEBIUS_SERVICE_ACCOUNT_ID,
|
|
345
|
+
NEBIUS_PUBLIC_KEY_ID, NEBIUS_PRIVATE_KEY, NEBIUS_PRIVATE_KEY_FILE,
|
|
346
|
+
NEBIUS_KEY_ID]
|
|
347
|
+
# EITHER FORM IS A COMPLETE CREDENTIAL — see `ProviderRow.env_groups`. IAM hands you a key id,
|
|
348
|
+
# an account id and a PEM in three places and never the assembled document, so requiring the
|
|
349
|
+
# single JSON var meant hand-building JSON around a private key; with the parts present the
|
|
350
|
+
# provider still reported itself unconfigured. The JSON form is listed first because it is one
|
|
351
|
+
# secret instead of three, so it is what an empty account is told to set.
|
|
352
|
+
#
|
|
353
|
+
# NEBIUS_KEY_ID is declared SO IT CAN BE DIAGNOSED, not because it authenticates: it is the
|
|
354
|
+
# object-storage access-key id, which the compute API's IAM rejects outright ("Invalid nid
|
|
355
|
+
# format", measured 2026-10-20). It is in no group, so it never counts towards a complete
|
|
356
|
+
# credential — it is here only so the loader may recognise it and say which kind of key it
|
|
357
|
+
# found and which kind it needs.
|
|
358
|
+
env_groups:
|
|
359
|
+
- [NEBIUS_SERVICE_ACCOUNT_KEY, NEBIUS_PROJECT_ID]
|
|
360
|
+
- [NEBIUS_SERVICE_ACCOUNT_ID, NEBIUS_PUBLIC_KEY_ID, NEBIUS_PRIVATE_KEY, NEBIUS_PROJECT_ID]
|
|
361
|
+
- [NEBIUS_SERVICE_ACCOUNT_ID, NEBIUS_PUBLIC_KEY_ID, NEBIUS_PRIVATE_KEY_FILE, NEBIUS_PROJECT_ID]
|
|
362
|
+
notes: >-
|
|
363
|
+
Second EU GPU provider, behind Verda in the preference order. Credential is a JSON document
|
|
364
|
+
(authorized key id + service account id + RSA private key), signed into a short-lived JWT and
|
|
365
|
+
exchanged at auth.eu.nebius.com for a 12-hour IAM token — never a static API key. The REST
|
|
366
|
+
shape is CONFIRMED: read from the vendor's published OpenAPI document
|
|
367
|
+
(api.nebius.cloud/openapi.json, 2026-10-16), so /compute/v1/instances create/list/get/delete
|
|
368
|
+
are quoted rather than guessed. The REGION IS A PROPERTY OF THE PROJECT, not of the instance —
|
|
369
|
+
a create body carries no region at all — so EU placement is enforced by checking the
|
|
370
|
+
project's spec.region against IAM before creating, and eu-north2 is excluded because it is
|
|
371
|
+
ICELAND (EEA, not EU). The boot disk is always VM-MANAGED so the vendor deletes it with the
|
|
372
|
+
instance. CLEARED FOR EU-RESIDENCY-LOCKED WORK by the owner on 2026-10-20 (the Yandex-lineage
|
|
373
|
+
question is answered); it shares EU work with Verda on price and availability, and still
|
|
374
|
+
carries the frontier/large models needing H200/B200.
|
|
375
|
+
LIVE CALL MADE 2026-10-18 (Owner 34 landed): the authorized key signs, exchanges for an IAM
|
|
376
|
+
token, and the adapter created and terminated a real instance
|
|
377
|
+
(computeinstance-e04fk8xszm6whg8fss) in project default-project-eu-west2. Two shapes only a
|
|
378
|
+
live call could settle: the boot disk is a NAMED resource wrapping its own spec, and the
|
|
379
|
+
public image catalog requires a region parameter.
|
|
380
|
+
WHICH EU PROJECT TO USE, MEASURED 2026-10-20. The account holds one project per region and
|
|
381
|
+
their GPU catalogues are NOT the same, which is what made this provider look broken: the
|
|
382
|
+
configured project was eu-west2, whose catalogue offers gpu-b300-sxm AND NOTHING ELSE, so
|
|
383
|
+
"the cheapest EU single-GPU preset" could only ever be a 1xB300 - and that preset goes
|
|
384
|
+
STARTING -> STOPPED after ~5m20s with no status message, no operation record and no serial
|
|
385
|
+
console (the API exposes none). eu-north1 publishes 17 GPU presets including single-GPU
|
|
386
|
+
L40S, H100 and H200; on the identical code path, image family and discovered subnet an
|
|
387
|
+
L40S-a/1gpu reached RUNNING in 78 seconds. So the B300 churn is vendor-side capacity for
|
|
388
|
+
this account in that region, not configuration - and FRUTEBOX_NEBIUS_PROJECT_ID should name
|
|
389
|
+
the eu-north1 project. Catalogue per region: eu-north1 = L40S/H100/H200/GB300 (17 presets),
|
|
390
|
+
eu-west1 = H200 only, eu-west2 = B300 only, eu-south1 = RTX6000-Ada only.
|
|
391
|
+
MULTI-REGION: the vendor's EU regions are eu-north1 (Finland), eu-west1 and eu-west2
|
|
392
|
+
(France); eu-north2 is Iceland and excluded. The REGION IS A PROPERTY OF THE PROJECT - a
|
|
393
|
+
create body carries none - and this account's project is eu-west2, verified against IAM
|
|
394
|
+
before any create. The project id is discoverable from the service account's own profile
|
|
395
|
+
(/iam/v1/profiles -> serviceAccountProfile.info.metadata.parentId).
|
|
396
|
+
PRICE: the vendor publishes no price LIST but DOES price a configuration, via
|
|
397
|
+
/billing/v1alpha1/calculators/estimate - 9.5/hr for the one qualifying EU single-GPU preset
|
|
398
|
+
(gpu-b300-sxm/1gpu-24vcpu-346gb) on 2026-10-18.
|
|
399
|
+
|
|
400
|
+
- id: hetzner
|
|
401
|
+
class: compute
|
|
402
|
+
status: backup
|
|
403
|
+
enabled: false
|
|
404
|
+
phase: 2
|
|
405
|
+
hq_country: DE
|
|
406
|
+
regions: [eu]
|
|
407
|
+
api_base: https://api.hetzner.cloud/v1
|
|
408
|
+
env: [HETZNER_API_TOKEN]
|
|
409
|
+
|
|
410
|
+
- id: vultr
|
|
411
|
+
class: compute
|
|
412
|
+
status: backup
|
|
413
|
+
enabled: false
|
|
414
|
+
phase: 2
|
|
415
|
+
hq_country: US
|
|
416
|
+
regions: [us]
|
|
417
|
+
api_base: https://api.vultr.com/v2
|
|
418
|
+
env: [VULTR_API_KEY]
|
|
419
|
+
|
|
420
|
+
# OFF on_hold 2026-09-21. The readme records azure "STRUCK FROM THIS LIST (supervisor
|
|
421
|
+
# correction 2026-09-12)", and the owner's 2026-09-08 ruling puts the GCP + Azure TEE lane in
|
|
422
|
+
# PHASE 1 — the readme is authoritative over this file on that point.
|
|
423
|
+
#
|
|
424
|
+
# `enabled: false` is not a hedge: plan 28 §A3's `HyperscalerConfidentialProvider` does not
|
|
425
|
+
# exist yet, so nothing can resolve or launch this. The row exists so the credential has
|
|
426
|
+
# somewhere to live when the service principal is PUT, and so the doctor reports it instead of
|
|
427
|
+
# omitting it — a provider missing from this file does not report as broken, it does not
|
|
428
|
+
# report at all. The lane also stays unreachable unless STEVAI_TEE_ALLOW_HYPERSCALER=true.
|
|
429
|
+
# OWNER RULING 2026-09-24 — AZURE IS ALSO THE AUSTRALIA LANE, AND THAT IS A SECOND LANE.
|
|
430
|
+
# Sharon AI is ELIMINATED from the roster and Hyperstack is DROPPED; Australia moves to Azure.
|
|
431
|
+
# The row below is the TEE lane and does NOT serve that: it is `class: tee`, so the class gate in
|
|
432
|
+
# `nodewake` will never place a general GPU pod on it. The general-purpose Australia lane is a
|
|
433
|
+
# SEPARATE row with its own class and its own `AZURE_POD_SPEC`, and one spec cannot serve both —
|
|
434
|
+
# a TEE instance type and a general GPU instance type are different machines.
|
|
435
|
+
#
|
|
436
|
+
# DELIBERATELY NOT ADDED AS A ROW YET. Owner: "do not build the Azure lane yet." A disabled row
|
|
437
|
+
# is one edit away from being enabled, and a lane with no spec of its own is exactly what the
|
|
438
|
+
# 2026-09-24 ruling on `VAST_POD_SPEC` forbids — a provider that borrows another's spec deploys
|
|
439
|
+
# the wrong machine and bills for it. QUEUED BEHIND `LAMBDA_POD_SPEC` and `DATACRUNCH_POD_SPEC`,
|
|
440
|
+
# in that order.
|
|
441
|
+
#
|
|
442
|
+
# `SHARONAI_API_KEY` has no row here and no code reader, but IS set on all three live gateways.
|
|
443
|
+
# OWNER RULING 2026-09-24: IT STAYS FOR NOW, and the reason is recorded so nobody removes it as
|
|
444
|
+
# tidy-up or leaves it as forgotten drift. Sharon AI is ELIMINATED from the roster but PERMITTED
|
|
445
|
+
# SHORT-TERM while the Azure lane is built, and it is not in `WARMPOOL_PROVIDERS`, so the key is
|
|
446
|
+
# INERT rather than drift: nothing reads it, and the race cannot reach the provider.
|
|
447
|
+
# REMOVE IT WHEN THE AZURE LANE IS LIVE. Until then it is also one of the 13 provider keys that
|
|
448
|
+
# readme rule 9 says belong on the DIRECTORY and not on the gateways (recorded 2026-09-17), so
|
|
449
|
+
# removing it later closes that divergence too.
|
|
450
|
+
- id: azure
|
|
451
|
+
class: tee
|
|
452
|
+
status: emerging
|
|
453
|
+
enabled: false
|
|
454
|
+
phase: 1
|
|
455
|
+
hq_country: US
|
|
456
|
+
regions: [eu, us]
|
|
457
|
+
api_base: https://management.azure.com
|
|
458
|
+
env: [AZURE_TEE_TENANT_ID, AZURE_TEE_CLIENT_ID, AZURE_TEE_CLIENT_SECRET, AZURE_TEE_SUBSCRIPTION_ID]
|
|
459
|
+
notes: >-
|
|
460
|
+
Standard_NCCadsH100v5, typically needing a one-off quota request. Unreachable unless
|
|
461
|
+
STEVAI_TEE_ALLOW_HYPERSCALER=true (default off), so it incurs nothing until an org sets
|
|
462
|
+
require_tee. Provider class still to be built (28 §A3).
|
|
463
|
+
|
|
464
|
+
# GCP CONFIDENTIAL H100 — THE SECOND HALF OF THE TEE PATH THE OWNER NAMED (2026-10-14: "the TEE
|
|
465
|
+
# path (Phala+GCP)").
|
|
466
|
+
#
|
|
467
|
+
# WHY THIS ROW DID NOT EXIST, WHICH IS THE WHOLE FINDING. `gcp` was ALREADY in
|
|
468
|
+
# `tee.HYPERSCALER_TEE_PROVIDERS`, so the policy half was right and the module's own docstring
|
|
469
|
+
# said "Azure and GCP are capable but gated by ALLOW_HYPERSCALER". But
|
|
470
|
+
# `provider_can_host_tee` finishes by asking the REGISTRY for a `class: tee` row, and there was
|
|
471
|
+
# no `gcp` row of any class — so `provider_can_host_tee("gcp")` returned False even with
|
|
472
|
+
# STEVAI_TEE_ALLOW_HYPERSCALER=true. Measured, not read: with the flag ON, phala=True,
|
|
473
|
+
# azure=True, gcp=False. The gate was closed by an absent row rather than by a decision, which
|
|
474
|
+
# is the shape that reads as a policy and is not one.
|
|
475
|
+
#
|
|
476
|
+
# `enabled: false`, and that is NOT a hedge — it is 08-A's own instruction. The plan lists GCP
|
|
477
|
+
# as "4) GCP confidential H100 (*verify GA at build; preview → disabled*)", and nothing here
|
|
478
|
+
# can verify GA. So the row exists (the doctor reports it, the credential has somewhere to
|
|
479
|
+
# live, routing can see it) and it cannot launch anything until an owner both VERIFIES GA and
|
|
480
|
+
# enables it. A provider missing from this file does not report as broken — it does not report
|
|
481
|
+
# at all, which is how this gap survived.
|
|
482
|
+
#
|
|
483
|
+
# Reachable only when STEVAI_TEE_ALLOW_HYPERSCALER=true (default off), exactly like azure:
|
|
484
|
+
# cloud-MEDIATED attestation is a deeper lock-in than renting, so the direct-NRAS lane (phala)
|
|
485
|
+
# sorts first in `allowed_tee_providers` even when this is switched on. Credentials by env name
|
|
486
|
+
# only — never committed, never a cloud KMS or Secret Manager (rule 10).
|
|
487
|
+
- id: gcp
|
|
488
|
+
class: tee
|
|
489
|
+
status: emerging
|
|
490
|
+
enabled: false
|
|
491
|
+
phase: 1
|
|
492
|
+
hq_country: US
|
|
493
|
+
regions: [eu, us]
|
|
494
|
+
api_base: https://compute.googleapis.com/compute/v1
|
|
495
|
+
env: [GCP_TEE_PROJECT, GCP_TEE_ZONE, GCP_TEE_SA_JSON]
|
|
496
|
+
notes: >-
|
|
497
|
+
Confidential H100 (a3-highgpu / CC mode) on Confidential VMs. VERIFY GA BEFORE ENABLING —
|
|
498
|
+
08-A says preview means disabled. Unreachable unless STEVAI_TEE_ALLOW_HYPERSCALER=true
|
|
499
|
+
(default off), so it incurs nothing until an org sets require_tee. Attestation is
|
|
500
|
+
cloud-MEDIATED, so phala stays the preferred direct-NRAS lane. Provider class still to be
|
|
501
|
+
built (28 §A3) — this row makes the lane VISIBLE to routing, not launchable.
|
|
502
|
+
|
|
503
|
+
# ON HOLD — never a launch dependency (readme provider-key placement note). Deliberately NOT
|
|
504
|
+
# rows above, so nothing can enable them by flipping a flag: Lambda, NAVER, Sakura,
|
|
505
|
+
# Spheron, Azure.
|
|
506
|
+
#
|
|
507
|
+
# MAGALU AND BINARYLANE ARE DROPPED, not on hold (owner ruling 2026-09-24). On-hold means "we may
|
|
508
|
+
# yet use this"; dropped means we will not, so leaving either here would be a standing invitation
|
|
509
|
+
# to re-enable something the owner removed. Neither ever had a row, a code reader or an adapter.
|
|
510
|
+
# `MAGALU_API_KEY` was never set on any service. `BINARYLANE_API_TOKEN` WAS set on all three
|
|
511
|
+
# gateways and has been removed from them.
|
|
512
|
+
#
|
|
513
|
+
# Z1 IS ALSO DROPPED (owner ruling 2026-09-24), and it is the clearest case yet for checking the
|
|
514
|
+
# real variable names before removing anything: the ruling named `Z1_ACCESS_KEY_ID` and
|
|
515
|
+
# `Z1_SECRET_ACCESS_KEY`, but the live names were `Z1_S3_ACCESS_KEY` and `Z1_S3_SECRET_KEY`.
|
|
516
|
+
# Removing exactly what was named would have deleted `Z1_S3_ENDPOINT` and LEFT BOTH CREDENTIALS
|
|
517
|
+
# on all three gateways, while reporting the job done. All three are removed.
|
|
518
|
+
on_hold: [naver, sakura, spheron]
|