dev defined twice, ports published on every interface, and a Skill that is four files on a mount
Make Every Hardware Intelligent — an open-source Agent OS for hardware intelligence
At a glance
- What is it?
- Anima is a Python agent runtime for household hardware, currently limited to Mi Home and MIoT devices, driving real appliances through LangGraph planners and MQTT. The interesting parts are the configuration seams: two conflicting dev dependency sets, a compose file that publishes its API on all interfaces, and skills that live on a bind mount where editing a file changes runtime behaviour.
- Who is it for?
- It fits someone who already runs Mi Home hardware and wants a local controller they can read, extend through skills, and drive from a dashboard, a REST API or a CLI. It does not fit a deployment on an untrusted network in its current form, because the compose file publishes the API, the broker and the dashboard on every interface and the visible configuration says nothing about authenticating callers who can then move real appliances.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 44 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 3, 2026, and from our analysis. They are not legal advice.
Editorial analysis
dev is defined twice with floors that do not overlap
pyproject.toml carries two separate dev lists. The optional extra named dev asks for pytest>=8.0.0, pytest-asyncio>=0.24.0, pytest-cov>=5.0.0, ruff>=0.8.0 and pre-commit>=4.0.0. The dependency group also named dev asks for pytest>=9.0.3, pytest-asyncio>=1.3.0 and pytest-timeout>=2.4.0. Same package names, floors a major version apart on the first two, and no overlap in the tails: coverage, linting and hooks live only in the extra, timeouts live only in the group. Which one you get depends on how you invoke the toolchain, and package.json makes the choice for you by running `uv sync --extra dev --python 3.13` in postinstall, which resolves the extra. Anyone who runs a group-based sync instead silently lands on a different pytest and a different asyncio plugin.
Three services publish on every interface, and the API binds 0.0.0.0
The compose file maps `"${ANIMA_MQTT_PORT:-1883}:1883"`, `"${ANIMA_API_PORT:-8080}:8080"` and `"${ANIMA_DASHBOARD_PORT:-3000}:80"` with no loopback prefix on any of them, and the core service sets `ANIMA_API_HOST: 0.0.0.0`. So on a default `docker compose up`, a FastAPI service that controls real household appliances is reachable from the local network on port 8080. The same service receives `ANIMA_XIAOMI_CLOUD_USER` and `ANIMA_XIAOMI_CLOUD_PASS` from the environment, defaulting to empty strings, which are the account the QR login path uses to obtain device tokens. Neither the compose file nor the README states an authentication requirement for the REST API or for the broker. The port defaults can be changed, but nothing in the shipped configuration asks you to.
The container always installs the Xiaomi stack the project calls optional
python-miio>=0.5.9 sits in an optional extra named miot, alongside the unconditional list of fastapi, uvicorn, aiomqtt, langgraph, openai, langchain-openai, pydantic, pydantic-settings, pyyaml, httpx, rich, socksio, amqtt and matplotlib. The Dockerfile makes that extra mandatory regardless, running `uv sync --frozen --no-dev --extra miot --no-install-project` after copying only pyproject.toml and uv.lock. That matters because Mi Home and MIoT are the only devices the project currently supports. So a container always carries the Xiaomi stack, while a local install that omits the extra removes the only device integration the runtime has. The image is python:3.13-slim even though requires-python allows 3.11 up to below 3.14, and the same 3.13 is pinned again in the postinstall hook.
A Skill is a four file contract, and skills/ is a live bind mount
The Skill layout is specified file by file:
SKILL.md # skill metadata, supported devices, and operating rules
references/
knowledge.md # domain knowledge
decide.md # single-decision prompt
learn.md # long-term learning prompt
scripts/
actions.py # structured action execution entrypointEight are built in, `light`, `humidifier`, `air_conditioner`, `air_purifier`, `speaker`, `coordinator`, `device_discovery` and `skill_creator`, and your own go under skills/custom/. Compose mounts `./skills:/app/skills`, so a Skill you edit on the host is the Skill the runtime loads, with no rebuild step. The same trick applies to state: the image creates data/memory/users/default and compose bind mounts ./data over /app/data. The documented rule for adding a device type is to extend Skills rather than hardcode policy into the Brain or an Adapter.
Two broker implementations, one port, and a three step health chain
The dependency list names both aiomqtt and amqtt, there is an amqtt.yaml at the repository root, and the package.json script `dev:broker` starts `.venv/bin/amqtt -c amqtt.yaml`. Compose runs something else entirely, the eclipse-mosquitto:2 image with `./mosquitto/mosquitto.conf` mounted over its config. So local development and the container arrangement do not run the same broker. The startup order is expressed with health conditions rather than plain depends_on, core waits for mqtt to be healthy and dashboard waits for core, and each service proves itself differently: mosquitto publishes to the anima/healthcheck topic, core runs a Python one-liner that opens http://127.0.0.1:8080/health, and dashboard runs wget with --spider against http://127.0.0.1/health. The dashboard is the only service whose health check is a shell utility rather than an interpreter.
The third core pillar is a heading that stops mid-word
The Core Highlights section has three numbered pillars. The first two are described in full: the Brain as the central decision layer, then Skill as the smallest unit of device intelligence. The third begins with the line `### 3. Memory: Evidence-Bas` and the document ends there, so the memory pillar is introduced by a truncated heading and has no body text at all. What can be said about memory comes from an earlier question block instead, which names a preferences.md file, normalized learned profiles per device type and extracted topic memories, and says the Brain pulls preferences incrementally out of interaction history. What that section does not establish, because it never arrives, is how those pieces are structured or when they are consulted.
The Brain states its own boundary, then ships with gpt-4o and no key
The Brain section is explicit about intent, saying its purpose is not to let an LLM control devices freely and that decisions happen inside explicit skill boundaries, device capabilities and safety rules. The mechanics listed are LangGraph based planner and executor flows, a unified chat entrypoint at /api/chat, scheduled brain ticks for proactive environment checks, context construction before a skill runs, and state verification plus history writes after actions. Configuration is OpenAI compatible, so any such endpoint works, with providers named as OpenAI, DeepSeek, Doubao, Anthropic through a proxy and local Ollama compatible endpoints, which is why Anthropic needs the proxy rather than being addressed directly. Two environment values need attention out of the box: ANIMA_LLM_API_KEY defaults to an empty string, and ANIMA_LLM_MODEL defaults to gpt-4o.
Four instruction documents, two languages, and no tagged release
The root carries AGENT.md and CLAUDE.md side by side, plus ARCHITECTURE_GUARDRAILS.md with a zh-CN twin, plus README.md with README.zh-CN.md, and CONTRIBUTING.md, SECURITY.md and CHANGELOG.md. Machine-enforced rules sit alongside the prose ones: .pre-commit-config.yaml, ruff.toml and .editorconfig, with .python-version and .nvmrc pinning the two toolchains and a pnpm-workspace.yaml alongside uv.lock. There are no GitHub releases, and both manifests sit at 0.1.0 while package.json is marked private, so there is no published version to pin and nothing to compare a checkout against. The dev scripts assume a POSIX layout as well, since the backend and broker entries point at `.venv/bin/python` and `.venv/bin/amqtt` by path rather than resolving them.
Editorial conclusion
It fits someone who already runs Mi Home hardware and wants a local controller they can read, extend through skills, and drive from a dashboard, a REST API or a CLI. It does not fit a deployment on an untrusted network in its current form, because the compose file publishes the API, the broker and the dashboard on every interface and the visible configuration says nothing about authenticating callers who can then move real appliances. Before exposing it, bind the three published ports to loopback or put a reverse proxy with authentication in front, decide which of the two dev dependency sets your environment should follow, and remember that the third core pillar is documented only as far as a heading that stops mid-word.
Frequently asked questions
Which smart home devices does Anima support?
Mi Home and MIoT devices are the only ones currently supported. Device discovery, QR login and device activation are handled by the device_discovery skill, and the python-miio integration is shipped as the miot extra.
What files make up an Anima Skill?
SKILL.md for metadata and operating rules, references/knowledge.md, references/decide.md and references/learn.md for the knowledge and prompts, and scripts/actions.py as the structured action entrypoint. Custom skills go under skills/custom/.
Which LLM providers can Anima talk to?
Any OpenAI compatible API, with OpenAI, DeepSeek, Doubao, Anthropic through a proxy and local Ollama compatible endpoints named in the project. Configuration is ANIMA_LLM_API_KEY plus an optional ANIMA_LLM_BASE_URL.
Does the Anima docker compose setup expose its API to the network?
The published ports carry no loopback prefix and the core service sets ANIMA_API_HOST to 0.0.0.0, so the API, the broker and the dashboard bind on all interfaces by default. No authentication requirement is stated for the REST API in the compose file or the README.
Why does Anima have two sets of dev dependencies?
pyproject.toml defines dev twice, once as an optional extra pinning pytest 8 and pytest-asyncio 0.24 with coverage, ruff and pre-commit, and once as a dependency group pinning pytest 9 and pytest-asyncio 1.3 with pytest-timeout. The postinstall hook syncs the extra.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/fullive-ai-anima)