Skip to content
Homefellow is being built. This is how it will work.Hear when it's ready

The admin API

The admin API sets the household up and manages the server. Every call here is also a homefellow command (see the homefellow command), which prints the same JSON with --json.

  • Before the household exists: the one-time setup code, in the header X-Homefellow-Setup-Code. The server prints it when it starts. homefellow setup-code shows it again. Five wrong codes in ten minutes answer 429 slow_down. Once the household exists, the code is spent (403 set_up).
  • After: an owner’s device token, as Authorization: Bearer <token>. A member’s token answers 403 forbidden.

Setup mode. Until the household exists, only /api/health, /api/admin/*, /setup and the web app’s files answer. The rest answers 503 not_set_up.

Settings chosen here go into the data folder’s settings.toml. Models take effect at once. Voice, home-network access, the listening address and a restore take effect at the next start. restart_needed lists them.

Call Command What it does
GET /api/admin/status[?probe=true] status, doctor the doctor’s report, what needs a restart, the addresses, the version
GET /api/admin/plan setup --plan the setup steps and where each stands
POST /api/admin/plan/later setup --later family leave inviting the household for later
GET /api/admin/version version the version and database schema
GET /api/admin/timezone the computer’s time zone
POST /api/admin/setup setup create the household and its first owner
GET /api/admin/models models the models in use and the last test
POST /api/admin/models/discover models discover look for model servers
POST /api/admin/models/choose models choose use those models
POST /api/admin/models/test models test test the models
GET, POST /api/admin/voice voice how spoken answers are turned into text
GET, POST /api/admin/voice/fetch voice fetch download the speech model and see its progress
GET /api/admin/access access addresses, home network, Tailscale, iroh, the firewall
POST /api/admin/access/lan access lan on|off the home network, over HTTPS
POST /api/admin/access/tailscale access tailscale publish on the tailnet
POST /api/admin/access/iroh access iroh on|off reach it from anywhere
POST /api/admin/access/firewall access firewall record or open the firewall
GET, POST /api/admin/invites invites, invite list or make invites
DELETE /api/admin/invites/{id} invites revoke ID withdraw an open invite
POST /api/admin/people/{id}/role people role ID owner|member change someone’s role
GET, PUT /api/admin/backups backups, backups schedule the backups and their schedule
POST /api/admin/backups/run backups run make a backup now
POST /api/admin/backups/check backups check check a backup would restore
POST, DELETE /api/admin/backups/restore backups restore stage a restore, or call it off

GET /api/admin/status answers {"doctor": Report, "restart_needed": [key], "setup": {"done"}, "addresses": [Address], "version"}. With probe=true it also asks the model servers and addresses, which takes a few seconds.

  • Report: {"ok", "problems", "warnings", "version", "checked_at", "checks": [Check]}.
  • Check: {"id", "status": "ok"|"warn"|"fail"|"skip", "title", "detail", "fix"?}.
  • The check ids: setup, models.keeper, models.explorer, goose, models.test, voice, disk, timezone, calendars, addresses, firewall, certificate, devices.pinning, iroh, backups, restore, version.

GET /api/admin/plan answers {"steps": [Step], "next": id|null, "done": bool}. Each step is {"id", "title", "state": "done"|"todo"|"attention"|"optional"|"later", "detail", "command", "api"}. The step ids are household, models, goose, voice, access, calendars, family, backups.

POST /api/admin/setup, with the setup code:

{ "household": "The Smiths", "owner": "Alex", "timezone": "America/New_York",
"device": "setup agent", "lan": true, "anywhere": true }

It answers {"household", "owner", "token", "invite"}.

  • timezone defaults to the computer’s.
  • lan: true turns on home-network access and makes its certificate before the invite, so the owner’s first invite carries the fingerprint.
  • anywhere: true turns on reaching it from anywhere, with the relay in relay (Homefellow’s by default), so the first invite carries the server’s key. Ask the person first: it keeps a connection to a relay on the internet.
  • With device, the caller is signed in as the owner. token is its device token. Treat it as a password.
  • invite signs the owner in on another device: give its url to the person.
  • It works once. After that it answers 409 conflict.
  • POST /api/admin/models/discover {"hosts"?: [host], "urls"?: [url]} answers {"found": [Found], "recommendation"}. Found is {"kind", "url", "models": [{"name", "billions"|null, "context"|null}]}. The recommendation is {"keeper", "explorer", "verdict": "good"|"maybe"|"too_small"|"unknown"|"none", "words"}.
  • POST /api/admin/models/choose takes one of {"keeper": Pick, "explorer"?: Pick}, {"keeper_profile", "explorer_profile"?} or {"recommended": true, "hosts"?, "urls"?}. A Pick is {"url", "model", "api_key"?, "context"?}.
  • POST /api/admin/models/test answers {"verdict": "good"|"slow"|"too_small"|"failed", "words", "reading_seconds", "question_seconds", "found", "missed", "question", "at"}. It makes real model calls.

GET /api/admin/access answers {"addresses": [Address], "primary", "lan", "mdns", "tailscale", "iroh", "firewall", "restart_needed"}.

  • Address: {"kind": "tailnet"|"public"|"mdns"|"lan"|"local", "url", "pinned"}, in the order the apps prefer. pinned means it uses the server’s own certificate.
  • firewall: {"manager", "ports", "state": "open"|"blocked"|"unknown"|"opened"|"none", "blocked", "words", "commands", "can_allow"}.
  • POST /api/admin/access/lan {"on", "port"?, "mdns"?}: HTTPS on port (8788) after the next start.
  • POST /api/admin/access/iroh {"on", "relay"?: "homefellow"|"n0"|"off"|"https://…", "port"?}: reaching it from anywhere after the next start. 422 no_iroh means this build has no iroh.
  • POST /api/admin/access/tailscale runs tailscale serve and records the ts.net address. 422 tailscale says what’s wrong.
  • POST /api/admin/access/firewall {"opened"?: bool, "allow"?: bool}: record that the ports are open, or run the commands (only when the server runs as root).
  • POST /api/admin/invites {"role"?, "name"?, "user"?} answers {"id", "code", "url", "join", "qr_svg", "expires_at", "role", "fingerprint"}. user signs in another device for that person.
  • The link is https://homefellow.org/join#hf1.<payload>, from the server’s server.join_url (--join-url). The payload is base64url JSON: {"v": 1, "household", "code", "addresses": [Address], "fingerprint"?, "iroh"?: {"key", "relay"?, "addrs"?}}. The part after # never reaches a server, so homefellow.org never sees the invite. The page there tells a newcomer to install the app. The apps open these links directly.
  • The older form, <address>/invite/<code>#hf1.<payload> (or <address>/invite/<code> alone), is still accepted everywhere. The server makes it when server.join_url is off, or when the payload has no address for other devices and no iroh part.
  • GET /api/admin/invites lists them, newest first, each open, used, expired or revoked. An invite’s code isn’t kept, so it can’t be shown again.
  • GET /api/admin/backups answers {"dir", "keep", "daily_at", "backups": [{"name", "path", "bytes"}], "last_check", "restore", "last_restore", "restart_needed"}.
  • PUT /api/admin/backups {"dir"?, "keep"?, "daily_at"?: "HH:MM"|"off"}.
  • POST /api/admin/backups/run makes one now.
  • POST /api/admin/backups/check {"name"?} answers {"ok", "words", …}: whether it would restore.
  • POST /api/admin/backups/restore {"name"} stages a restore for the next start, after checking the backup and backing up the household as it is now. DELETE calls it off. See backups and restoring.

The rest of the server’s API, for the apps, is documented in docs/design/API.md in Homefellow’s source.

Free and open source. For families of every shape.homefellow.orgPrivacyFor AI assistants