docs: Phase 7 — update docs to reflect optional services migration

Email, calendar, and files are now optional store services, not always-on
builtins. Updated README, QUICKSTART, Wiki, and service-developer-guide to
reflect: dynamic nav, optional service install flow, correct egress
identifiers (wireguard_ext/default vs wireguard/cell_internet), removed
builtin/store distinction from manifest reference, 7 core containers.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-29 17:10:48 -04:00
parent 44d7e96f29
commit 10ac15d9fe
4 changed files with 145 additions and 108 deletions
+46 -28
View File
@@ -52,7 +52,7 @@ Browser / WireGuard peer
└── SetupManager first-run wizard state
```
All 12 service containers run on a Docker bridge network (`cell-network`, `172.20.0.0/16` default). Static IPs per container are defined in `docker-compose.yml`.
The 7 core containers run on a Docker bridge network (`cell-network`, `172.20.0.0/16` default). Static IPs per container are defined in `docker-compose.yml`. Installed optional services join the same network via their own compose projects, managed by `ServiceComposer`.
Runtime configuration lives in `config/api/cell_config.json`, managed by `ConfigManager`. All service managers read and write through `ConfigManager`, which validates and backs up automatically.
@@ -101,7 +101,7 @@ The wizard collects:
- **Cell name** — used for hostnames and DDNS subdomain (e.g. `myhome``myhome.pic.ngo`)
- **Domain mode** — determines TLS certificate source: `lan` (internal CA), `pic_ngo`, `cloudflare`, `duckdns`, `http01`
- **Timezone**
- **Initial services to enable**
- **Services to install** — optional services (email, calendar, files) to install after setup; each starts a background install via `ServiceStoreManager`
- **Admin password** — minimum 12 characters
On completion:
@@ -109,6 +109,7 @@ On completion:
2. Cell identity is written to `config/api/cell_config.json`
3. Caddy config is generated
4. If domain mode is `pic_ngo`, the cell registers `<name>.pic.ngo` with the DDNS service
5. Each selected service is installed in a background thread
Wizard endpoints: `GET/POST /api/setup/step`, `GET /api/setup/status`, `POST /api/setup/complete`.
@@ -182,17 +183,17 @@ DNS records, DHCP leases and reservations, NTP status, network connectivity test
Peer add/remove, key generation, QR code export, per-peer routing policy, WireGuard status.
### Email (`/api/email/`)
### Email (`/api/email/`) _(available when email service is installed)_
User account management, mailbox config, alias management, connectivity test.
User account management, mailbox config, alias management, connectivity test. Returns HTTP 404 when the email service is not installed (except `/api/email/status`).
### Calendar (`/api/calendar/`)
### Calendar (`/api/calendar/`) _(available when calendar service is installed)_
User, calendar, and contacts (CardDAV) management.
User, calendar, and contacts (CardDAV) management. Returns HTTP 404 when the calendar service is not installed (except `/api/calendar/status`).
### Files (`/api/files/`)
### Files (`/api/files/`) _(available when files service is installed)_
WebDAV user management, file upload/download/delete, folder management.
WebDAV user management, file upload/download/delete, folder management. Returns HTTP 404 when the files service is not installed (except `/api/files/status`).
### Routing (`/api/routing/`)
@@ -252,7 +253,7 @@ Supported providers: `pic_ngo`, `cloudflare`, `duckdns`, `noip`, `freedns`.
### Navigation
The left-hand navigation contains a **Services** group (previously labelled "Store"). Both admin and peer users see this group. It has three collapsible sub-items: **Email**, **Calendar**, and **Files**.
The left-hand navigation contains a **Services** group. Both admin and peer users see it. Sub-items for installed services (Email, Calendar, Files, etc.) are added dynamically: the UI fetches `GET /api/services/active` on load and after each install/uninstall. Services not yet installed do not appear in the nav.
Legacy paths redirect to their new canonical locations:
@@ -265,9 +266,13 @@ Legacy paths redirect to their new canonical locations:
### Services page (`/services`)
The top of the page shows a **Built-in** section with three cards — Email, Calendar, and Files. Each card has a **Manage** link that navigates to the corresponding sub-page.
A single unified catalog of all available services from the store index. Each card shows:
- Service name, description, version
- **Install** button (not installed) or **Uninstall** button (installed)
- **Open** link for installed services (navigates to the service sub-page)
- Running/stopped status dot for installed services
Below the Built-in section, the optional add-on store lists third-party services that can be installed or removed (see [Service Store (Add-ons)](#service-store-add-ons)).
The `pic-services-changed` custom DOM event is dispatched after install/uninstall, causing the nav to re-fetch active services immediately.
### Service sub-pages — admin view
@@ -276,19 +281,18 @@ Each sub-page at `/services/email`, `/services/calendar`, and `/services/files`
1. **Connection info** — hostnames, ports, and protocol details (e.g. IMAP/SMTP/Webmail, CalDAV/CardDAV, WebDAV/Filegator).
2. **Service status** — current running state fetched from the API.
3. **Users list** — accounts registered with that service.
4. **Inline config form** — editable fields for that service's settings:
- Email: ports and mail domain.
- Calendar: port and data directory.
- Files: ports, data directory, and per-user quota.
4. **Inline config form** — editable fields for that service's settings.
Config forms save automatically with an 800 ms debounce after the last change. Dirty state persists through navigation: if a user edits the form and navigates away before the debounce fires, clicking **Apply Now** on return still saves the pending changes.
If the service is not installed, the page shows a `ServiceNotInstalledBanner` with a link to the catalog for admins, or a "contact your admin" message for peer users. All non-status API routes for uninstalled services return HTTP 404.
Config forms save automatically with an 800 ms debounce after the last change.
### Service sub-pages — peer view
Peers access the same URLs without an admin gate. The peer view shows only:
Peers access the same URLs. The peer view shows only:
- Connection info (hostnames, ports, copy buttons).
- Personal credentials for that service (email address, CalDAV username, or WebDAV username), fetched from `/api/peer/*`.
- Personal credentials for that service, fetched from `/api/peer/*`.
The config form and users list are not shown to peers.
@@ -296,22 +300,36 @@ The config form and users list are not shown to peers.
The Email, Calendar, and Files configuration forms have been removed from the Settings page. Settings now covers: Identity, DDNS, Network (DNS/DHCP/NTP), WireGuard, Routing & Firewall, Vault & Trust, and Backup & Restore.
### API change
### Relevant API endpoints
`GET /api/config` now includes an `installed_services` field — a dict keyed by service ID — listing services currently installed on the cell.
| Method | Path | Description |
|---|---|---|
| GET | `/api/services/active` | List installed services with id, name, subdomain, capabilities |
| GET | `/api/config` | Full cell config, includes `installed_services` dict |
---
## Service Store (Add-ons)
`ServiceStoreManager` fetches a manifest index from `http://git.pic.ngo/roof/pic-services/raw/branch/main/index.json`. Each manifest declares:
- Container image
- Caddy routes (added to the Caddyfile)
- iptables rules
- Environment variables
- Health check endpoint
Email, calendar, and file storage are store services — not part of the core stack. All optional functionality ships through this mechanism.
`POST /api/store/install` pulls the image, writes the Caddy route, applies iptables rules, and starts the container. `POST /api/store/remove` reverses this.
`ServiceStoreManager` fetches a manifest index from `https://git.pic.ngo/roof/pic-services/raw/branch/main/index.json`. Each manifest (`schema_version: 3`) declares:
- Container image and compose template
- Caddy subdomain routes
- Capabilities: `has_subdomain`, `has_accounts`, `has_admin_config`, `has_storage`, `has_egress`
- Account provisioning interface (`accounts.manager`)
- Backup declarations (`backup.volumes`, `backup.config_paths`)
- Egress routing policy (`egress.allowed`)
- Per-peer connection info template (`peer_config_template`)
`POST /api/store/install` fetches the manifest and compose template, validates them, renders the template with PIC-specific variables (`${PIC_DOMAIN}`, `${PIC_DATA_DIR}`, etc.), writes a per-service compose file, and brings the containers up via `ServiceComposer`. Caddy routes and DNS entries are applied automatically.
`POST /api/store/remove` checks for dependent services, stops and removes containers, and regenerates Caddy.
**`ServiceComposer`** (`api/service_composer.py`) manages the per-service compose lifecycle independently of the main stack. Each service gets its own compose project at `data/services/<id>/docker-compose.yml`. On startup, `reapply_active_services()` brings up containers for all recorded installs.
See `docs/service-developer-guide.md` for the full manifest schema reference and submission process.
---
@@ -354,7 +372,7 @@ Routing uses fwmark and `ip rule` / `ip route` in separate routing tables. Confi
## Testing
```bash
make test # unit tests (pytest, ~1500 functions)
make test # unit tests (pytest, ~1900+ functions)
make test-coverage # coverage report in htmlcov/
```