- TypeScript 47%
- Svelte 20.5%
- HTML 10.1%
- Python 9.9%
- Shell 7%
- Other 5.5%
| .forgejo/workflows | ||
| .opencode/skills/manage-todos | ||
| agent | ||
| backend | ||
| data/images | ||
| device-setup | ||
| docs | ||
| firmware | ||
| infra | ||
| scripts | ||
| shared | ||
| web-admin | ||
| web-device | ||
| web-student | ||
| web-www | ||
| web-www-content | ||
| .dockerignore | ||
| .gitignore | ||
| AGENTS.md | ||
| CHANGELOG.md | ||
| CLA.md | ||
| cliff.toml | ||
| CONTRIBUTING.md | ||
| decisions-log.md | ||
| install-device.sh | ||
| install-server.sh | ||
| install.sh | ||
| LICENSE | ||
| package.json | ||
| pnpm-lock.yaml | ||
| pnpm-workspace.yaml | ||
| README.md | ||
| uninstall-device.sh | ||
| uninstall-server.sh | ||
DIY-Avatar
A self-hosted telepresence device that lets a long-term-sick child attend a school class remotely. An old laptop in the classroom streams audio + video to the child's PWA; the child can speak, show their face, raise a hand (physical LED signal), or send text back. Power-on / power-off is the only action school staff need to perform. Everything is self-hosted on a single EU VPS — no recording, no third-party media, transport-encrypted SRTP/DTLS end-to-end.
Install
Two one-shot installers cover the entire bring-up. Both idempotent; re-run any time to update. Updates run fully unattended (no prompts).
# Relay VPS (Debian 12 or Ubuntu 22.04+):
curl -fsSL https://git.buss-labs.de/public/diy-avatar/raw/branch/LIVE/install-server.sh | sudo bash
# Classroom device (Debian 12 / 13):
curl -fsSL https://git.buss-labs.de/public/diy-avatar/raw/branch/LIVE/install-device.sh | sudo bash
Release channels
The installers track three git branches as release channels:
| Channel | Branch | Use for |
|---|---|---|
LIVE |
LIVE |
production / stable (default) |
BETA |
BETA |
test releases |
master |
master |
edge / development |
On the first install the channel is taken from --channel=… /
DIY_AVATAR_CHANNEL, or interactively prompted (default LIVE), and persisted
to /etc/diy-avatar/channel. Every subsequent re-run reads that file and
sticks to the same channel — no prompts, no surprises.
Switch a device or server to a different channel:
sudo bash install-device.sh --channel=BETA
sudo bash install-server.sh --channel=BETA
Override the git ref for a single run without changing the saved channel:
sudo bash install-device.sh --ref=some-feature-branch
After a successful install/update the installers restart all services
automatically (local-agent + kiosk + timers on the device, all docker compose
app-profile containers on the server). Pass --no-restart to skip.
Full reference: docs/install/README.md.
Build your own kiosk device (fastest path)
The unattended installer ISO is the quickest way to turn an old laptop into a kiosk — no manual Debian install needed.
- Download the ISO for your laptop's CPU:
https://wiederdabei.de/kiosk/amd64.iso(x86-64 laptops)https://wiederdabei.de/kiosk/arm64.iso(ARM)
- Flash it to a USB stick (this wipes the USB stick — double-check
/dev/sdX):sudo dd if=diy-avatar-kiosk-amd64.iso of=/dev/sdX bs=4M status=progress oflag=sync - Boot the laptop from the USB. It wipes the WHOLE laptop disk and installs minimal Debian unattended. If no ethernet cable is detected it asks once for WiFi (ESSID + passphrase).
- After it reboots, set the
wiederdabeiadmin password when prompted on screen; it then installs the kiosk and reboots into it. - Finish: seed the device token + settings PIN — see
docs/device-setup/install.md§5.
Building your own ISO / CI details:
device-setup/os-install/README.md.
Documentation
| Audience | Doc |
|---|---|
| Developer / contributor | docs/dev-concept.md |
| Operator running the VPS + device (you) | docs/admin-manual.md |
| Child + parents (German) | docs/user-manual-student.md |
| Classroom teacher (German) | docs/user-manual-teacher.md |
| Teacher agreement form (German) | docs/teacher-agreement-form.md |
| Master design spec | docs/specs/2026-06-10-diy-avatar-design.md |
| Decisions log (D1–D37+) | decisions-log.md |
All runtime logs are aggregated into a single server log on the VPS under the
local project log/ dir (e.g. /opt/diy-avatar/log, per-device logs alongside
it); admins download them from
the Logs & archives page in web-admin — see
docs/admin-manual.md §10.
Admin: devices + rooms
web-admin manages multiple classroom devices and the rooms they serve:
- Device registry — list each device with live status, running version/branch, host OS, local PIN, and the current students-connected count. Mint a new device token (shown once) or revoke one, and run per-device actions: reboot, update, change-branch, and change-PIN.
- Rooms — each room belongs to a school and may hold one or more devices
(room 1→N devices), and is either public or restricted
(membership-gated). Students see only the rooms they may join and auto-connect
when exactly one is online, otherwise pick from a list. A default public
mainroom is seeded on first boot.
Per-component deep dives live under docs/{infra,backend,web-device,web-student,web-admin,web-www,agent,firmware,device-setup}/.
Per-phase status: docs/phase{2,3,4}-summary.md and
docs/testing/{phase1,phase2,phase4,cross-phase}-verification.md.
Repo layout
/infra docker compose stack (livekit + coturn + backend) + Caddy snippet
/backend Fastify + TypeScript + SQLite. Auth, tokens, admin API.
/shared TS types (data-channel + REST shapes), imported everywhere.
/web-device Svelte SPA — classroom kiosk page.
/web-student Svelte PWA — installable student app.
/web-admin Svelte SPA — admin dashboard.
/web-www Svelte public website, served at the apex wiederdabei.de.
/agent Python systemd-style service — LED + on-air truth + heartbeat.
/firmware PlatformIO ESP8266/ESP32 firmware (WS2812).
/device-setup Debian provisioning: install.sh + systemd units + udev.
/scripts env.sh + test-stack.sh + test-phaseN-integration.sh.
/docs this documentation.
Build + test
Always source the env helper first (it sandboxes HOME so pnpm and Node
caches go to writable paths):
source scripts/env.sh
Install + build + test everything:
pnpm install --frozen-lockfile
pnpm -r --if-present build
pnpm -r --if-present test
pnpm -r covers every workspace package, including the web apps
(web-device, web-student, web-admin, web-www). To build the frontend
nginx images (incl. the public website served at the apex):
cd infra
docker compose --profile app build web-www # or omit the name to build all
Compose smoke (LiveKit + coturn):
bash scripts/test-stack.sh
Full integration (brings the stack up, exercises auth + agent + admin round-trip, tears down):
bash scripts/test-phase1-integration.sh --manage
bash scripts/test-phase4-integration.sh --manage
Agent + firmware (outside pnpm):
agent/.venv/bin/python -m pytest -q # 70 cases
make -C firmware test # 23 Unity cases
End-to-end Playwright (in Docker — host Chromium SEGVs on the macOS sandbox):
bash web-device/scripts/e2e-docker.sh
bash web-student/scripts/e2e-docker.sh
bash web-admin/scripts/e2e-docker.sh
For one-command bring-up procedures see
docs/admin-manual.md (operator) and
docs/infra/bringup.md (infra-only).
License
Licensed under the GNU Affero General Public License v3.0 or later
(AGPL-3.0-or-later); see LICENSE. You are free to self-host. Any
modified or network-hosted version must publish its complete source under the
AGPL. A commercial license is available on request (dual-licensing) for
those who cannot or do not wish to comply with the AGPL.
Copyright © 2026 Buss IT-Labs GmbH.
Contributions are accepted under CONTRIBUTING.md and the
Contributor License Agreement in CLA.md. Repository:
https://git.buss-labs.de/public/diy-avatar.