Environment Variables
Common environment variables used by Federation.
| Variable | Purpose |
|---|---|
DOWNCITY_FEDERATION_ADMIN_SECRET_KEY | Lets Admin City manage Federation |
DOWNCITY_FEDERATION_TOKEN_SIGNING_KEY | Lets Federation issue and validate user_token internally |
DOWNCITY_CITY_DATABASE_URL | Optional. Specifies the database URL used by Federation |
OPENAI_API_KEY | Example provider key; recommended to write it into the Federation database through Admin env |
OPENAI_BASE_URL | Example provider base URL; recommended to write it into the Federation database through Admin env |
DOWNCITY_FEDERATION_ADMIN_SECRET_KEY, DOWNCITY_FEDERATION_TOKEN_SIGNING_KEY, and BETTER_AUTH_SECRET are generated automatically on first boot and stored in Federation's env table. Provider API keys should be written into the same table through the Admin API or the fed admin workspace.
If DOWNCITY_CITY_DATABASE_URL is omitted, the default database path is:
.base/downcity.sqliteHow provider env is used
The model handler reads directly from ctx.env:
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
import { generateText } from "ai";
import {
Provider,
buildAssistantMessage,
type Context,
type AIProviderChargedOutput,
type OpenAICompatibleClientConfig,
} from "@downcity/city";
import type { UIMessage } from "ai";
class DeepSeekProvider extends Provider {
constructor() {
super({
id: "deepseek",
env: { DEEPSEEK_API_KEY: "DeepSeek API Key" },
envKey: "DEEPSEEK_API_KEY",
baseURL: "https://api.deepseek.com",
});
}
protected createClient({ apiKey, baseURL }: OpenAICompatibleClientConfig) {
return createOpenAICompatible({ apiKey, baseURL, name: "deepseek" });
}
async text(ctx: Context): Promise<AIProviderChargedOutput<UIMessage>> {
const apiKey = ctx.env("DEEPSEEK_API_KEY");
const client = createOpenAICompatible({
apiKey,
baseURL: this.baseURL ?? "",
name: "deepseek",
});
const result = await generateText({
model: client.chat("deepseek-v4-flash"),
prompt: ctx.input.prompt,
});
return buildAssistantMessage(result.text, ctx, { finishReason: "stop" });
}
}
const deepseek = new DeepSeekProvider();So the practical rules are simple:
- you choose the env key names yourself
- uppercase snake case is recommended
- read values through
ctx.env(key)inside the handler
Write values into the database
A trusted backend can write provider keys through Admin City:
await admin.env.upsert({
key: "DEEPSEEK_API_KEY",
value: "sk-xxx",
});These values are stored in the env table inside the Federation database. Business runtime reads only from that Federation-managed env table and no longer falls back to .env or process environment variables.
See also Provider environment variables.