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.nixis the single source of truth for all outputs.api/flake.nixis 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 indocs/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:. Thetreefmtcheck 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