Run the API locally
The nix run and nix build commands in this section need the Nix features nix-command and flakes.
See Prerequisites for how to enable them.
Every nix run .#... / nix build .#... invocation in this section runs from
the api/ devShell, where .# resolves to the api flake; from the repo root
write them as ./api#... (the root entrypoint is thin and only re-exports the
deploy family -- see the top-level README.md).
There are two local setups. Pick Simple unless you specifically want to exercise the Windmill / Centrifugo paths.
Simple setup (synchronous, no Windmill/Centrifugo)
Deployments run as background subprocess threads inside api/; there is no job
runner and no event bus. The panel reflects completion by polling api/ (see
Run the panel locally). Leave all WINDMILL_*
and CENTRIFUGO_* variables unset.
# terminal 1 (api/ devShell)
nix run .#terraform-backend-dev
# terminal 2 (api/ devShell)
export API_CORS_ORIGINS=http://127.0.0.1:8000,http://localhost:8000
export DEPLOYMENT_ROOT="$(git rev-parse --show-toplevel)"
export DEPLOYMENT_ATTRIBUTE=operator-tf-incus-operator-all
export DEPLOYMENT_STATE_ADDRESS_VARS='{"hp_state_address":"hosting-provider-addresses"}'
nix run .#api-http-stack
The variables in terminal 2 do these tasks:
API_CORS_ORIGINS: the browser origins of the panel. Without it, the browser blocks the deployment form; see CORS below.DEPLOYMENT_*: the deployment program of the fediversity hosting provider; see Deployment program.api-http-stackrefuses to start without a deployment program: setDEPLOYMENT_ROOTandDEPLOYMENT_ATTRIBUTE, orDEPLOY_CONFIG_FILE.
api-http-stack checks each call against the authz callback of the local
panel, at http://127.0.0.1:8000/api/authz/, with the token in
$XDG_RUNTIME_DIR/fediversity-authz/token. The panel must run, else api/
refuses all calls that need authorization; see
Run the panel locally. To use api/ without the
panel, also set API_AUTHZ_ALLOW_ALL=1: then api/ allows all calls. Outside of
development, api/ refuses to start without AUTHZ_CALLBACK_URL.
The environment reference documents all other variables.
terraform-backend-dev listens on 127.0.0.1:8888 and persists state across
restarts. api-http-stack automatically sets
TF_HTTP_ADDRESS=http://127.0.0.1:8888/state/local/default so the API picks
it up without additional configuration.
api-http-stack also persists its own deployment state DB
($XDG_RUNTIME_DIR/fediversity-api/state.db) across restarts, so a deployment
made before a restart survives it -- restarting api/ (for example to rebuild it
against a different source) no longer wipes deployment state. To start from a
fresh DB, run with API_RESET_STATE=1:
API_RESET_STATE=1 nix run .#api-http-stack
When the panel form is in use, the browser fetches the schema and rename table
(/schema, /schema/migrations) directly from api/. Both schema endpoints
(GET /schema and the authenticated POST /schema/get_for) take an optional
application -- such as mastodon or authelia -- which re-roots the
response at that one application's own options instead of returning the whole
operator form, so a consumer rendering or validating a single application does
not have to walk the form itself:
curl -s 'http://127.0.0.1:8088/schema?application=peertube' | jq .schema
The result is self-contained ($schema carried over, $defs reduced to what the
subtree references), and an undeclared name is a 400 listing the declared ones.
It comes from core's provider-application-schemas output: baked for
GET /schema, built per source for POST /schema/get_for. The rename table
takes no such parameter (it is a flat list, not an option tree). See Serving one application's schema.
CORS
Those calls are cross-origin (the panel is served from :8000, api/ from
:8088), so terminal 2 sets API_CORS_ORIGINS before api-http-stack to
allow the browser to read the responses. The panel side sets the matching API_BROWSER_BASE_URL; see
Run the panel locally. Match the origin to the
host in the browser's address bar -- 127.0.0.1 and localhost are distinct
origins to the browser. Leaving API_CORS_ORIGINS unset disables CORS, so the
form's fetches return 200 but the browser blocks reading them.
Full setup (Windmill + Centrifugo)
Adds Windmill (deployment job tracking) and Centrifugo (live loading-screen updates over WebSocket). Run the two simple-setup terminals plus:
# terminal 3 (api/ devShell)
nix run .#windmill-dev
# terminal 4 (api/ devShell)
nix run .#centrifugo-dev
windmill-dev starts a local PostgreSQL instance and the Windmill server on
http://localhost:8001. It writes a superadmin token to
$XDG_RUNTIME_DIR/fediversity-windmill/token on first run.
centrifugo-dev listens on 127.0.0.1:8090 and writes an HTTP admin key to
$XDG_RUNTIME_DIR/fediversity-centrifugo/api_key on first run. The api/
variant lets api/ publish, but allows no browser origin and has no proxy
views. A front-end leaf runs its own variant through
centrifugo-dev.override { frontendOrigins; proxyBaseUrl; frontendNotes; }.
Both apps print the env vars to set. Start them before api-http-stack, as
the token and the key exist only after their first run. Then terminal 2 has
the variables of the simple setup and the variables of the two apps:
# terminal 2 (api/ devShell)
export API_CORS_ORIGINS=http://127.0.0.1:8000,http://localhost:8000
export DEPLOYMENT_ROOT="$(git rev-parse --show-toplevel)"
export DEPLOYMENT_ATTRIBUTE=operator-tf-incus-operator-all
export DEPLOYMENT_STATE_ADDRESS_VARS='{"hp_state_address":"hosting-provider-addresses"}'
export WINDMILL_BASE_URL=http://localhost:8001
export WINDMILL_TOKEN="$(cat "$XDG_RUNTIME_DIR/fediversity-windmill/token")"
export CENTRIFUGO_API_URL=http://127.0.0.1:8090
export CENTRIFUGO_API_KEY_FILE="$XDG_RUNTIME_DIR/fediversity-centrifugo/api_key"
nix run .#api-http-stack
api-http-stack reads these variables when it starts, so nix run needs no
--impure. Only the variables that change the build, PIN_EFFECT and
HOSTING_CONFIG, need nix run --impure; see
PIN_EFFECT.
The panel side sets CENTRIFUGO_BROWSER_WS_URL; see
Run the panel locally for that and the
host-consistency caveat (use 127.0.0.1 or localhost consistently for the
panel and the ws url, or the session cookie is not shared and the WS connect
silently fails).
With these set, deployment progress flows through live Centrifugo events and Windmill job tracking instead of the subprocess fallback and polling.
To also run the panel, follow the instructions in Run the panel locally.
The component reads configuration from environment variables. Every one of them
-- what it does, its default, and which services.fediversity-api option
derives it on NixOS -- is documented in the
environment reference.
Deployment program
api/ has no default deployment program. The hosting provider names it, with
these services.fediversity-api.deployment options:
root_path: the flake that contains the program.attribute: the dev shell of the program (devShells.<system>.<attribute>).stateAddressVars: Terraform variables that get the state address of an other deploy. api/ scopes the state address of each deploy, so a program cannot derive the address of an other deploy from its own address. For each<name> = <suffix>, api/ setsTF_VAR_<name>to<base>-<suffix>, where<base>is theTF_HTTP_ADDRESSbefore the scope.
The fediversity hosting provider sets these values in
api/nix/component/api.nix:
| Option | Value |
|---|---|
root_path |
the repository root |
attribute |
operator-tf-incus-operator-all |
stateAddressVars |
{ hp_state_address = "hosting-provider-addresses"; } |
The operator graph reads the addresses document of the hosting-provider
deploy at hp_state_address to find the shared nodes. The front of the state
backend lets the operator read only this document of that deploy. api-http-stack takes the same
values from DEPLOYMENT_ROOT, DEPLOYMENT_ATTRIBUTE and
DEPLOYMENT_STATE_ADDRESS_VARS (JSON).
Using a password-protected SSH key
Deployments authenticate to target hosts over SSH. By default the deploy path
uses an explicit key file (the SSH_PRIVATE_KEY_FILE env var, forwarded through
deploy-config.json's opaque deploy_env map). Because every SSH call runs
with BatchMode=yes, that key file must be unencrypted -- a passphrase prompt
would be suppressed and the connection would fail.
To deploy with a password-protected personal key, use ssh-agent instead:
decrypt the key once with ssh-add, and SSH talks to the agent over
SSH_AUTH_SOCK, so no passphrase is needed at deploy time.
# start an agent (skip if one is already running) and add the protected key
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519 # prompts once for the passphrase
# run the API (or panel) from this same shell so it inherits SSH_AUTH_SOCK,
# and leave SSH_PRIVATE_KEY_FILE / ssh_private_key_file unset
# (api/ devShell, or `./api#api-http-stack` from the repo root)
nix run .#api-http-stack
When no readable key file is configured but SSH_AUTH_SOCK is set, the deploy
scripts omit -i and let the agent supply identities. With a key file present,
the explicit-key behavior is unchanged (this is what production's agenix
unprotected key relies on). The Windmill worker runs as a service, so an agent
is only available there if the operator arranges a service-level
SSH_AUTH_SOCK.