Instructions to use thundercode/SatQuery with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- PEFT
How to use thundercode/SatQuery with PEFT:
Task type is invalid.
- Notebooks
- Google Colab
- Kaggle
Download docs/DEPLOYMENT.md from thundercode/SatQuery: direct link, hf CLI and curl.
- Browser
- Download file 16 kB
-
https://huggingface.co/thundercode/SatQuery/resolve/5ce82bef20f9510057a0a4f7bcbb0b5ee643d9cb/docs/DEPLOYMENT.md
- Command line
-
hf download hf://thundercode/SatQuery@5ce82bef20f9510057a0a4f7bcbb0b5ee643d9cb/docs/DEPLOYMENT.md
-
curl -L -o DEPLOYMENT.md https://huggingface.co/thundercode/SatQuery/resolve/5ce82bef20f9510057a0a4f7bcbb0b5ee643d9cb/docs/DEPLOYMENT.md
Deployment
Status tags: IMPLEMENTED · VERIFIED · MEASURED · OPEN · DEFERRED · BLOCKED · BY DESIGN.
The live SatQuery AI system runs across three private repositories plus one public umbrella repository. The monorepo working copy is not the deployed source. This document records the live topology, every environment variable, the deploy mechanics, the cold-start behaviour, the known issues, and the platform traps that cost real debugging time.
The single most important trap in this document.
deploy/inside the monorepo is stale and untracked. It is not the deployed source. Edits must go to the three real repositories.
1. Live revisions (VERIFIED)
Read from the GitHub API during the release reconnaissance.
| Component | Repository | Visibility | Branch | Revision | Host |
|---|---|---|---|---|---|
| Frontend | Anish-lab-blip/SatQuery-Frontend |
private | main |
2d7ae53b482d |
Cloudflare Pages → satquery.pages.dev |
| Backend / orchestrator | Anish-lab-blip/SatQuery-Backend |
private | main |
89d80eaddec5 |
Render → satquery-backend-m4yv.onrender.com |
| Inference | Anish-lab-blip/SatQuery-Inference |
private | main |
5a0936ace491 |
Codespace potential-space-trout-r4ppw969w45j2pvvw, port 8000 |
| Public umbrella | Anish-lab-blip/SatQuery-AI |
public | main |
3dcabd32da41 ("Initial commit") |
this release home |
| Monorepo (working copy) | C:/Users/anish/satquery-ai |
local only | master |
9d57aed |
no git remote; 334 dirty entries |
The public umbrella repository previously contained only a 13-byte README (# SatQuery-AI). It is
the intended home for this release.
2. The four tiers
Browser
│ HTTPS
▼
Cloudflare Pages — satquery.pages.dev (static frontend, 11 pages)
│ HTTPS / JSON → /api/*
▼
Render — satquery-backend-m4yv.onrender.com (orchestrator / gateway)
│ outbound long-poll POST /tunnel/agent
▼
GitHub Codespace — FastAPI inference, CPU, port 8000
│ build_space_app()
▼
specialists: SmolVLM · RemoteCLIP · MiniLM · CROMA · STANet
│
▼
ResultEnvelope → tunnel → Render → browser
flowchart LR
U[Browser] -->|HTTPS| CF["Cloudflare Pages<br/>static frontend"]
CF -->|"HTTPS JSON<br/>/api/health · /api/capabilities · /api/infer · /api/assets"| R["Render<br/>orchestrator / gateway"]
R -->|"outbound long-poll<br/>POST /tunnel/agent"| C["GitHub Codespace<br/>FastAPI inference :8000"]
C --> S[(SmolVLM · RemoteCLIP<br/>MiniLM · CROMA · STANet)]
C -->|ResultEnvelope| R
R -->|"envelope + error translation"| CF
3. Why each tier exists
3.1 Cloudflare Pages — the static tier
Serves the frontend. No backend, no secrets, and no API calls of its own on the static pages. The
one exception is the Analyze console, which calls the gateway. Staged by scripts/stage_pages.mjs and
deployed with npx wrangler pages deploy.
3.2 Render — the gateway
A deliberately thin, stateless orchestrator. Its responsibilities, from
docs/DEPLOYMENT_ARCHITECTURE.md §2:
| Responsibility | Detail |
|---|---|
| Schema validation | reject malformed requests before they cost inference |
| Size limits | whole-request body cap, shared with the Codespace |
| Rate limiting | per-IP count + window — fairness, NOT a security control |
| CORS allowlist | the Pages origin; never * |
| Request ids | correlate a request across tiers |
| Timeouts | sit inside the task budget |
| Secret custody | holds credentials that must never reach the browser |
| Error translation | upstream failures → the documented error envelope |
It is not a model host. It has no database, no auth, and no queue.
Hard rule. The gateway must not retry
POST /api/inferon its own — a retry would consume inference a second time. The client decides on retry.
3.3 GitHub Codespace — the inference tier
Runs the real inference service: build_space_app() from app/space_app.py, served by
deploy/codespace/serve.py on $PORT, in CPU mode. It honours the four-endpoint contract
(/v1/health, /v1/capabilities, /v1/analyze, /v1/assets), imports cheaply without torch, reuses
app/serving.py as the composition root, and degrades rather than crashes on absent artifacts.
3.4 Hugging Face — the model tier
Holds the six trained artifacts and the model card. It is not the runtime inference host; the Codespace resolves the pinned backbones from the Hub at run time.
4. Why the transport is an outbound tunnel
The inference host is a Codespace in a private repository. A forwarded port for a private repo
returns 302, so an inbound-forwarding design cannot work. Instead:
- the Codespace runs
deploy/codespace/tunnel_agent.py; - the agent dials out to
POST /tunnel/agentand long-polls; - work is executed against
http://127.0.0.1:8000locally.
This inverts the usual direction: the inference host needs no inbound firewall hole. It also means the transport is only alive while the agent is polling.
Measured: GET /api/health reported tunnel.agent_connected: true with a non-zero completed
counter, and POST /api/infer {} returned 422 invalid_request with the response header
x-satquery-transport: tunnel.
5. The full live health payload (VERIFIED, probed)
{
"status": "ok",
"service": "satquery-orchestrator",
"tunnel": {
"agent_connected": true,
"agent_id": "codespaces-fd1038",
"pending": 0,
"completed": 97
},
"config": {
"codespace_name": "potential-space-trout-r4ppw969w45j2pvvw\n",
"codespace_port": 8000,
"transport_mode": "auto",
"tunnel_timeout_s": 150.0,
"wake_timeout_s": 120.0,
"upstream_timeout_s": 90.0,
"device": "cpu",
"has_github_token": true
}
}
Note codespace_name still carries a trailing \n — this is B-02, cosmetic and OPEN; the
wake path strips it (_codespace_name()), so only the health payload reports the raw value.
6. Environment variables
6.1 Render (gateway) — measured live values
| Variable | Value (live) | Purpose |
|---|---|---|
CODESPACE_NAME |
potential-space-trout-r4ppw969w45j2pvvw |
which Codespace to wake |
CODESPACE_PORT |
8000 |
the inference port |
SATQUERY_ALLOWED_ORIGINS |
https://satquery.pages.dev |
CORS allowlist (never *) |
SATQUERY_DEVICE |
cpu |
device preference |
SATQUERY_TRANSPORT |
auto |
tunnel first, then forward |
SATQUERY_TUNNEL_TIMEOUT_S |
150 |
how long to wait on the tunnel |
SATQUERY_WAKE_TIMEOUT_S |
120 |
how long to wait for a cold start |
SATQUERY_UPSTREAM_TIMEOUT_S |
90 |
gateway → upstream budget |
GITHUB_TOKEN |
present | Codespace control |
PORT |
platform-supplied | Render's own listen port |
Measured absence. There is no
SATQUERY_UPSTREAM_URLand noHF_TOKENin the live config. The transport is the outbound tunnel, not a forwarded port. This contradicts the olderdocs/DEPLOYMENT_TOPOLOGY.md§3.2 table, which predates the tunnel design.
6.2 Codespace (inference)
| Variable | Purpose |
|---|---|
PORT |
platform-assigned; must be read (a historical blocker was that no code read it) |
SATQUERY_DEVICE |
cpu | cuda | mps | null; read without importing torch |
SATQUERY_MAX_FILE_BYTES |
per-file cap, shared with Render |
SATQUERY_ASSET_ENABLED / SATQUERY_ASSET_DIR |
both required for /v1/assets; fails closed (503) otherwise |
SATQUERY_ASSET_MAX_FILES / SATQUERY_ASSET_TTL_S |
optional handle capacity / lifetime |
6.3 Config-loader environment overrides
Two registry values can be overridden from the environment without editing the YAML
(core/config.py):
| Variable | Effect |
|---|---|
SATQUERY_PRECISION |
overrides training.precision |
SATQUERY_TORCH_COMPILE |
overrides deployment.torch_compile ("true" → True) |
Both are still validated by the loader — setting SATQUERY_TORCH_COMPILE=true fails startup
because finding C-8 forbids torch.compile.
7. Deploy mechanics
| Tier | Mechanism |
|---|---|
| Frontend → Cloudflare Pages | scripts/stage_pages.mjs builds a Pages bundle; npx wrangler pages deploy |
| Backend → Render | render.yaml blueprint; main.py exposes the ASGI object app |
| Inference → Codespace | deploy/codespace/serve.py binds build_space_app() to $PORT; .devcontainer/ forwards 8000 and starts the tunnel agent via postStartCommand |
| Repository writes | the GitHub Git Data API — blob → tree → commit → PATCH ref |
7.1 Why the Git Data API instead of git push
Every deployed file is uploaded as a blob whose sha256 is computed locally and verified against the uploaded blob, then assembled into a tree, committed, and the branch ref patched. This means:
- each file is content-verified rather than trusted;
- deletions are expressed explicitly as
sha: nulltree entries; - the deploy is idempotent — re-running it with identical content produces no change.
Measured: 9 deployed files were re-read from the API and found sha256 byte-identical to the
local copies, with the deployed HEAD re-read independently (verify_deployed_head.py).
8. Cold start (documented, not hidden)
Render's free tier sleeps when idle, and the Codespace may be stopped. Before a request can be served, Render must start the Codespace (if stopped) and wait for the tunnel agent to reconnect. The frontend shows "Waking inference engine…" during this.
| Property | Value |
|---|---|
| Cold start | tens of seconds |
| Tunnel wait before falling through | SATQUERY_TUNNEL_TIMEOUT_S = 150 s |
| Wake wait | SATQUERY_WAKE_TIMEOUT_S = 120 s |
| Upstream budget | SATQUERY_UPSTREAM_TIMEOUT_S = 90 s |
Cold start is documented rather than papered over: an honest "this will take a while the first time" is better than a silent hang.
8.1 The transport_mode: auto fallthrough — B-07 (OPEN)
SATQUERY_TRANSPORT=auto means: try the tunnel; on timeout, fall through to the forward path.
The forward path to a private repo returns 302 quickly — but the wake step still consumes
SATQUERY_WAKE_TIMEOUT_S (120 s) first. So a worst-case failed request takes roughly
150 s (tunnel timeout) + 120 s (wake timeout on a 302) ≈ 249 s
This is the root shape of the observed transient tunnel gap, and it is why a request can appear to
hang and then fail. A patch (fix-b07-forward-unavailable.patch) was authored and verified
(py_compile clean, applies cleanly to the deployed main.py), adding distinct
forward_unavailable (503, terminal 302/401/403 on the forward path) and upstream_timeout
(504, tunnel healthy but slow) codes, plus the codespace_name .strip() fix.
Status: B-07 is
OPEN. The patch is prepared but NOT deployed. The deployed health payload still shows the trailing\n.
9. The five historical backend blockers
Before any backend could boot, five verified blockers had to be closed. The current design closes them:
| # | Blocker (verified) | How it is closed |
|---|---|---|
| 1 | requirements.txt declared no fastapi / uvicorn / httpx / starlette |
the runtime installs the ASGI stack so build_space_app() and the gateway can import |
| 2 | no code read $PORT — a platform port would be ignored |
deploy/codespace/serve.py binds to $PORT; Render reads its own |
| 3 | hand-rolled CORS raised 405 on OPTIONS, so browser preflight failed |
the gateway registers OPTIONS explicitly / uses Starlette's CORS middleware |
| 4 | module-level app = create_app() swallowed config errors into app = None |
construction errors now propagate (fail-fast) instead of leaving a dead app |
| 5 | adapter integrity unverified on load (_adapter_sha256 computed but never compared) |
the load path compares the digest against an expected value, or fails startup |
10. Platform traps (recorded so they are not rediscovered)
| Trap | Detail |
|---|---|
Cloudflare _headers CONCATENATE |
Two matching rules are merged, not overridden. A specific rule nested under a broad /assets/img/* rule yields max-age=604800, …, max-age=0, must-revalidate — and Chromium takes the FIRST max-age. The file's own "later rules override" comment is false. |
| Cloudflare 308 redirect | X.html → /X. Reference the extensionless path. |
| Forwarded port returns 302 | for a private repo — this is why the tunnel exists. |
| Tunnel agent must start on boot | via the devcontainer postStartCommand, or a restarted Codespace comes up with agent_connected: false. |
Never retry /api/infer at the gateway |
a retry consumes inference twice. |
deploy/ is stale and untracked |
not the deployed source. |
11. The superseded design, and what did NOT change
The earlier design ran inference on an HF Space with ZeroGPU (5 GPU-min/day, @spaces.GPU
decoration) behind a Railway gateway. The active design changes three things:
- CPU-first instead of ZeroGPU. No code change was required —
device_preferencehonoursSATQUERY_DEVICEand defaults to CPU, every specialist defaults todevice="cpu", and all placement is.to(device)(never.cuda()). - A real, always-buildable inference environment. A Codespace gives a reproducible container without a GPU quota or a Space's cold-start constraint. The wake flow replaces ZeroGPU lazy loading as the cold-start story.
- No GPU quota to protect at the gateway. Rate/size limits remain, but as fairness controls rather than quota protection.
What did NOT change:
| Unchanged | Detail |
|---|---|
| the four-endpoint contract | /v1/health, /v1/capabilities, /v1/analyze, /v1/assets |
| the gateway responsibility table | §3.2 above |
| the env-var vocabulary | only host names moved (SATQUERY_SPACE_URL → SATQUERY_UPSTREAM_URL) |
| the config freeze | 78f1e3700da15aa1 |
configs/deploy.yaml still describes the old HF-Space/ZeroGPU target (platform: huggingface-spaces,
sdk: gradio, zerogpu: true, the gpu_duration_* values). It is frozen paperwork: no Gradio
runtime exists in code, and editing it would move Config.hash. It is left undisturbed.
12. Deployment preconditions (open items)
- Cloudflare Pages project name / domain (needed for the deploy command and
robots.txtsitemap). - Artifacts present, or capabilities honestly ship
available: false— the service degrades rather than crashes on absent artifacts. HF_TOKENon Render if the HF proxy path is used (not used in the live config).- Codespace
.devcontainer/forwarding:8000and starting the tunnel agent. - The five blockers in §9 closed and verified on the first live run.
13. What is NOT RUN / OPEN / BLOCKED for this topic
| Item | State |
|---|---|
| B-07 tunnel fallthrough | OPEN — patch prepared, NOT deployed |
B-02 codespace_name trailing \n |
OPEN (cosmetic) |
| A deployed system-level load test | NOT RUN |
| Multi-region / HA deployment | NOT RUN |
| A production database / persistence | BY DESIGN (absent) |
| The ZeroGPU/Gradio deployment target | REJECTED (superseded; frozen paperwork only) |
14. Where the evidence lives
| Evidence | Location |
|---|---|
| Live health + capabilities probe | release/CURRENT_RELEASE_STATE.md §1 |
| Topology contract | docs/DEPLOYMENT_TOPOLOGY.md, docs/DEPLOYMENT_ARCHITECTURE.md |
| The undeployed B-07 patch | session scratch: fix-b07-forward-unavailable.patch |
| Deployed-artifact integrity check | verify_deployed_head.py (session scratch) |
| Live validation (3 passes) | .workbuddy-ai/scratch/live_validation/ |