Federation
Federation is the server-side runtime container that manages Service lifecycle, auth, and HTTP routing.
Federation is the server-side core instance (formerly called "City runtime" in older docs). It encapsulates HTTP routing, auth, env vars, and database infrastructure. The client-side entry is City; do not confuse the two.
Minimal Usage
import { Federation } from "@downcity/federation";
const base = new Federation({ database });Service Registration
import { createDeepSeek } from "@ai-sdk/deepseek";
import {
AIService,
AIChannel,
read_required_env,
type AIChannelStreamInput,
type LanguageModelV3StreamResult,
} from "@downcity/federation";
class DeepSeekChannel extends AIChannel {
constructor() {
super({
id: "deepseek",
base_url: "https://api.deepseek.com",
env_key: "DEEPSEEK_API_KEY",
ai_sdk_provider_id: "deepseek",
});
}
protected async stream(
input: AIChannelStreamInput,
): Promise<LanguageModelV3StreamResult> {
const deepseek = createDeepSeek({
apiKey: read_required_env(input, this.env_key ?? ""),
baseURL: this.base_url,
});
const model = deepseek(input.model.upstream_model);
return model.doStream(input.call);
}
}
const deepseek = new DeepSeekChannel();
const ai = new AIService();
ai.use(deepseek.model({ id: "deepseek-v4-flash", upstream_model: "deepseek-chat", name: "DeepSeek V4 Flash" }));
base.use(ai);See AIChannel & Model Registration.
Official Service Registration
import { AccountsService, googleAccountsProvider } from "@downcity/services";
base.use(new AccountsService({
providers: [
googleAccountsProvider(),
],
}));Routing
import { serve } from "@hono/node-server";
serve({ fetch: (request) => base.fetch(request), port: 43127, hostname: "127.0.0.1" });HTTP Middleware
Use base.middle() to register Federation-level HTTP middleware. Middleware runs before the internal router and before Action body parsing, so callers can attach CORS, security headers, body size limits, coarse rate limits, request timeouts, or other HTTP policies at the edge of the Federation.
@downcity/city only defines the middleware interface. It does not implement or export CORS, rate limit, timeout, security header, body limit, or client IP helpers. Those policies should come from your application, platform, or HTTP framework.
base.middle(async (ctx, next) => {
const started_at = Date.now();
const response = await next();
const headers = new Headers(response.headers);
headers.set("X-Downcity-Time", String(Date.now() - started_at));
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers,
});
}));Middleware enters in registration order and exits in reverse order. It can return a Response directly to short-circuit a request, or call await next() and then modify response headers.
instruction aggregation
Federation can return a plain-text instruction document for the current instance:
const text = await base.instruction();The text aggregates:
- how to use the Federation itself
- which services are currently mounted
- env requirements declared by each module
- extra usage notes contributed by services
To read the same document remotely from a trusted admin side, call:
GET /v1/base/instructionThis route is admin-only and returns text/plain.
Auth
Federation automatically validates user_token and administrator session tokens. Access current user via ctx.user.
Database
ctx.db["table_name"] provides CityTableApi. Supports SQLite and PostgreSQL.