Deploy With Dokploy
Deploy the Downcity Node block to Dokploy with a persistent Federation database.
The Node block is the standard Docker / Dokploy deployment shape for Downcity Federation Infra.
It runs templates/node, exposes the Federation HTTP API, and stores SQLite data in a persistent /data volume. Use it when you want a long-running service layer for cities, apps, agents, or internal tools.
Dokploy Settings
Create an app in Dokploy from this Git repository:
| Setting | Value |
|---|---|
| Deployment mode | Compose |
| Compose file | ./docker-compose.yml |
| Container port | 43127 |
| Volume | Compose creates downcity_infra_data |
Configure these two stable bootstrap secrets in Dokploy:
DOWNCITY_FEDERATION_ADMIN_SECRET_KEY=admin_xxx
DOWNCITY_FEDERATION_TOKEN_SIGNING_KEY=sign_xxxHOST, PORT, and DOWNCITY_CITY_DATABASE_URL are already defined in docker-compose.yml:
HOST=0.0.0.0
PORT=43127
DOWNCITY_CITY_DATABASE_URL=file:/data/downcity.sqlite/data must stay persistent. Cities, models, users, sessions, usage, payments, and Federation env entries are stored in that SQLite database.
Domain Path
The Node block listens on 43127, and the Federation API is mounted at the service root:
GET /health
POST /v1/env/upsert
GET /v1/services
POST /v1/ai/textIf your domain is https://base.example.com, product-side SDKs and trusted backends should use:
base_url=https://base.example.comIf Stripe redirect URLs are not configured directly, write DOWNCITY_CITY_BASE_URL into Federation env so payment pages can derive default success and cancel URLs:
curl -X POST https://base.example.com/v1/env/upsert \
-H 'content-type: application/json' \
-H 'authorization: Bearer admin_xxx' \
-d '{"key":"DOWNCITY_CITY_BASE_URL","value":"https://base.example.com"}'Provider Keys
Provider API keys should not be stored in Dokploy env. After deployment, write them into the Federation database through the Admin API:
curl -X POST https://base.example.com/v1/env/upsert \
-H 'content-type: application/json' \
-H 'authorization: Bearer admin_xxx' \
-d '{"key":"OPENAI_API_KEY","value":"your_api_key"}'
curl -X POST https://base.example.com/v1/env/upsert \
-H 'content-type: application/json' \
-H 'authorization: Bearer admin_xxx' \
-d '{"key":"DEEPSEEK_API_KEY","value":"your_api_key"}'At runtime, ctx.env.OPENAI_API_KEY and ctx.env.DEEPSEEK_API_KEY are read from the Federation database first.
Verify
After Dokploy finishes the deployment, verify the public service:
curl https://base.example.com/healthWhen using an IP and port:
curl http://1.2.3.4:43127/healthExpected shape:
{
"ok": true,
"services": ["env", "cities", "accounts", "balance", "payment", "usage", "ai"]
}If the request times out, the Dokploy service is not exposed publicly, the VPS firewall does not allow 43127, or a reachable domain has not been bound yet.
Call From Cities
City frontends and apps should call the Node block through @downcity/city:
import { City } from "@downcity/city";
const client = new City({
role: "user",
federation_url: "https://base.example.com",
city_id: "my-city",
user_token: "user_token",
});Trusted backends should use Admin City with DOWNCITY_FEDERATION_ADMIN_SECRET_KEY:
import { City } from "@downcity/city";
const admin = new City({
role: "admin",
federation_url: "https://base.example.com",
admin_secret_key: process.env.DOWNCITY_FEDERATION_ADMIN_SECRET_KEY,
});See User City and Admin City for the full SDK surface.