---
name: maluos-app-server
description: Set up a MaluOS application / web server: the MaluDB Business OS kernel on a MaluOS Server as the office. Choose domain, scheme and the first super-admin before first boot (or re-provision after), add DNS and a TLS reverse proxy, sign in, give the office a model key, mint desk enrolment codes, publish applications and agents to desks, back up. Use when the user wants to run the Business OS, create an office, host MaluOS web applications, enrol desks from the server side, or fix a FAILED maludb-os-core component.
---

# Installing a MaluOS application / web server

Human guide: https://maluos.com/howto/app-server/ . The server must already be a MaluOS
Server (`maluos-first-server` skill); the server image carries the Business OS component
(`maludb-os-core`, metapackage `maluos-business-os`) enabled by default.

## Decide before first boot

The component renders the kernel's loopback names, environment files and Apache sites from
three settings in `/etc/maluos/firstboot.yaml` under `components.maludb-os-core`:

| key | default | choose |
|---|---|---|
| `domain` | `maluos.local` | a name the person controls; the kernel serves `<domain>`, `www.`, `os.`, `app.` |
| `scheme` | `http` | `http` on a LAN; `https` behind a TLS reverse proxy (required when reachable from outside) |
| `admin_email`, `admin_name`, `admin_password` | `admin@<domain>`, generated | the first super-admin |

Other keys: `model_key`, `model_name`, `model_provider_id`, `anthropic_api_key`,
`openrouter_api_key`, `malumail_api_key`.

On the cloud image, write the file with cloud-init `write_files`. On an ISO install first boot
has already run with the defaults: edit the file and `sudo maluctl provision --force`. Every
step is idempotent; names, env files and Apache sites are re-rendered, databases and the
super-admin kept, migrations already applied are skipped.

## Procedure

### 1. Provisioning (host)

The component runs after vault, gateway and API server: memory database + MaluDB API token,
kernel database with all migrations (resumable ledger in `/var/lib/maluos/business-os`),
application roles, env files, Apache vhosts (`/etc/apache2/sites-available/maludb-os.conf`),
eight units, PHP API health check, first super-admin, first model registry row, cron, the
Installer and JEV agents. Minutes under KVM; up to 25 under emulation; the unit allows two hours.

```sh
journalctl -u maluos-firstboot -f
maluctl status                                   # component: maludb-os-core ok <scheme>://os.<domain>
curl -s http://127.0.0.1:8080/api/v1/health      # the kernel's PHP API, localhost only
sudo cat /var/lib/maluos/secrets/business-os/admin-password   # only when the person asks; never echo elsewhere
```

The password is also in the vault's `system` compartment as `business-os-admin-password`.

### 2. DNS and TLS

A records (or CNAMEs) for `<domain>`, `www.`, `os.`, `app.` to the server. With
`scheme: https`, a reverse proxy terminates TLS and forwards to Apache on port 80 with the Host
header preserved. Example Caddyfile:

```
office.example.com, www.office.example.com, os.office.example.com, app.office.example.com {
    reverse_proxy 192.0.2.10:80
}
```

Port 8080 (PHP API) stays on localhost. Verify: `https://app.<domain>/login` loads with a valid
certificate. On a LAN without DNS, hosts-file entries on the desks do the same job.

### 3. Sign in

`https://os.<domain>` (or `http://os.maluos.local`) with the admin email and password. Advise
changing a generated password after signing in.

### 4. Model for the office

The office's model endpoint is what enrolled desks (`--model office`) and the office's agents
use. The first model registry row is an Anthropic model that needs a key: set
`anthropic_api_key` (or `openrouter_api_key`) in the component configuration and re-provision,
or add it in the kernel's settings. The key stays on the office; desks never hold provider keys.
A local or shared GPU serves the office through the server's own gateway as on any MaluOS host.

### 5. Desks

Mint a one-time enrolment code for a person in **Settings → Desks** on the office, or:

```sh
cd /var/www && sudo -u www-data php bin/mint_desk_code.php --help
```

The desk's owner runs `sudo maluctl office enroll https://app.<domain> --code XXXX-XXXX`
(`maluos-desktop` skill, step 7). Verify in Settings → Desks: the desk with its owner, online.

### 6. Applications and agents to desks

Desks read `/api/v1/desks/apps` (the office's catalogue, from the kernel's applications table)
and `/api/v1/desks/agents`. State today: the kernel's default applications (Help Desk, Spaces,
HR, Projects) and the integration plugin are **not yet packaged** as MaluOS components; the
kernel installs them from git as upstream documents. MaluMail is part of the kernel in this
release. Say so rather than promising a one-command app install.

### 7. Backup and updates

```sh
sudo maluctl secret export /root/vault-$(date +%F).bundle
sudo -u postgres pg_dump maludb_os | gzip > /root/maludb_os-$(date +%F).sql.gz
sudo -u postgres pg_dump maludb    | gzip > /root/maludb-$(date +%F).sql.gz
sudo tar czf /root/business-os-secrets-$(date +%F).tgz /var/lib/maluos/secrets/business-os
maluctl update status && sudo maluctl update apply -y
```

`scripts/verify.sh` on the host checks the result.

## Stop and ask before

- re-provisioning (`provision --force`) on a live office: Apache sites are re-rendered and the
  units restarted;
- changing the domain after desks have enrolled: their enrolment URL changes;
- entering a provider key (the person types it; keep it out of the transcript);
- exposing ports 80/443 beyond the LAN without TLS.

## Never

- print the admin password, a desk code or an API key into the conversation;
- change `scheme` without a proxy actually terminating TLS: the kernel's URLs would break;
- run kernel scripts in `/var/www` as root; use `sudo -u www-data` (the component uses
  `runuser -u www-data`);
- promise application packages that are not shipped (step 6).

## Troubleshooting map

| Symptom | Look at | Fix |
|---|---|---|
| `maludb-os-core FAILED` | `journalctl -u maluos-firstboot` (step named) | fix (network, PHP API health), `sudo maluctl provision --force` |
| login page redirects to the wrong name | `domain`/`scheme` vs the proxy | fix the file, re-provision |
| desk enrolment 4xx | code used/expired; URL not the `app.` name; port unreachable | mint a new code; check DNS and TLS |
| office agents have no model | step 4 | set the key |
