Operations
This runbook documents the D-088 configuration and maintenance procedures, not a record of commands exercised by this page’s author. Configured means present in the cited source or deployment overlay; exercised evidence belongs on Status; blocked identifies a path unavailable in this configuration. Read Security before changing access, and Architecture for the browser/server split.
Inventory
The source baseline is the pinned OpenMAIC fork. Installed destinations below are the D-088 operator’s deployment inventory; the overlay filenames identify their maintained source, not proof of loaded configuration.
| Item | D-088 value | Source |
|---|---|---|
| Repository checkout | /home/loca/dev/maic | deploy/maic.service:11-14 |
| Pinned upstream commit | 8c6c8f68eecf60a3472d12f1d0098e055c4cd5a6 | .git/refs/heads/feat/maic-d088-showcase:1 at authoring; deploy/nginx/maic.loca.zone.conf:3 |
| Deployment branch | feat/maic-d088-showcase | .git/HEAD:1 |
| Image tag | maic-showcase:d088 | deploy/compose.yml:11 |
| Container name | maic | deploy/compose.yml:12 |
| Backend publication | 127.0.0.1:52300 → container port 3000 | deploy/compose.yml:19-20 |
| Application data bind | /home/loca/dev/maic/data → /app/data | deploy/compose.yml:21-22 |
| Systemd unit | maic.service, installed at /etc/systemd/system/maic.service | deploy/maic.service |
| Compose definition | /home/loca/dev/maic/deploy/compose.yml | deploy/maic.service:12-14 |
| Nginx vhost source | deploy/nginx/maic.loca.zone.conf | deploy/nginx/maic.loca.zone.conf |
| Nginx vhost installation | /etc/nginx/sites-available/maic.loca.zone; enabled symlink /etc/nginx/sites-enabled/maic.loca.zone | D-088 installation of deploy/nginx/maic.loca.zone.conf |
| Nginx rate-limit file | /etc/nginx/conf.d/maic-ratelimit.conf | deploy/nginx/maic-ratelimit.conf:1-4 |
| Gate credential file | /etc/nginx/maic.htpasswd | deploy/nginx/maic.loca.zone.conf:28-29 |
| Environment file | deploy/maic.env, supplied through Compose env_file | deploy/compose.yml:13-14; deploy/maic.env.example:1 |
| Wiki vault | /home/loca/dev/wikis/maic/content/ | D-088 companion vault for deploy/nginx/maic.loca.zone.conf:81-98 |
| Published wiki root | /home/loca/dev/wikis/maic/current | deploy/nginx/maic.loca.zone.conf:88 |
The runtime limits are 3 GiB memory, 3 CPUs, no-new-privileges:true, restart policy unless-stopped, and a 20-second stop grace period (deploy/compose.yml:23-28).
These runtime limits are separate from the larger, explicitly bounded image-build limits below (deploy/compose.yml:27-28; Dockerfile:20-27).
Configuration boundary
The environment template leaves its LLM keys and DEFAULT_MODEL empty and sets LOG_LEVEL=info, LOG_FORMAT=json (deploy/maic.env.example:22-34).
The deployed policy is no server provider credentials; visitors supply their own keys in settings, stored under browser settings-storage and sent through the server (deploy/maic.env.example:3-8; lib/store/settings.ts:944-946).
Blocked: headless POST /api/generate-classroom cannot generate here: it resolves the server model, falls back to OpenAI gpt-4o-mini, then throws for the missing key (lib/server/classroom-generation.ts:178; lib/server/resolve-model.ts:30; lib/ai/providers.ts:945-947).
Do not add a server key merely to make a health indicator look successful; capability flags report configuration presence, not successful provider calls (app/api/health/route.ts:11-20).
Service control
The unit runs as root as a oneshot wrapper, with RemainAfterExit=yes, requiring Docker and ordered after network-online (deploy/maic.service:3-11).
Its start and stop actions are Compose up -d and down; systemd startup/stop timeouts are 300/60 seconds (deploy/maic.service:12-16).
Use the systemd interface for routine control so the wrapper’s state remains consistent with maintenance actions (deploy/maic.service:8-14).
Start and stop
sudo systemctl start maic.service
sudo systemctl stop maic.service
Equivalent container operations, as specified by the unit (deploy/maic.service:12-13):
sudo docker compose -f /home/loca/dev/maic/deploy/compose.yml up -d
sudo docker compose -f /home/loca/dev/maic/deploy/compose.yml down
Restart and status
sudo systemctl restart maic.service
sudo systemctl status maic.service
sudo docker compose -f /home/loca/dev/maic/deploy/compose.yml ps
For the same stop/start lifecycle without systemd, run both Compose operations in order (deploy/maic.service:12-13):
sudo docker compose -f /home/loca/dev/maic/deploy/compose.yml down &&
sudo docker compose -f /home/loca/dev/maic/deploy/compose.yml up -d
systemctl reload maic.service instead invokes Compose restart; it restarts the existing container, not the down/up recreation used after rebuilding an image (deploy/maic.service:12-14).
The corresponding direct command is:
sudo docker compose -f /home/loca/dev/maic/deploy/compose.yml restart
An active oneshot wrapper is not a provider or classroom-generation acceptance result; inspect container state separately and consult Status for actual observations (deploy/maic.service:8-9; app/api/health/route.ts:11-20).
Logs
Application output is available through Docker; the configured logging driver is json-file, capped at 10m × 3 files per container (deploy/compose.yml:29-33; lib/logger.ts:28-44).
Systemd’s journal records the wrapper’s start/stop activity rather than replacing the container log (deploy/maic.service:12-14).
sudo docker logs maic
sudo journalctl -u maic.service
Rebuilding after a source change
First retain the currently accepted image under a distinct rollback tag and record its image ID, source revision, and lockfile digest; maic-showcase:d088 is a reusable tag, not an immutable release identifier (deploy/compose.yml:11).
Take a data backup before an upgrade that might change stored records; the server persists JSON and media beneath its mounted data directory (lib/server/classroom-storage.ts:6-7,76-78; deploy/compose.yml:21-22).
The following is the exact operator-supplied D-088 bounded build invocation, run from /home/loca/dev/maic; the tag matches Compose and the build uses the repository Dockerfile (deploy/compose.yml:11; Dockerfile:2-51).
It is a rebuild procedure here, not a claim that this page’s author ran it.
cd /home/loca/dev/maic
sudo -n env DOCKER_BUILDKIT=0 docker build --memory 8g --memory-swap 8g --cpuset-cpus 0-4 --pull=false -t maic-showcase:d088 .
sudo systemctl restart maic.service
The shared-host build policy for deploy/compose.yml is legacy builder, 8g memory, 8g memory-plus-swap ceiling, and CPUs 0–4; keep the operator-supplied command resource-bounded rather than substituting an unrestricted build.
The recorded build procedure for deploy/compose.yml deliberately disables BuildKit because that path does not enforce these legacy build resource flags; --pull=false is part of this procedure, not a content digest pin.
The image build performs native dependency installation, workspace postinstall builds, and Next compilation, so it is a heavy operation (Dockerfile:12-18,27; package.json:10).
Runtime uses Node 22 Alpine, pnpm 10.28.0 during build, and Next standalone node server.js as UID/GID 1001 (Dockerfile:2-5,30-51).
Recreation is required to use the newly tagged image; the unit’s restart performs its configured down/up lifecycle (deploy/compose.yml:11; deploy/maic.service:12-13).
Backup and restore
Server-side application state
The intended durable server application state is the single bind mount /home/loca/dev/maic/data, not the container filesystem (deploy/compose.yml:21-22; lib/server/classroom-storage.ts:6-7).
This scope describes normal storage; the unchecked classroom POST path is a containment defect, not an additional supported storage location (Security; app/api/classroom/route.ts:24-27; lib/server/classroom-storage.ts:77-78).
Relative path inside data/ | Meaning | Source |
|---|---|---|
classrooms/*.json | Persisted classroom records | lib/server/classroom-storage.ts:61-78 |
classrooms/<id>/media/ | Generated/downloaded image and video files | lib/server/classroom-media-generation.ts:74-75,123-125,155-158 |
classrooms/<id>/audio/ | Generated speech files | lib/server/classroom-media-generation.ts:209-210,241-253 |
classroom-jobs/*.json | Generation status, input summary, result/error | lib/server/classroom-job-store.ts:17-57,105-120 |
A classroom record has the top-level shape { id, stage, scenes, createdAt }; createdAt is an ISO timestamp, and JSON replacement uses a temporary sibling file followed by rename (lib/server/classroom-storage.ts:21-28,37-42,69-78).
The stage contains identity/name/timestamps and optional description/language/style/whiteboard; scenes contain identity, stage ID, type, order, content, and optional actions/whiteboards (lib/types/stage.ts:15-62).
No owner/account field is part of this server record (lib/server/classroom-storage.ts:37-42).
Job records retain only an input summary, not replayable generation input: requirement preview capped at 200 characters, language, PDF presence, PDF text length, and image count (lib/server/classroom-job-store.ts:27-33,48-56).
They also retain progress, timestamps, scene counters, result and error (lib/server/classroom-job-store.ts:17-42).
In-flight execution and per-job locks live in process memory; restoring job JSON does not resume interrupted generation (lib/server/classroom-job-runner.ts:11-49; lib/server/classroom-job-store.ts:59-76).
A running job with no update for 30 minutes is reported as failed on read; that computed status is not a durable replay mechanism (lib/server/classroom-job-store.ts:78-96,124-130).
Take a consistent backup
Schedule downtime, let visitors finish, and stop the service before archiving; normal writes span classroom JSON and separate media files (lib/server/classroom-storage.ts:76-78; lib/server/classroom-media-generation.ts:123-125,249-253).
Use a new archive name on each run; the date below is a concrete filename example, not an existing backup receipt.
The backup directory is outside both the data mount and the public wiki root (deploy/compose.yml:21-22; deploy/nginx/maic.loca.zone.conf:88).
sudo install -d -m 0700 /home/loca/backups/maic
sudo systemctl stop maic.service
sudo tar --numeric-owner -czpf /home/loca/backups/maic/data-20260916T000000Z.tar.gz -C /home/loca/dev/maic data
sudo systemctl start maic.service
Check each command before proceeding; a failed archive is not a backup, and service restart does not establish archive integrity. Inspect the archive listing and retain a checksum with the private backup record:
sudo tar -tzf /home/loca/backups/maic/data-20260916T000000Z.tar.gz
sudo sha256sum /home/loca/backups/maic/data-20260916T000000Z.tar.gz
This archive does not include deployment configuration, the Basic-auth credential file, or browser state; those are outside the data mount (deploy/compose.yml:13-22; deploy/nginx/maic.loca.zone.conf:29; lib/store/settings.ts:944-946).
Keep any separate credential recovery material private; never add it to the public wiki or source archive.
Restore a trusted archive
Choose an unused rescue-directory name, verify the saved checksum, and inspect the trusted archive before extraction.
Stop the service, preserve the current directory rather than merging old and new state, restore the archived data/, then start the service (deploy/maic.service:12-13; deploy/compose.yml:21-22).
sudo systemctl stop maic.service
sudo mv /home/loca/dev/maic/data /home/loca/dev/maic/data.before-restore
sudo tar --numeric-owner -xzpf /home/loca/backups/maic/data-20260916T000000Z.tar.gz -C /home/loca/dev/maic
sudo systemctl start maic.service
sudo docker compose -f /home/loca/dev/maic/deploy/compose.yml ps
Stop on any failed step; do not start with a partially restored directory.
Preserve archived ownership and permissions so the runtime UID/GID 1001 can write the bind mount (Dockerfile:40-47; lib/server/classroom-storage.ts:21-28).
Check a known restored classroom and its media without assuming that opening it is read-only: pending outlines/media can resume provider work (app/classroom/[id]/page.tsx:103-149).
Record the restoration outcome on Status; no restore drill is claimed by this runbook.
Browser-side state is a separate recovery boundary
Each visitor’s browser holds Dexie MAIC-Database, schema 8, with stages, scenes, audio, images, legacy snapshots, chats, playback state, outlines, media, and generated agents (lib/utils/database.ts:176-196,298-310).
Browser-generated classroom saves use IndexedDB; the recent-classroom list reads that browser’s stages, not a server catalogue (lib/store/stage.ts:249-264; lib/utils/stage-storage.ts:36-71,137-159).
A server data/ backup therefore does not back up these classrooms, chats, media, or browser credentials (deploy/compose.yml:21-22; lib/utils/database.ts:298-310; lib/store/settings.ts:944-946).
| Browser key(s) | Contents / source |
|---|---|
settings-storage | Provider configurations and keys, models, audio/media/search and UI settings; lib/store/settings.ts:28-149,421-477,944-946 |
user-profile-storage | Avatar, nickname, bio; lib/store/user-profile.ts:20-42 |
agent-registry-storage | Agent registry; lib/orchestration/registry/store.ts:203-257 |
theme, locale | Display preferences; lib/hooks/use-theme.tsx:24-26,53-55; lib/hooks/use-i18n.tsx:12-40 |
webSearchEnabled, generationLanguage, recentClassroomsOpen, requirementDraft | Homepage preferences/draft; app/page.tsx:52-54,80-82,94-105 |
quizDraft:${sceneId}, pblChatDraft | Quiz/PBL drafts; components/scene-renderers/quiz-view.tsx:694-701; components/scene-renderers/pbl/chat-panel.tsx:33-37 |
Session storage separately holds generationSession and generationParams, including generation context (app/page.tsx:254-295; app/generation-preview/page.tsx:714-724).
There is no working full-database export/import UI: the uncalled exportDatabase()/importDatabase() helpers cover only stages, scenes, chats and playback state, omitting other tables and settings (lib/utils/database.ts:345-381).
The visible export is PPTX or a PPTX/resources ZIP, not a full classroom backup (components/header.tsx:267-295; lib/export/use-export-pptx.ts:1133-1180).
Clear Local Cache is destructive: after confirmation it deletes the database, clears all origin localStorage/sessionStorage, and reloads (components/settings/general-settings.tsx:32-51; lib/utils/database.ts:340-342).
Do not suggest clearing that cache as a harmless troubleshooting step; server backups cannot undo those browser deletions (deploy/compose.yml:21-22; components/settings/general-settings.tsx:39-44).
Rollback and removal
For an application rollback, stop maic.service, change image: in deploy/compose.yml to the previously retained image tag, restore the matching data archive if necessary, then start the service (deploy/compose.yml:11,21-22; deploy/maic.service:12-13).
The direct stop equivalent is sudo docker compose -f /home/loca/dev/maic/deploy/compose.yml down; do not mistake Compose restart for image replacement (deploy/maic.service:12-14).
If no previous image was retained, its availability must be established before promising a rollback; the deployment overlay names only the current tag (deploy/compose.yml:11).
To remove the public deployment, stop the service and remove only the D-088 vhost and rate-limit installation, then test and reload nginx (deploy/nginx/maic.loca.zone.conf; deploy/nginx/maic-ratelimit.conf:1-4).
The vhost file contains both the app and companion wiki, so removing it removes both configured routes (deploy/nginx/maic.loca.zone.conf:20-119).
sudo systemctl stop maic.service
sudo rm /etc/nginx/sites-enabled/maic.loca.zone
sudo rm /etc/nginx/sites-available/maic.loca.zone
sudo rm /etc/nginx/conf.d/maic-ratelimit.conf
sudo nginx -t && sudo systemctl reload nginx
Keep the data archive and preserved directory until recovery/removal has been accepted; this procedure does not delete them (deploy/compose.yml:21-22).
Certificates are managed by Certbot automatic renewal through packaged certbot.timer/certbot.service, not a project cron (/usr/lib/systemd/system/certbot.timer:4-7; /usr/lib/systemd/system/certbot.service:5-7).
Both names use the maic.loca.zone certificate lineage and the configured ACME webroot is /var/www/html (deploy/nginx/maic.loca.zone.conf:75-78,101-114).
Full removal also removes this file’s ACME route; coordinate retirement of that certificate lineage rather than leaving an expected renewal dependent on a removed route (deploy/nginx/maic.loca.zone.conf:107-119).
Do not disable the shared Certbot timer to retire this one deployment (/usr/lib/systemd/system/certbot.service:7).
Upgrades and documentation discipline
- Record the exact upstream commit and the deployment-overlay revision separately; do not equate the moving deployment branch with the pinned upstream baseline (
.git/HEAD:1;deploy/nginx/maic.loca.zone.conf:3). - Record
sha256sum pnpm-lock.yamlfor each accepted upgrade; Docker installs with--frozen-lockfile(Dockerfile:15-18). - The authoring baseline lockfile SHA-256 is
80de967d9f41fccd34657d11e03c7a3b3edf2d2c2ab16c53f95cc6ded5c0df2f(entirepnpm-lock.yaml:1-18566, local checksum measurement). - Retain the previous image and backup before moving
maic-showcase:d088; the configured tag alone cannot reconstruct a prior image (deploy/compose.yml:11;Dockerfile:2,30). - Recheck the whole-vhost gate, loopback binding, request limits, and credential-free environment after changing the overlay (Security;
deploy/compose.yml:13-20;deploy/nginx/maic.loca.zone.conf:27-69;deploy/maic.env.example:22-30). - Keep publishing corresponding source for deployment modifications and preserve required notices; the network source-offer obligation is in AGPL §13, not satisfied by linking only an unrelated upstream revision (Licensing;
LICENSE:185-191,202-221,540-551). - After editing this vault, re-run the shared wiki build for
maicand verify the published pages; nginx servescurrent, not the Markdown source directory (deploy/nginx/maic.loca.zone.conf:88-98). - Keep execution receipts and acceptance evidence on Status; a documented command or configured capability is not an exercised result (
app/api/health/route.ts:11-20).