fediversity api Constitution

Core Principles

I. Only the Public Surface of core

api wraps the stateless deployment programs, the application catalog and the configuration model of core. It MUST use only the public surface of core: the attributes of core/default.nix, that is the rendered outputs (nixosConfigurations, packages, apps, devShells, checks, formatter, legacyPackages, deploymentAttributeNames, attributeFamilies) and lib (render, foldGroupComponents, foldGroupModules, foldGroupExtensions, baseGroups, applications-type, the environment key sets, testers, components). api MUST NOT import other files under core/, and MUST NOT copy the internal logic of core (for example a port of a Nix function to Python). When api needs a fact that the public surface does not give, core adds it to its public surface first.

api MAY use shared infrastructure of the repository root that is not a sub-project (for example npins/, the overlays and the contracts library under nix/).

Rationale: core must be free to change its internals. A second front-end must get the same facts from core as api, through the same surface.

II. Agnostic to Front-End Business Logic

api MUST NOT contain the business logic of a front-end: authentication of end users, accounts, sessions, multi-tenancy, ownership of deployments, or billing. api MUST NOT resolve the identity of a caller. It forwards each request to the authorization callback of the front-end, and obeys the decision. A deployment identifier is an opaque token that the front-end supplies. api stores and returns it, and MUST NOT parse a structure out of it. api MAY refuse an identifier that cannot name the resources of a deployment, by a rule on its characters and length that is the same for each front-end.

Rationale: the front-end that owns the accounts is the one identity authority. A second source of identity makes two truths that can disagree. Different front-ends (single-tenant, or multi-tenant with other rules) must be able to use the same api.

III. Any API Client, Not the Panel

api serves API clients. The panel is one of them. api MUST NOT know the panel: its code, names, paths, state directories, URLs, roles or conventions. A value that a front-end supplies (for example the base URL of the front-end, or the address of its authorization callback) MUST be configuration, not a default that names the panel.

Rationale: the panel was split out of api so that api is a service with one public contract. A second front-end is the reason for this contract.

IV. Agnostic to the Hosting-Provider Business Case

api runs the deployment programs for a hosting provider, but it MUST NOT contain the business case of one provider: its group of machines, its products, its identity, secrets or IPAM systems, or its names. A service that the provider gives (a PostgreSQL database, a job runner, a state backend, a trusted backend that calls api) MUST be a generic, optional input with a generic name. The wiring of api into the deployment of a provider is configuration of that provider.

Rationale: other providers must be able to run api with their own infrastructure, and a change in the setup of one provider must not change api.

V. Runtime Facts, Derived for the Client

api owns the runtime facts of a deployment: the build history, the state of the deployment, the configuration and module source of each build, and the infrastructure that the builds made. The front-end owns the intent: drafts, human names and ownership. A front-end treats a configuration as opaque, so api MUST derive the facts that a front-end shows (for example the domain and the instances of a deployment, and the schema of one application) and serve them as typed fields. The runtime facts MUST survive a restart of api.

Rationale: a front-end that reads option names out of a configuration breaks when core changes the configuration model.

VI. A Described Wire Contract

The HTTP interface MUST be described by OpenAPI 3.1, and the events by AsyncAPI 3.0, both made from the models in the code. specification.md is the prose contract of the wire surface and of its design decisions. A change to the wire surface updates both in the same pull request. A change that breaks a client MUST be named as such in the pull request.

VII. Safe and Bounded Execution

A module source that a caller names is code that api runs. api MUST run only module sources that the provider trusts. Work that a caller starts (schema builds, deployment jobs) MUST have limits on concurrency and time, and MUST fail with a clear error when a limit is reached. A job that a restart interrupts MUST get a final state, and MUST NOT stay "running" forever.

Rationale: naming an untrusted fork is equal to remote code execution, and a slow build must not block other callers.

Constraints

  • All inputs are pinned with npins. The pinned nixpkgs is upstream nixpkgs, not a fork.
  • api/default.nix is the single source of truth for all outputs. api/flake.nix is a thin wrapper that adds the ${system} layer and does not add, remove or change outputs.
  • The service is Python with FastAPI. Configuration comes from environment variables, read in one place (settings.py) and described in docs/environment.md.
  • The state store is SQLite, or PostgreSQL when a database URL is given.
  • Events go through Centrifugo. Deployment jobs run through Windmill, or as local subprocesses without Windmill.

Development Workflow

  • Changes go through pull requests on git.fediversity.eu, with the title prefix api: . The treefmt check and the checks that CI routes for the change MUST pass.
  • A pull request that changes behavior updates the matching specification under api/specs/ in the same pull request.
  • Prose in comments, commit messages and pull requests uses ASD-STE100, US English and ASCII punctuation, with backticks around code.
  • Documentation describes the current state of the code. The description of an environment variable is next to the code that reads it.

Governance

This constitution has priority over other practices in api/. An amendment is a pull request that changes this file, updates the version and the Sync Impact Report, and updates any specification that the amendment affects. Versions follow semantic versioning: MAJOR for a removed or redefined principle, MINOR for a new principle or section, PATCH for clarifications.

Version: 1.0.1 | Ratified: 2026-09-23 | Last Amended: 2026-10-15