Server CLI Tool für das Deployment und Infrastrukturmanagement
  • TypeScript 98.5%
  • Shell 1.5%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
oliver.maklott 5e3436ab19
All checks were successful
check / check (pull_request) Successful in 7s
check / check (push) Successful in 7s
release / release (push) Successful in 14s
Version 0.10.2 for platform 0.13.0: restart and logs of the replay service
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 16:48:26 +00:00
.forgejo CI: check on ubuntu-latest-2, release stays on ubuntu-latest 2026-10-07 20:54:11 +00:00
.githooks B08: apcmd, the operator CLI for the server 2026-09-26 21:46:29 +00:00
docs No check of the bundle version against apcmd's own version: since apcmd has its own numbers (aivopod-app D178) every apcmd warned about platform 0.11.0 and self-update could not help (D018); 0.10.1 2026-10-09 19:40:56 +00:00
scripts Release plan: a release candidate never replaces a stable latest (aivopod-app D136) 2026-10-04 08:04:54 +00:00
src replay is a service of the stack: restart and logs take it (aivopod-app Plan 12, G6, D204) 2026-10-10 14:17:10 +00:00
tests replay is a service of the stack: restart and logs take it (aivopod-app Plan 12, G6, D204) 2026-10-10 14:17:10 +00:00
.gitignore Add working rules 2026-09-26 21:46:28 +00:00
CLAUDE.md B26: point to the SPEC instead of Plan 2 2026-09-27 18:00:16 +00:00
deno.json Version 0.10.2 for platform 0.13.0: restart and logs of the replay service 2026-10-10 16:48:26 +00:00
deno.lock Snapshots for disaster recovery: apcmd snapshot create|list|restore put the installation directory, a database dump and the Docker volumes (all but pgdata) of a running instance into one file, each part encrypted with age in the backup container, with a plain snapshot.json of versions and SHA-256 sums; restore checks every sum and the key before it changes anything, also on a new host without an installation, and replaces an instance still there only with --replace (D015); 0.9.1-rc.1 2026-10-06 14:16:04 +00:00
install.sh Questions, help and progress: (j/N), --help per command, live docker output 2026-09-29 09:23:03 +00:00
main.ts Web server modes: install --proxy, configure, proxy-config, port check, doctor; 0.6.0-rc.4 (aivopod-app Plan 7, G03) 2026-10-02 18:16:15 +00:00
README.md Which instance: --instance <name>, apcmd use <name> (remembered in the registry), the only instance by itself; with several and none chosen a command stops and names them, and every command names the instance it acts on; apcmd school delete shows what goes, advises a backup and asks for the school's name (D017, aivopod-app D184); 0.10.0 2026-10-09 11:48:37 +00:00

apcmd

Betrieb von Aivopod auf dem Server: Installation, Start und Stopp, Aktualisierung mit Rückkehr bei Fehlern, Sicherungen, Diagnose. Eine einzelne ausführbare Datei (deno compile) für Linux x86_64 und aarch64; auf dem Server brauchen Sie nur Docker mit Compose v2, kein Node und kein Deno.

Installation

curl -fsSL https://git.belowtoxic.cloud/api/packages/aivopod/generic/apcmd/latest/install.sh | sh

Das Skript lädt die passende Datei, prüft ihre Prüfsumme (auch gegen release-trust.json) und legt sie unter /usr/local/bin/apcmd ab. APCMD_VERSION wählt eine Version.

Erste Installation von Aivopod

sudo apcmd install --app-host app.schule.example.org --preview-host p.aivopod-preview.example \
  --tls admin@schule.example.org
sudo apcmd school create --name "BRG Beispiel"

install lädt das neueste Deploy-Bundle (oder --version, --bundle <zip>), prüft es, legt /opt/aivopod mit .env und erzeugten Geheimnissen an, zieht die Images per Digest, startet den Stack und prüft seine Gesundheit. Fehlende Angaben fragt es im Terminal ab (run. und assets. werden aus dem App-Hostnamen vorgeschlagen). Ohne --backup-recipient erzeugt es ein Schlüsselpaar für die Sicherungen; den privaten Schlüssel (backup-identity.key) sofort vom Server wegkopieren.

Jede Installation ist eine Instanz mit eigenem Namen (--name, sonst gefragt; Vorschlag aivopod, auf einem Rechner mit einer solchen Instanz aivopod-2). Der Name ist COMPOSE_PROJECT_NAME in .env und Präfix von Containern, Netz und Volumes; das Verzeichnis ist /opt/<name> (oder --dir). Einen Namen, den es auf dem Rechner schon gibt, lehnt install ab. apcmd list zeigt alle Instanzen. Welche ein Befehl nimmt: --dir, sonst --instance <name>, sonst AIVOPOD_HOME, sonst die mit apcmd use <name> gewählte, sonst die einzige. Mit mehreren und keiner gewählten bricht ein Befehl ab und nennt die Namen (D017).

Neben anderen Diensten: Hält auf dem Rechner schon ein Server 80 und 443 (nginx, Apache, Caddy …), installiert install --proxy hinter-proxy unseren Caddy auf 127.0.0.1:8080 (oder den nächsten freien Port) ohne TLS; der vorhandene Server macht TLS und leitet weiter. Unser Caddy setzt in beiden Modi die Sicherheits-Header. Vorlagen für nginx, Apache und Caddy liegen danach in proxy/ (apcmd proxy-config erzeugt sie neu); apcmd configure --proxy eigenstaendig|hinter-proxy wechselt den Modus einer bestehenden Instanz. Belegte Ports erkennt install und schlägt freie vor.

Anmeldung der Lehrpersonen (OIDC)

Lehrpersonen melden sich über den Anmeldedienst der Schule an (Keycloak, Entra ID, …). install zeigt die beiden URIs, die dort für einen neuen Client einzutragen sind, und fragt dann:

  • Issuer-URL des Anmeldedienstes, genau so, wie er sich selbst nennt (bei Keycloak https://<host>/realms/<realm>). install liest sein Discovery-Dokument und lehnt eine Adresse ab, unter der sich der Dienst anders nennt. Leer lassen: später mit apcmd oidc set.
  • Client-ID, und das Client-Secret. Ohne Secret ist Aivopod ein öffentlicher Client (nur PKCE); das muss der Anmeldedienst erlauben.

Die Scopes sind openid profile email. Ohne Terminal gelten --oidc-issuer, --oidc-client-id, --oidc-client-secret. apcmd oidc show zeigt die Werte (nie das Secret) und die URIs, apcmd oidc set ändert sie, prüft den Anmeldedienst und startet die API neu; apcmd doctor prüft die Anmeldung mit.

Schulen und Lehrpersonen

school create legt die Schule an (gibt es sie mit dem Namen schon, bleibt sie unverändert) und fragt, ob gleich eine Lehrperson dazukommen soll. Dafür erzeugt es einen einmaligen Anmeldelink (https://<App-Host>/claim#…), standardmäßig 72 Stunden gültig (--valid-hours, höchstens 720). Wer ihn zuerst öffnet und sich beim Anmeldedienst anmeldet, gehört danach als Eigentümer:in zur Schule. Niemand muss Issuer oder sub der Lehrperson kennen; der Name kommt aus --teacher-name oder vom Anmeldedienst. Wer sich über den Link anmeldet, wird manager (Leitung) der Schule; --role wählt eine andere Rolle, --admin macht zugleich zur Admin der ganzen Instanz. Weitere Lehrpersonen: apcmd teacher add, oder die Leitung selbst mit aivopod teacher add.

apcmd school list zeigt Schulen, Lehrpersonen mit ihren IDs und offene Links. Hat eine Lehrperson ein neues Konto (oder hat sich der Issuer geändert), erzeugt apcmd teacher link <id> einen Link, mit dem sie ihr neues Konto verknüpft; Kurse und Verlauf bleiben erhalten, das alte Konto gilt danach nicht mehr.

Den Link wie ein Passwort behandeln: persönlich oder über einen vertraulichen Kanal weitergeben. apcmd.log enthält ihn nicht. Für Skripte bindet school create --issuer … --sub … --teacher-name … eine bekannte Anmeldung direkt, ohne Link.

Befehle

Befehl Wirkung
apcmd install [--name] [--version] Bundle laden und prüfen, .env mit Geheimnissen, Name der Instanz, Hostnamen, Images per Digest, Datenbank und Rollen, Start
apcmd list Alle Instanzen auf diesem Rechner mit Verzeichnis, Version und Zustand
apcmd use [<name>] Instanz für die folgenden Befehle wählen; ohne Namen die gewählte zeigen
apcmd configure --proxy <modus> Webserver-Modus wechseln (eigenstaendig oder hinter-proxy), Ports prüfen, Stack neu starten
apcmd proxy-config Vorlagen für den vorgeschalteten Server in proxy/ neu erzeugen (nginx, Apache, Caddy)
apcmd up / down / restart [dienst] Stack steuern (down behält alle Daten)
apcmd status Dienste, Version, Gesundheit, nicht veröffentlichte Uploads, freier Speicher, letzte Sicherung
apcmd logs [dienst] [--follow] Logs
apcmd update [--version] Sicherung, neues Bundle prüfen, MIGRATION_NOTES.md zeigen, .env ergänzen, Images ziehen, Start (Migrationen), Gesundheitsprüfung; bei Fehler zurück zur vorherigen Version
apcmd rollback [--restore-db --identity <key>] Vorherige Version; die Datenbank nur mit --restore-db aus der Sicherung von vor dem Update
apcmd backup now / list / restore <datei> --identity <key> Sicherungen
apcmd snapshot create / list / restore <datei> --identity <key> [--replace] Abbild der laufenden Instanz für den Katastrophenfall: Verzeichnis mit .env, Datenbank-Abzug, Volumes, verschlüsselt in einer Datei; auf einem neuen Rechner wiederherstellen
apcmd oidc show / set Anmeldung der Lehrpersonen: Werte und URIs zeigen; Werte prüfen, in .env schreiben, API neu starten
apcmd school create / list Schule anlegen (vorhandene bleibt), auf Wunsch mit Anmeldelink; Schulen, Lehrpersonen und offene Links zeigen
apcmd school delete --school <name|id> [--yes] Schule mit allen Daten löschen; zeigt zuerst, was verschwindet, und fragt nach dem Namen (ab Plattform 0.11)
apcmd teacher add / link <id> Einmaliger Anmeldelink für eine neue Lehrperson; Link zum Verknüpfen einer Lehrperson mit einem neuen Konto
apcmd teacher promote / demote <id> --admin Admin der ganzen Instanz machen oder wieder entziehen (Rollen in den Schulen bleiben)
apcmd doctor DNS und TLS aller vier Hostnamen, Ports, Anmeldedienst (OIDC), Docker, Uhrzeit, Schreibrechte, .env
apcmd self-update apcmd selbst aktualisieren
apcmd version Versionen von apcmd, Bundle und Images

Hilfe zu jedem Befehl und Unterbefehl mit allen Optionen und Beispielen: apcmd <befehl> --help, z. B. apcmd school create --help oder apcmd backup --help.

Längere Befehle (install, update, rollback, backup restore) zeigen ihre Schritte nummeriert ([3/6] …) und darunter den Fortschritt von Docker. Fragen enden mit einem Doppelpunkt; ein Vorschlag steht in eckigen Klammern und wird mit Enter übernommen. Ja/Nein-Fragen zeigen (j/N), der Großbuchstabe ist die Vorgabe.

Alle Befehle: --dir (Standard /opt/aivopod oder AIVOPOD_HOME), --json (ein JSON-Objekt {ok, command, id, message, messages, data}). Exit-Code 0, 1 bei Fehlern, 2 bei falschem Aufruf.

Prüfung von Releases

Prüfsummen werden immer geprüft: die des Bundles, release.sha256 in release-trust.json und die Hashes aller Dateien darin. Signaturen gibt es erst mit v1; AIVOPOD_VERIFY_SIGNATURES steuert, was bis dahin passiert:

Wert Wirkung
off keine Meldung
warn (Standard) „Signatur nicht geprüft (erst ab v1 verfügbar)“, Eintrag in apcmd.log
enforce Abbruch mit diesem Hinweis

Entwicklung

deno task check      # fmt, lint, check, test
deno task compile    # dist/apcmd
git config core.hooksPath .githooks

Siehe CLAUDE.md und docs/ci.md.