Pi Agent internal architecture
You do not need to understand Pi Agent's internals for everyday use. This appendix lifts the hood for the occasions when Chapter 22 is not enough: an unfamiliar error appears in the Logs tab, you want to debug a problem instead of waiting for the next release, or you simply wonder why a chat interface needs so many background services. No programming is required. By the end, you will understand what s6-overlay does, why nginx sits in front of pi-web, what belongs under /data/pi-agent/, how the Watchdog probe works, and how Skills are discovered. Think of it as the wiring diagram for your home: you may never need to touch it, but it helps to know where the main switch is.
config.yaml, Dockerfile, rootfs/etc/nginx/nginx.conf, CHANGELOG.md, and DOCS.md); the upstream pi-web project at agegr/pi-web (the HA add-on pins version 0.8.4; see that tag's package.json); and the agent SDK at earendil-works/pi. If anything below looks questionable, check those repositories. Their source files are more authoritative than this guide.Why read this appendix?
To be clear, you will not need this appendix 99% of the time. Follow Chapter 2 to install Pi Agent and Chapter 3 to open it from the sidebar. If everything works, you no more need to understand the services behind it than you need to understand a compressor to use an air conditioner.
For the other 1%, the architecture becomes useful:
- The Logs tab shows an unfamiliar error. Messages such as “nginx: [emerg] host not found in upstream,” “s6-rc: fatal: unable to start service pi-web,” or “video-tools-init exited with code 1” may not have Pi Agent-specific search results. This appendix explains what each service does and what its failure means.
- You want to debug the problem yourself. Before entering the add-on container over SSH, you need to know its directories, processes, and logs.
- You want to customize or contribute. Anyone forking Pi Agent, contributing upstream, or building a companion add-on needs to understand the complete pipeline.
- You are simply curious. If you are the kind of person who opens a new device to see how it works, read on.
/etc/services.d/ as authoritative; this appendix is a map, not a guarantee.Architecture overview: the apartment-building analogy
A building provides a useful mental model for the layers of the system:
| Analogy | Component | Role |
|---|---|---|
| The building | Home Assistant (HA) operating system | Provides the foundation, utilities, and network that its tenants—the add-ons—need. |
| The building manager | HA Supervisor | Installs and starts add-ons, checks their health, and restarts them when necessary. |
| The reception desk | HA Ingress | Authenticates visitors—your browser—and routes them to the appropriate add-on. |
| One apartment | The Woow HA Pi Agent add-on container | The subject of this appendix. |
| The occupants | Processes inside the container | nginx, pi-web, and video-tools-init each perform a different part of the work. |
| The household manager | s6-overlay (process supervisor) | Starts the processes, coordinates their order, and restarts long-running services when required. |
| The storeroom | /data/pi-agent/ |
Holds valuable persistent data: conversations, keys, Skills, and video projects. |
Keep two design principles in mind:
-
Ingress is the only entry point
Your browser cannot connect directly to Pi Agent's internal services. Every request passes through HA Ingress, which confirms that you are signed in as an HA administrator before forwarding the request to Pi Agent. This is why Chapter 3 emphasizes the automatically created sidebar button: that button opens the Ingress route.
-
The container has several layers
The Pi Agent add-on container does not run just one process. nginx receives and forwards traffic, pi-web is the main application, and video-tools-init is a one-time initialization worker. s6-overlay coordinates them in the background.
The s6-overlay boot sequence
When HA Supervisor starts Pi Agent, events inside the container follow a defined sequence. s6-overlay (often shortened to s6) is the process supervisor that starts and manages the container's services.
| Stage (see the note below) | Service | Type | What it does | Typical duration |
|---|---|---|---|---|
| 1 | s6-rc-init |
System initialization | Starts s6 itself and prepares the internal signaling channels needed by the other services. | < 1 second |
| 2 | video-tools-init |
One-time task (oneshot) | Checks for the .video-tools-installed sentinel and verifies that Python 3 actually exists in the virtual environment. If either check fails, it downloads Playwright, edge-tts, pyyaml, mutagen, and Chromium (~720 MB total), installs them under /data/pi-agent/venv/ and /data/pi-agent/playwright-cache/, and then creates the sentinel. Failure is non-fatal: chat remains available even if initialization fails. |
3–8 minutes on the first run; then < 1 second |
| 3 | nginx |
Long-running service | Starts the reverse proxy on internal port 30142. Every request from HA Ingress reaches nginx first. | < 1 second |
| 4 | pi-web |
Long-running service | Starts the main Node.js/Next.js application—the interface in your browser—on internal port 30141. nginx forwards requests to it. | 3–15 seconds |
There is an important source-level detail behind that numbered overview: the video-tools-init oneshot is not listed in nginx or pi-web's dependencies.d. s6-overlay therefore starts all three services in parallel. While video-tools-init downloads 720 MB of dependencies, nginx and pi-web can already accept requests. During the initial 3–8 minutes, you can use chat even though the video pipeline is not yet ready. A comment at the start of rootfs/etc/s6-overlay/scripts/video-tools-init calls this “Parallel with pi-web.” The deliberate trade-off is an immediately available chat UI rather than making the whole interface wait for the video tools.
Supervisor also sends the Watchdog probe described below. If pi-web becomes stuck or too slow to respond, Supervisor restarts the entire container. s6-overlay then performs the same initialization again. Stage 2 normally exits in under a second, but only when both the sentinel and executable ${VENV}/bin/python3 are present. This double check prevents an interrupted first installation from being mistaken for a complete one.
nginx: the translator for HA Ingress
Why put nginx in front of a web application? In this container, nginx is both subtle and essential: it translates between HA Ingress and pi-web.
HA gives each add-on a URL like this:
https://homeassistant.local:8123/hassio/ingress/woow_ha_pi_agent/
Internally, pi-web understands only the root path (/); it does not know that HA has placed it under /hassio/ingress/woow_ha_pi_agent/. Without translation, a pi-web link such as /settings would send the browser to https://homeassistant.local:8123/settings, outside HA's route, and return a 404.
nginx performs that translation in several stages:
-
Receive the request from HA Ingress
HA sends the request to port 30142 in the Pi Agent container (
ingress_port: 30142). Because nginx listens on that port, it receives the request first. -
Read the Ingress path from the request header
HA supplies an HTTP header named
X-Ingress-Path, with a value such as/api/hassio_ingress/A1b2C3d4e5F6g7H8. Its random identifier is 16–128 characters long and changes whenever HA restarts. -
Forward the request to pi-web
nginx proxies the request to
http://127.0.0.1:30141, pi-web's internal port, and receives pi-web's HTML response. -
Validate the header against an allowlist
To prevent a forged
X-Ingress-Pathfrom injecting arbitrary content, themap $http_x_ingress_path $safe_ingress_pathblock validates it against^/api/hassio_ingress/[A-Za-z0-9_-]{16,128}$. This expression comes directly from nginx.conf; it is not a guess. A nonmatching value becomes an empty string, making subsequent body rewrites no-ops and preventing XSS through a forged header. This defense was added in v0.7.0 for security-audit finding F-01. -
Rewrite absolute paths with sub_filter
nginx uses
sub_filterto rewrite outgoing HTML, CSS, and JavaScript byte by byte. It prefixes the Ingress path tohref="/_next/,src="/_next/,href="/manifest,href="/icons/, CSS references such asurl(/_next/, and the JSON-escaped\"/_next/references in RSC flight payloads. -
Patch runtime JavaScript that sub_filter cannot reach
This is the most intricate part of the configuration. The pi-web front-end bundle makes 40+ absolute-path
/api/*calls through fetch, EventSource, and XMLHttpRequest. It also uses Next.js App Router RSC prefetches (?_rsc=<token>),history.pushState/replaceState, and links inserted at runtime byReactDOM.preinit()/next/fontas<link>elements. Staticsub_filterrewriting cannot intercept these operations. nginx therefore injects JavaScript before</head>that monkey-patcheswindow.fetch,EventSource,XMLHttpRequest.prototype.open,history.pushState/replaceState,Element.prototype.setAttribute, and theHTMLLinkElement/HTMLScriptElement/HTMLImageElementhref/srcsetters, addingwindow.__INGRESS_PATH__where needed. It also stubsnavigator.serviceWorker.registerwith an empty registration because a PWA service worker is not useful under HA Ingress and would generate console errors. The history inCHANGELOG.mdv0.5.0–v0.10.4 shows how difficult this compatibility layer has been.
Together, these layers make it possible for pi-web to remain completely unaware of HA Ingress. pi-web behaves like an ordinary Next.js application; nginx combines header rewriting, sub_filter path rewriting, and a client-side shim to fit it into HA's routing system. This design is called an Ingress shim: an adapter between incompatible interfaces.
Host to localhost and clears Origin (the same technique as the Podman version, added in v0.6.0 to fix a 403 from pi-web's isApiRequestAllowed()), then applies Ingress path rewriting and the client-side shim. The HA version adds two Ingress-specific layers; it does not perform an entirely different job./data/pi-agent/ directory map
Chapter 20 provides a short summary. The full map below shows what you may find after entering the add-on container:
| Path | Contents | Included in backups? |
|---|---|---|
Files under sessions/ with the .jsonl extension |
One JSONL file per conversation Session, with one message per line. DOCS.md promises only “one per conversation.” The upstream pi coding agent SDK determines whether and how files are grouped by working directory. For the exact structure, enter the container and run ls /data/pi-agent/sessions/ instead of relying on a guessed directory tree. |
Yes. These are the conversation records; the per-add-on mount is covered by HA backups. |
models.json |
Providers, model names, and API keys configured in the Models panel. API keys are stored in plaintext, not encrypted. Permissions such as chmod 600 restrict which local user can read the file, but they do not protect a copied backup or a compromised owner account. See Chapter 6. |
Yes. You need this file when moving to another HA host, but its plaintext secrets also make every backup sensitive. |
auth.json |
Refresh tokens for OAuth providers, such as Google Drive authorization; likewise protected with chmod 600. |
Yes. |
home/pi-cwd-*/ |
Working directories created by the pi coding agent. Since v0.10.0, the container's HOME is pinned to /data/pi-agent/home, so worktrees created by the pi CLI under pi-cwd-* survive image upgrades. The upstream project controls the naming convention. DOCS.md guarantees only pi-cwd-*/; inspect the container rather than assuming a suffix such as pi-cwd-<date>. |
Yes, except that node_modules/ and .cache/ are listed in backup_exclude to keep backups smaller. |
skills/<skill-name>/ |
Installed Skills—typically directories created with git clone from GitHub. See Chapter 14. |
Yes. Skill files are usually small. |
rclone/rclone.conf |
rclone cloud authorization, including the Google Drive access token configured in Chapter 19. | Yes. |
projects/<project>/ |
Video-pipeline workspaces containing script/, clips/, segments/, voice/, and output/. These are the intermediate files listed in Chapter 17. |
Partly. final.mp4, script.md, and subtitles.srt are included; large directories such as clips/ and voice/ are usually excluded to avoid filling a backup. |
venv/ |
The Python virtual environment containing edge-tts and other pip packages. | No. HA backups exclude it because it can be downloaded again. |
playwright-cache/ |
Playwright's downloaded Chromium binary, approximately 200 MB. | No. It can also be downloaded again. |
.video-tools-installed |
An empty sentinel file indicating that video-tools-init completed. The reset procedure in Chapter 18 removes this file so initialization can run again. | Yes. It is tiny, although including it has little practical effect. |
settings.json |
UI preferences, including the theme and default model. | Yes. |
Remember two principles:
- Irreplaceable data lives under
/data/pi-agent/. Sessions, models.json, Skills, and the rclone configuration form the core of an HA backup. Because key and token files contain plaintext secrets, protect exported backups accordingly. - Large, reproducible data such as venv and playwright-cache is deliberately excluded. Including the virtual environment would make each backup at least 500 MB larger. Instead, video-tools-init downloads these resources again after restoration.
/mnt/data/supervisor/addon_configs/<addon-slug>/ on HA OS and /usr/share/hassio/addon_configs/<addon-slug>/ on Supervised installations. The add-on source does not guarantee either path. Supervisor controls the mount, so it can vary by installation and version. If you must inspect the host, check ls /mnt/data/supervisor/addon_configs/ and ls /usr/share/hassio/addon_configs/ rather than assuming one. The safer approach is to discover the installed container name; do not blindly run docker exec -it addon_<hash>_woow_ha_pi_agent bash. After verifying the discovered name, inspect /data/pi-agent/ inside that container. The <hash> depends on the repository; b9cf5676_woow_ha_pi_agent is only an example, while local_woow_ha_pi_agent applies only to a local override. Never assume a fixed Docker or Podman container name.How the Watchdog probe checks Pi Agent
As a long-running service, Pi Agent can become unresponsive because of a memory leak, infinite loop, or stalled external API. HA Supervisor's Watchdog detects services that still have a running process but no longer function correctly.
| Item | Details |
|---|---|
| Probe endpoint | http://[HOST]:[PORT:30142]/api/home, copied directly from config.yaml's watchdog: field. Supervisor contacts port 30142, and nginx forwards the request to pi-web's /api/home. This endpoint was selected because, on a fresh installation, it is the only pi-web route that returns 200 without Session context: /api/home, as recorded in CHANGELOG v0.10.0. |
| How it is enabled | config.yaml contains the complete URL: watchdog: "http://[HOST]:[PORT:30142]/api/home". It does not use watchdog: true. An earlier version of this appendix got that HA add-on-specific URL syntax wrong. |
| Frequency | HA Supervisor determines the interval. No fixed value—30 seconds, 60 seconds, or otherwise—is guaranteed, and it may change between HA releases. To measure it on your system, inspect the timing of Watchdog entries in the add-on Logs tab. |
| Success condition | An HTTP 200 response, regardless of its body. A timeout or non-200 status is a failure. |
| Action on failure | Supervisor restarts the entire add-on container—the equivalent of docker restart addon_<hash>_woow_ha_pi_agent when that placeholder is replaced with the discovered name—and s6-overlay begins its sequence again. Do not assume the generated name. |
| Effect on Sessions | Completed messages are retained. Conversation files are append-only JSONL: each message is appended and flushed to disk. A restart can at most interrupt the message still being written; the next opening continues from the last complete line. |
| Effect on the video pipeline | If Watchdog restarts the container while pitch_video is running, clips/ may contain a partially written WebM file. There is roughly an 80% chance that the output is unusable, so rerun the job. This is one possible cause of the “video suddenly broke” symptom in Chapter 22. |
Watchdog is a double-edged sword. Automatically recovering a stuck service is useful, but a healthy add-on that merely responds slowly—for example, while processing a very long conversation history—can be mistaken for a hang and restarted. In that situation, increase the timeout in config.yaml or temporarily disable Watchdog with the switch on the HA page.
How Skill discovery works
Chapter 14 describes a Skill as a work manual for the AI. This section explains how the AI may discover those manuals—and why a Skill installed with pi install may not appear until you start a new Session.
@earendil-works/pi-coding-agent.The likely process occurs whenever you select “New Conversation”:
-
Create the Session
pi-web creates a new Session file under
sessions/.../*.jsonl. -
Scan the Skills directory
pi-web scans
/data/pi-agent/skills/*/SKILL.mdand reads theSKILL.mdfile in each subdirectory. -
Parse the YAML frontmatter
Each
SKILL.mdbegins with YAML frontmatter like this:--- name: fridge_inventory description: Help the user inventory refrigerator items and identify food that is about to expire --- # Fridge inventory skill ...Details...
pi-web reads only
nameanddescriptionat this point. It does not place the entire SKILL.md body into the AI's context; it initially supplies only the summary. -
Build an
<available_skills>blockThe names and descriptions are combined into a list wrapped in XML tags:
<available_skills> <skill name="fridge_inventory"> Help the user inventory refrigerator items and identify food that is about to expire </skill> <skill name="pitch_video"> Script-to-YouTube Uploadable Video Pipeline </skill> </available_skills>
-
Add the block to the system prompt
The
<available_skills>block is placed near the start of the AI's system prompt, giving it a menu of capabilities available on this machine. -
Let the AI select a Skill when relevant
For each request, the AI compares the task with that list. When it chooses a Skill, it uses a tool call to read the complete SKILL.md and follow the detailed instructions.
This explains why installing a new Skill requires a new Session: an existing Session still has the old system prompt. It also explains why the description in SKILL.md is so important. A vague description makes the Skill hard for the AI to select; a clear one makes it useful at the right time.
The pi-web technology stack
pi-web, a Node.js application, provides the entire browser interface: the message composer, conversation list, model selector, and Skills panel. Its main components are:
| Component | Purpose | Notes |
|---|---|---|
| Node.js 22 | JavaScript runtime | The Dockerfile installs node_22.x from NodeSource; pi-web's package.json specifies "node": ">=22.19.0". |
| Next.js 16.2.12 | Web framework handling both the React front end and back-end API routes | Version taken from the package.json at the pi-web 0.8.4 tag. It uses the App Router under app/; this appendix has not verified whether any legacy pages/ routes coexist, so consult the pi-web repository. |
| React ^19.2.4 | Front-end UI component library | Version taken from the same package.json. Server Components and Suspense are expected, but this appendix has not inspected pi-web's source to identify their use on individual routes. |
| pino | Structured JSON logger | It produces the JSON lines shown in the Logs tab. The DOCS.md description of log_level explicitly says it is “exported as LOG_LEVEL for pi-web's Next.js pino logger.” pino is not listed directly in pi-web 0.8.4's package.json; it arrives indirectly through Next.js or the upstream SDK. |
| @earendil-works/pi-coding-agent | The coding-agent SDK that handles AI logic, tool calls, and Skill management | pi-web 0.8.4 directly depends on @earendil-works/[email protected] and calls it in-process in the same Node process. It does not start a separate daemon; DOCS.md explicitly states that “there is no separate agent daemon.” |
| Internal port 30141 | pi-web's listening port | Exposed only inside the container; external requests arrive through nginx on 30142. |
| Ingress port 30142 | nginx's listening port for HA Ingress | Matches the setting in config.yaml: ingress_port: 30142. |
Several technical details are worth noting:
- The pi-coding-agent SDK runs in-process. If pi-web crashes, the agent crashes with it. Agent tool calls, such as file writes, use pi-web's permissions. Omitting a separate daemon avoids another layer of IPC.
- There is no database. Conversations use JSONL files, settings use JSON files, and all state lives in the filesystem. This intentional simplification also makes backups straightforward.
- Chat streams over SSE (Server-Sent Events). SSE delivers each part of an AI response as it is generated. This is why the nginx configuration must disable
proxy_buffering; otherwise nginx would hold the response until generation finished and then deliver it all at once.
0.8.4, not @latest, through the Dockerfile setting ARG PI_WEB_VERSION=0.8.4. The nginx filters and shim depend on that release's _next chunk names and RSC prefetch behavior, so an upstream refactor can silently break Ingress. Review the matching nginx.conf before changing the pi-web version.Why v0.13.0 moved API keys into the pi-web UI
v0.13.0 removed the API-key fields from the add-on Configuration page. If you upgrade from an older release, those fields disappear; Chapter 21 covers the procedure. This section explains the architectural reason.
Old architecture (through v0.12.x):
-
Enter keys on the add-on Configuration page
The form contained fields such as
anthropic_api_key,openai_api_key, andglm_api_key. -
Supervisor writes
/data/options.jsonWhen you saved, Supervisor serialized the form as JSON inside the container at
/data/options.json. -
The startup script exports environment variables
The s6-overlay startup script read
/data/options.json, converted each field into an environment variable such asANTHROPIC_API_KEY=..., and then executed pi-web. -
pi-web reads the environment
At startup, pi-web obtained the key from values such as
process.env.ANTHROPIC_API_KEY.
Problems with the old architecture:
- Adding a provider required a new
config.yamlschema and a new release; providers could not be added dynamically. - One provider could not have two keys, such as separate work and personal credentials, because each field held one value.
- There was no Test button. A bad key was not discovered until the first real request returned 401.
- Changing a key required restarting the add-on before the new environment variable took effect.
New architecture (v0.13.0 and later):
-
Open pi-web's Models panel
Provider configuration now lives in the browser interface, not on the add-on Configuration page.
-
Select Add Provider, complete the form, and select Test
Follow the procedure in Chapter 6. pi-web sends a real API request to validate the key and saves it only after a successful test.
-
pi-web writes
/data/pi-agent/models.jsonThis structured JSON supports many providers, multiple keys per provider, and multiple models per key.
-
The setting takes effect without an add-on restart
The next Session uses the new configuration because pi-web rereads
models.jsonwhenever it opens a Session.
Benefits of the new architecture:
- Providers, keys, and models can all be managed dynamically.
- The Test action validates credentials in advance.
- No add-on restart is required.
- The UI can enable only selected models for a provider.
Migration path—hard cut, no auto-migration. v0.13.0 does not seed old keys into models.json. Both CHANGELOG.md and DOCS.md explicitly say “No auto-import”: there is no automatic migration. After upgrading, re-enter every provider key in the pi-web Models panel and select Test. Supervisor discards the old fields in options.json under the new schema. Existing models.json placeholders such as $GLM_API_KEY resolve to empty strings, so requests return 401 until you supply the keys again. This is the expected transitional state, not an automatic migration in progress.
Advanced debugging inside the container
The add-on Logs tab is enough for routine debugging. For deeper investigation, you may need to enter the container itself. These are the most useful techniques:
-
Enable debug logging
On the add-on Configuration page, change
log_levelfrominfotodebug. Appendix A describes the levels. Saving restarts the add-on, after which the Logs tab becomes much more verbose. Return the setting toinfowhen you finish. -
Discover and enter the add-on container
Install SSH & Web Terminal or use Advanced SSH. First list the running containers and capture the exact Pi Agent name reported by your host. Repository hashes and slugs vary, so never hard-code a Docker or Podman container name:
CONTAINER_NAME="$(docker ps --format '{{.Names}}' | grep pi_agent | head -n 1)" printf '%s\n' "$CONTAINER_NAME" docker exec -it "$CONTAINER_NAME" bashConfirm that the printed value is the intended Pi Agent container before executing the final command. Once inside, use
ls /data/pi-agent,cat /etc/nginx/nginx.conf, andps auxto inspect its state. Treat the container filesystem as read-only unless you deliberately need to change persistent content under/data. Changes elsewhere disappear on restart. -
Inspect the nginx access log
Inside the container:
tail -f /var/log/nginx/access.log
This shows each request's URL, status code, and response time. It is the quickest way to investigate whether HA is sending the expected
X-Ingress-Pathheader. -
Test the edge-tts connection to Microsoft
Inside the container:
/data/pi-agent/venv/bin/python -m edge_tts --list-voices | head
If this lists voices, the network connection works and the virtual environment is intact. If it does not, treat the video-tool installation as incomplete and use the reset procedure in Chapter 18.
-
Test rclone access to Google Drive
Inside the container:
rclone --config /data/pi-agent/rclone/rclone.conf ls gdrive: | head
If this lists Google Drive content, the authorization still works. An error such as
token expiredmeans you need to runrclone config reconnect, as described at the end of Chapter 19.
docker exec rather than writing to the HA host filesystem. Run exit when you finish.Architecture-level troubleshooting
This section does not repeat the user-facing symptoms in Chapter 22. It covers cases that require an understanding of the architecture:
-
You cannot find
/data/pi-agent/on the HA hostSymptom: you want to use File Editor or SSH to
vimodels.jsonon the HA host, but cannot find the path.
Cause: an add-on's internal/datais not mounted as host/data. The add-on source specifies only a per-add-on mount and does not confirm a host path. Candidates include/mnt/data/supervisor/addon_configs/<slug>/on HA OS and/usr/share/hassio/addon_configs/<slug>/on Supervised installations, but Supervisor controls this location and it can change.
Solution: if you must work from the host, uselsto check both candidate directories. A safer approach is to discover the container name first. Do not copydocker exec -it addon_<hash>_woow_ha_pi_agent bashliterally; use the discovery procedure above, verify the printed name, enter that container, and thenvi /data/pi-agent/models.json. Its internal/datais the add-on's mounted storage. An add-on restart is not normally required because pi-web rereads the file, although restarting after a consequential manual edit is safer. -
Ingress fails: nginx repeatedly returns 403
Symptom: selecting Pi Agent in the sidebar produces a blank page or nginx's 403 page.
Cause: nginx's allowlist rejected theX-Ingress-Pathheader. An HA update may have changed the format—for example, by adding a character not covered by_—or HA may not be sending the header.
Solution: inspecttail -f /var/log/nginx/access.loginside the container. If the observed format has clearly changed, open an issue in Woow_ha_pi_agent_add_on and include the relevant details. As a temporary measure, restart HA so Ingress generates a new identifier. -
pi-web hangs: Watchdog keeps restarting the add-on
Symptom: the Logs tab shows
s6-rc-init startedevery few minutes, indicating repeated Supervisor restarts.
Cause: pi-web's/api/homeendpoint did not return 200 within 60 seconds. CPU exhaustion, insufficient memory, or a synchronous operation blocking Node's event loop may be responsible.
Solution: (a) check whether the HA host's CPU or RAM is saturated—Pi 3 hardware and older mini PCs are especially vulnerable; (b) temporarily disable Watchdog on the HA add-on page to observe whether pi-web recovers; (c) enablelog_level: debugand capture the final activity before each restart; and (d) if the cause remains unclear, open a GitHub issue with the logs. -
Video tools are incomplete even though the sentinel exists
Symptom: the video pipeline fails because edge-tts or Playwright cannot be found.
Cause: an earlier video-tools initialization was interrupted, leaving state that needs to be reset.
Solution: on the add-on Configuration page, setreset_video_tools: trueand save. The reset removes the sentinel so initialization can run again. This is a one-shot trigger: the add-on automatically returns the option to false after handling it, preventing a new 720 MB download at every start. See Chapter 18 for details. -
A Skill is installed, but the AI cannot see it
Symptom:
pi installreports success, but the AI never selects the Skill.
Cause: the Skill discovery process runs when a new Session starts. The existing Session's system prompt does not include the new Skill.
Solution: select “New Conversation.” If the Skill still does not appear, runls /data/pi-agent/skills/to confirm that its directory exists, thencat SKILL.mdand verify that the frontmatter contains bothnameanddescription. A YAML error, including incorrect indentation, can cause the entire Skill to be skipped.
Frequently asked questions
May I fork and modify the Pi Agent add-on?
Dockerfile label org.opencontainers.image.licenses="MIT"; the repository's LICENSE is authoritative. You may fork, modify, and publish it. Keep two points in mind: (1) upstream pi-web (agegr/pi-web, whose package.json says MIT) and pi-coding-agent (earendil-works/pi) are separate repositories; review each LICENSE before modifying its code. (2) Give your fork a distinct name, such as Woow_ha_pi_agent_myfork, rather than reusing the original woow_ha_pi_agent slug, which could confuse HA during upgrades.Can Pi Agent run with Home Assistant Container rather than Supervised or HA OS?
homeassistant/home-assistant—contains only Core and cannot install add-ons. If you use Home Assistant Container and want Pi Agent, run Woow_podman_pi_agent_package instead. It provides an equivalent Podman/Docker deployment without the HA add-on system.Are there K3s or Podman versions for Kubernetes or other hosts?
- Woow_ha_pi_agent_add_on—the HA add-on covered by this guide.
- Woow_k3s_pi_agent_package—a K3s (lightweight Kubernetes) deployment.
- Woow_podman_pi_agent_package—a Podman/Docker deployment.
Where should I contribute changes to pi-web or the agent?
- UI components, the chat interface, and the Models and Skills panels: github.com/agegr/pi-web, the Next.js application.
- AI logic, tool calls, Skill invocation, and streaming behavior: github.com/earendil-works/pi, the pi coding agent SDK.
- HA add-on packaging—Dockerfile, nginx.conf, s6-overlay services, and config.yaml: github.com/WOOWTECH/Woow_ha_pi_agent_add_on.
Why is panel_admin: true? Can a non-admin HA account use Pi Agent?
config.yaml sets panel_admin: true, so only HA administrator accounts can see the sidebar entry. This is deliberate: Pi Agent holds API keys and conversation history, and its AI can execute tools. Giving an ordinary household account access creates security and billing risks. If you choose to allow non-admin accounts, change config.yaml by setting panel_admin to false, but understand the consequences: shared credentials, mixed conversation histories, and unintended API spending. A compromise is to create a separate administrator account for each trusted person so actions remain attributable.Does Pi Agent send my conversations to the cloud?
sessions/*.jsonl on the HA disk, and no WOOWTECH telemetry uploads them. Generating an AI response still contacts the provider you selected. Messages sent to OpenAI, Anthropic, GLM, DeepSeek, or another cloud provider are governed by that provider's privacy policy. For a 100% local setup, configure a local LLM, such as Ollama or llama.cpp, as the provider. This choice is one benefit of the BYOK architecture.Can I run two Pi Agent add-ons, one for work and one for home?
Will an upgrade, such as v0.12 to v0.14, delete my data?
/data. Sessions, models.json, and Skills remain. The v0.13 key transition described in Chapter 21 changed the configuration structure, but it is a hard cut: API keys do not migrate automatically and must be re-entered in Models. Persistent data is removed only when you explicitly delete it, for example by uninstalling and reinstalling the add-on. The reset_video_tools option resets only reproducible video-tool state and automatically returns to false after the one-shot reset.