this is a DIY OpenSource scool avatar system for sick kids to be participate the class
  • TypeScript 47%
  • Svelte 20.5%
  • HTML 10.1%
  • Python 9.9%
  • Shell 7%
  • Other 5.5%
Find a file
2026-06-13 22:34:22 +00:00
.forgejo/workflows feat(device-setup): unattended multi-arch kiosk installer ISO 2026-06-13 12:09:33 +02:00
.opencode/skills/manage-todos feat(skills): add manage-todos project skill 2026-06-13 18:38:16 +02:00
agent docs: add hierarchical AGENTS.md knowledge base 2026-06-13 23:26:22 +02:00
backend fix: demo account never hits session_limit 2026-06-14 00:33:05 +02:00
data/images feat(profile-image): store avatars as files, serve publicly by hash 2026-06-13 19:27:22 +02:00
device-setup fix(device-setup): parse multi-line xorriso el-torito opts for ISO repack 2026-06-13 12:43:27 +02:00
docs infra: rename env vars to backend canonical names 2026-06-13 19:54:55 +02:00
firmware ci: ship prebuilt firmware bins [skip ci] 2026-06-12 14:18:11 +00:00
infra docs: add hierarchical AGENTS.md knowledge base 2026-06-13 23:26:22 +02:00
scripts fix(infra): move web-student host port 8083 -> 8087 (8083 already in use) 2026-06-12 21:27:56 +02:00
shared Merge feat/diag-logs-filters: event select, autocomplete filters & row filter icons for logs 2026-06-13 23:47:24 +02:00
web-admin Merge feat/diag-logs-filters: event select, autocomplete filters & row filter icons for logs 2026-06-13 23:47:24 +02:00
web-device docs: add hierarchical AGENTS.md knowledge base 2026-06-13 23:26:22 +02:00
web-student feat(web-student): spinner + auto-close on enabling E2EE 2026-06-13 22:38:21 +02:00
web-www fix(web-www): fill legal/price placeholders, disable lang selector 2026-06-14 00:20:11 +02:00
web-www-content fix(web-www): fill legal/price placeholders, disable lang selector 2026-06-14 00:20:11 +02:00
.dockerignore fix(infra,docs): stop baking .env into images; document real E2EE 2026-06-13 12:07:53 +02:00
.gitignore fix(admin): make Server status honest + build-injected version 2026-06-13 19:59:25 +02:00
AGENTS.md fix(web-www): fill legal/price placeholders, disable lang selector 2026-06-14 00:20:11 +02:00
CHANGELOG.md chore(release): v0.30.1 [skip ci] 2026-06-13 22:34:22 +00:00
CLA.md chore(license): adopt AGPL-3.0 + commercial dual-license (LICENSE, CONTRIBUTING, CLA, README); record web-www routing decision 2026-06-12 18:05:50 +02:00
cliff.toml feat(release): semver tag releases + admin release management 2026-06-12 13:35:11 +02:00
CONTRIBUTING.md chore(license): adopt AGPL-3.0 + commercial dual-license (LICENSE, CONTRIBUTING, CLA, README); record web-www routing decision 2026-06-12 18:05:50 +02:00
decisions-log.md refactor: rename wave -> raise_hand (melden / raise hand) 2026-06-12 12:39:42 +02:00
install-device.sh feat(kiosk,student): UX fixes for text/hands-up overlays, avatar, version, boot 2026-06-13 11:06:16 +02:00
install-server.sh fix(infra): make data/images writable by backend app user 2026-06-13 20:49:27 +02:00
install.sh feat(install): release channels + unattended update + service restart 2026-06-11 23:16:44 +02:00
LICENSE chore(license): adopt AGPL-3.0 + commercial dual-license (LICENSE, CONTRIBUTING, CLA, README); record web-www routing decision 2026-06-12 18:05:50 +02:00
package.json rebrand: avatar-lahja -> diy-avatar 2026-06-11 12:52:13 +02:00
pnpm-lock.yaml feat(profile-image): store avatars as files, serve publicly by hash 2026-06-13 19:27:22 +02:00
pnpm-workspace.yaml merge: integrate origin/master (website/demo/shop/altcha) into student RBAC line; rename + model reconciled 2026-06-12 20:43:52 +02:00
README.md feat(device-setup): unattended multi-arch kiosk installer ISO 2026-06-13 12:09:33 +02:00
uninstall-device.sh installers: device + server + dispatcher + uninstallers 2026-06-11 14:54:20 +02:00
uninstall-server.sh infra(caddy): make 'caddy' docker network external + installer ensures it 2026-06-11 16:45:05 +02:00

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.

  1. Download the ISO for your laptop's CPU:
    • https://wiederdabei.de/kiosk/amd64.iso (x86-64 laptops)
    • https://wiederdabei.de/kiosk/arm64.iso (ARM)
  2. 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
    
  3. 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).
  4. After it reboots, set the wiederdabei admin password when prompted on screen; it then installs the kiosk and reboots into it.
  5. 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 (D1D37+) 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 main room 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.