ExApp development (any language)
An ExApp is a separate HTTP service, usually a container, that Nextcloud manages through AppAPI. Nothing in
the contract is Python-specific: implement three small lifecycle endpoints, validate three auth headers, and
package the image with an FRP tunnel, and any language works. This skill teaches that contract, ships a
runnable reference implementation, and gives you two iteration loops (a fast process-restart loop and a
production-like Docker loop).
How to work
- Read references/exapp-development.md in full: the contract, both loops,
the redeploy table, and the capability map.
- Read the reference app assets/minimal_exapp/: the entire contract in one
framework-free Python file plus the smallest correct Dockerfile. To develop in Go, Rust, Node or anything
else, port
main.py; for Python, use nc_py_api instead, which
implements all of it.
- Manifest questions (
info.xml, routes, --json-info, env allow-list, mounts, Kubernetes roles):
references/exapp-contract.md.
Facts that save hours
- Under a HaRP daemon (
HP_SHARED_KEY in the env) listen on the unix socket /tmp/exapp.sock; everywhere
else on TCP APP_HOST:APP_PORT.
- No init work? Do not implement
/init at all: AppAPI treats 404/501 as "no init needed". Otherwise return
200 immediately and report progress 0..100 in the background; 100 enables the app.
- Never validate the
AA-VERSION header strictly; the HaRP path rewrites it.
- Rebuilding an image does nothing by itself. Redeploy = bump
<version> AND <image-tag> (keep them equal),
then app_api:app:update --info-xml; same-version updates are a hard no-op.
- A local-only image needs the daemon registry mapping
--registry-from <registry> --registry-to local, or
the deploy aborts at pull. Use a fictional registry (the example uses example.local).
- "Heartbeat check failed" after minutes with the container running almost always means the image lacks the
frpc/start.sh tunnel or the app listens on TCP instead of the socket.
Files
- references/exapp-development.md: contract, loops, redeploy semantics,
capability map, troubleshooting.
- references/exapp-contract.md: the
<external-app> manifest reference.
- references/known-exapps.md: real ExApps, examples (including Go) and wrapper
libraries to read.
- assets/minimal_exapp/: runnable reference ExApp (main.py, Dockerfile, start.sh,
appinfo/info.xml, Makefile with both loops).
- Environment to run all this in: nextcloud-dev-setup. Changing an app that
is already installed somewhere: exapp-maintenance.
1---2name: exapp-development3description: Builds a Nextcloud External App (ExApp) in any programming language against the raw AppAPI contract: heartbeat, init and enabled endpoints, authentication headers in both directions, the info.xml manifest, Docker packaging with an FRP tunnel for HaRP, and the two develop-and-redeploy loops. Use when creating a new ExApp, porting an existing service to Nextcloud, or debugging why an ExApp fails to deploy, authenticate, or serve its routes.4license: AGPL-3.0-or-later5---67# ExApp development (any language)89An ExApp is a separate HTTP service, usually a container, that Nextcloud manages through AppAPI. Nothing in10the contract is Python-specific: implement three small lifecycle endpoints, validate three auth headers, and11package the image with an FRP tunnel, and any language works. This skill teaches that contract, ships a12runnable reference implementation, and gives you two iteration loops (a fast process-restart loop and a13production-like Docker loop).1415## How to work16171. Read [references/exapp-development.md](references/exapp-development.md) in full: the contract, both loops,18 the redeploy table, and the capability map.192. Read the reference app [assets/minimal_exapp/](assets/minimal_exapp/): the entire contract in one20 framework-free Python file plus the smallest correct Dockerfile. To develop in Go, Rust, Node or anything21 else, port `main.py`; for Python, use [nc_py_api](https://github.com/cloud-py-api/nc_py_api) instead, which22 implements all of it.233. Manifest questions (`info.xml`, routes, `--json-info`, env allow-list, mounts, Kubernetes roles):24 [references/exapp-contract.md](references/exapp-contract.md).2526## Facts that save hours2728- Under a HaRP daemon (`HP_SHARED_KEY` in the env) listen on the unix socket `/tmp/exapp.sock`; everywhere29 else on TCP `APP_HOST:APP_PORT`.30- No init work? Do not implement `/init` at all: AppAPI treats 404/501 as "no init needed". Otherwise return31 200 immediately and report progress 0..100 in the background; 100 enables the app.32- Never validate the `AA-VERSION` header strictly; the HaRP path rewrites it.33- Rebuilding an image does nothing by itself. Redeploy = bump `<version>` AND `<image-tag>` (keep them equal),34 then `app_api:app:update --info-xml`; same-version updates are a hard no-op.35- A local-only image needs the daemon registry mapping `--registry-from <registry> --registry-to local`, or36 the deploy aborts at pull. Use a fictional registry (the example uses `example.local`).37- "Heartbeat check failed" after minutes with the container running almost always means the image lacks the38 frpc/start.sh tunnel or the app listens on TCP instead of the socket.3940## Files4142- [references/exapp-development.md](references/exapp-development.md): contract, loops, redeploy semantics,43 capability map, troubleshooting.44- [references/exapp-contract.md](references/exapp-contract.md): the `<external-app>` manifest reference.45- [references/known-exapps.md](references/known-exapps.md): real ExApps, examples (including Go) and wrapper46 libraries to read.47- [assets/minimal_exapp/](assets/minimal_exapp/): runnable reference ExApp (main.py, Dockerfile, start.sh,48 appinfo/info.xml, Makefile with both loops).49- Environment to run all this in: [nextcloud-dev-setup](../nextcloud-dev-setup/SKILL.md). Changing an app that50 is already installed somewhere: [exapp-maintenance](../exapp-maintenance/SKILL.md).