Platform Service¶
The platform is the single public API — every SDK call, WebUI click, and OpenAI-compatible request enters here. It authenticates the caller, then either answers directly (login, Stripe checkout, chain queries, the diagnostics console, docs) or proxies the request to the right backend with the user's identity attached. Port 8100.
It's essentially stateless — auth is JWT, and its Postgres database holds a single table (stripe_events, for webhook idempotency). Everything else lives in the services it fronts.
Auth¶
Every token is a JWT (HS256, JWT_SECRET) with a sub (username) and role — user, provider, or admin; admin passes every role check. The platform mints them (create_token):
- Users get one at signup — the faucet triggers the platform's admin-only
POST /v1/auth/onboard; an admin can also call it directly. - Providers get theirs as a JWT join key issued when their node registers (by kuberay, same secret), so the one token works as auth everywhere — SDK, web UI, API.
- Admin is a shortcut: the raw
ADMIN_TOKENused as a bearer is accepted directly as{role: admin}— no JWT — for service-to-service and first run.
On each request the platform verifies the token, sets X-User-Id / X-User-Role, and forwards downstream — the backends trust those rather than re-verifying, so end-user login is centralized here. User tokens are additionally checked against the faucet's revocation flag (cached 60s, fail-open) since JWTs live 30 days; a revoked user gets 401.
What it answers directly¶
- Auth (
/v1/auth) —me,onboard(create user + grant credits), join-key CRUD (proxied to kuberay). - OpenAI-compatible API (
/v1/models,/v1/chat/completions,/v1/completions) — a drop-in for the OpenAI SDK that routes to a user's hosted endpoints (/{username}/{name}/v1/…on Ray Serve). - Payments (
/v1/credits/checkout,/v1/credits/webhook) — Stripe checkout for credit packs; the webhook mints paid credits to the buyer, idempotent viastripe_events. - Chain (
/v1/chain) — read the on-chain token-economy ledger: events, a tx by hash, a user's history, raw RPC. - Diagnostics (
/v1/diagnostics, admin) — the operator's log + health console (a read layer over the cluster's logging/metrics backends). - Docs (
/llms.txt) — public SDK docs with this deployment's own URLs + current SDK version substituted in.
What it proxies (auth → forward with user context)¶
| Prefix | → Service | For |
|---|---|---|
/v1/jobs/*, /v1/jobs/train, /v1/resources |
job gateway (:8200) | run/manage jobs + training, cluster resources |
/v1/endpoints/* |
job gateway | deploy / start / stop / delete model endpoints |
/v1/admin/*, /v1/operator/* |
job gateway | GPU pricing, revenue splits, cluster status (admin) |
/v1/accounting/* |
metering (:8300) | usage summaries + reports |
/v1/credits/* |
credit service (:8400) | balance, transactions, transfer, packs |
/v1/storage/* |
S3 | per-user files — every user is boxed into a users/{username}/ prefix |
Storage isolation is enforced here: a user can only touch their own prefix. Full endpoint reference, error codes, and config: STORAGE.md.
Configuration¶
| Variable | Default | Description |
|---|---|---|
PLATFORM_PORT |
8100 | Service port |
PLATFORM_DATABASE_URL |
— (required) | Postgres (holds only stripe_events; DB auto-created) |
JWT_SECRET |
dev-secret-change-me |
JWT signing key |
ADMIN_TOKEN |
— (required) | Bootstrap admin token (no-JWT admin access) |
GATEWAY_URL |
http://localhost:8200 |
Job gateway (jobs, endpoints, admin, operator) |
CREDIT_SERVICE_URL / METERING_URL |
service defaults | Credits + usage proxies |
KUBERAY_URL |
http://localhost:8800 |
Join-key proxy |
FAUCET_URL |
http://faucet:8500 |
Revocation check on each request |
S3_ENDPOINT / S3_ACCESS_KEY / S3_SECRET_KEY / S3_BUCKET |
MinIO defaults | User file storage |
STRIPE_SECRET_KEY / STRIPE_WEBHOOK_SECRET |
— | Stripe checkout + webhook; key alone suffices — the webhook auto-provisions on boot (see docs/guides/4-stripe.md). Also settable at runtime via /v1/admin/stripe |
CORS_ORIGINS |
* |
Allowed origins |
RATE_LIMIT_PER_MINUTE |
6000 | Per-IP rate limit |
Running & testing¶
# Unit tests need a Postgres (test compose exposes one on host port 5433):
docker compose -f docker-compose.test.yml --profile credit up postgres -d
pytest tests/platform_service/ -v --ignore=tests/platform_service/test_docker.py
Coverage: test_auth / test_routers_auth (JWT, roles, onboard, join-key proxy), a test_routers_* per proxy (jobs, serve, credits, accounting, operator, training, storage) checking each forwards the right user context, test_routers_storage (per-user prefix isolation), test_openai_compat (OpenAI mapping), test_routers_stripe (checkout + webhook), test_routers_chain* (chain queries), test_diagnostics (health console), test_rate_limit, test_docker (deployed). Interactive API docs at /docs.