- Python 54.1%
- JavaScript 22%
- CSS 11.6%
- Nix 7.7%
- HTML 4.6%
| apps/homefree-ai | ||
| docs | ||
| .gitignore | ||
| AGENTS.md | ||
| CLAUDE.md | ||
| flake.nix | ||
| README.md | ||
homefree-ai
A HomeFree flake plugin that turns natural-language prompts into HomeFree plugin scaffolds — the same recursive idea as lovable.dev / gpt-engineer, narrowed to HomeFree's plugin contract.
The service runs as a HomeFree app: a small FastAPI backend, a
single-page vanilla-JS frontend, an OCI container built from nixpkgs,
SSO-gated by HomeFree's Caddy oauth2-proxy flow. The user enters a
prompt ("build me a HomeFree plugin called homefree-feedreader that
runs Tiny Tiny RSS on port 3060"); the model produces a complete
plugin-flake directory on disk; the user registers it through the same
admin panel Custom Flakes page they used to register this plugin.
It works against the Anthropic Claude API out of the box and against HomeFree's local Ollama instance when enabled, via a small provider abstraction.
What it adds
HomeFree AI runs as a single container, reverse-proxied at
ai.<your-domain>. Once registered it appears in the admin panel
under Services → HomeFree AI with these options:
- enable — turn the service on/off
- public — expose it on the WAN port
- provider —
anthropic(default) orollama - anthropic-model — defaults to
claude-opus-4-7 - ollama-model — defaults to
llama3.1:8b - ollama-base-url — optional override; defaults to the local
HomeFree Ollama instance when its
enableflag is true
The web UI is gated by HomeFree's Zitadel SSO. The API key for
Anthropic is read at runtime from
/var/lib/homefree-secrets/homefree-ai/anthropic-api-key; populate it
once and systemctl restart homefree-ai (the file is mounted
read-only into the container, but re-read on every request, so a
restart is only needed if the file was empty at boot).
How to add it to HomeFree
This plugin is registered through the admin panel — no command line,
no editing /etc/nixos by hand.
- Put this repository on the HomeFree machine (it must be a git
repository — a
git+file://flake requires one). Either clone it there, or copy the directory and rungit init && git add -A && git commitinside it. - Open the HomeFree admin panel and go to Developers → Custom Flakes.
- Click Add a custom flake and choose:
- Local repository — file-browser to this directory, or
- Remote URL —
github:<you>/homefree-ai(or wherever you publish it).
- Click Register flake, then Apply Changes.
- Open Services → HomeFree AI, enable it, choose a provider, Apply Changes.
- Populate the API key (Anthropic provider) or pull a model (Ollama provider) — see below.
To remove it: delete the entry on Custom Flakes and Apply Changes.
Populating the Anthropic API key
Until the admin UI grows a generic secrets-entry page, set the key once over SSH:
sudo install -d -m 700 /var/lib/homefree-secrets/homefree-ai
sudo install -m 600 /dev/stdin /var/lib/homefree-secrets/homefree-ai/anthropic-api-key <<'EOF'
sk-ant-...
EOF
sudo systemctl restart homefree-ai
Using the local Ollama provider
Enable HomeFree's built-in Ollama app first (Services → Ollama) and pull a model:
sudo podman exec ollama ollama pull llama3.1:8b
Then set provider = "ollama" in Services → HomeFree AI. The
container will auto-resolve the Ollama base URL from the LAN address.
How it works
flake.nix # exposes nixosModules.default
apps/homefree-ai/
default.nix # the NixOS module — declares options, container,
# systemd service, and the homefree.service-config
# entry that drives reverse proxy / SSO / backup.
image.nix # OCI image: python3 + fastapi/uvicorn/anthropic
# + jinja2 + httpx, plus the ./app source tree.
icon.svg # admin-panel tile icon
app/ # Python source copied into the image
main.py # FastAPI app: project APIs + SSE token stream
settings.py # env-var loader
providers/ # LLM provider abstraction
generator/ # prompt → file blocks → on-disk project
static/ # vanilla-JS single-page UI
flake.nix's nixosModules.default is what HomeFree composes into the
system build. apps/homefree-ai/default.nix is an ordinary HomeFree
module — it declares options in both homefree.services.homefree-ai
and homefree.service-options.homefree-ai (the dual-namespace pattern
required for plugin flakes, see
~/homefree-navidrome/README.md for the rationale), wires a podman
container with virtualisation.oci-containers, and contributes a
single homefree.service-config list entry that the rest of HomeFree
consumes (Caddy reads the reverse-proxy block, restic reads the backup
paths, the admin panel reads the options-metadata).
The provider abstraction
providers/base.py defines an async LLMProvider.stream(system, messages) interface. Two implementations:
anthropic_provider.py— officialanthropicSDK, streaming.ollama_provider.py—httpxover/api/chatwithstream=true.
get_provider(name) returns the right one based on the configured
provider name. Adding e.g. an OpenAI provider is a single new file
plus a registry entry.
The generation flow
The model is given a system prompt that summarizes the HomeFree plugin contract in ~1500 tokens, plus one stripped Navidrome example for few-shot. It is told to emit project files as plain-text blocks:
<<<FILE path="flake.nix">>>
{
outputs = ...;
}
<<<END>>>
generator/parser.py extracts these blocks with a deterministic
regex; generator/projects.py writes them under
/data/projects/<slug>/, refusing any path that escapes the project
root. Tokens stream to the browser over SSE while the model is still
producing output. The result is browsable, downloadable as a tarball,
and ready for the user to git init and register through Custom
Flakes — homefree-ai itself never writes to live system config or
executes the generated code.
Security model
- The UI is fully SSO-gated (
oauth2 = true,kind = "caddy_gated"). - The Anthropic key is owned by root and mounted read-only into the container.
- The generator only writes inside
/data/projects/<slug>/; path traversal is rejected before any write. - The model has no shell access, no tool-use that reaches the host, and no path to the live HomeFree configuration. The generated artifact is plain text the user reviews and registers themselves.
Limits and future work
- v1 ships Anthropic + Ollama; OpenAI/Gemini providers are a straightforward addition.
- The API key is set over SSH today; the admin panel will eventually grow a generic secrets-entry page.
- Generated plugins are scaffolds — the user should still read and trim the output, and is expected to test in a HomeFree VM before deploying to a real box.