Update deployment standards for MCP build lifecycle

Add [build] section to service definition format, document mcp build
and mcp sync auto-build, add Build and Release Workflow section.
Pin example image tags to explicit versions — :latest is no longer
permitted in service definitions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-26 23:31:07 -07:00
parent 75f5327364
commit 4b74221cff

View File

@@ -72,6 +72,7 @@ but the top-level skeleton is fixed.
│ ├── templates/ Go HTML templates │ ├── templates/ Go HTML templates
│ └── static/ CSS, JS (htmx) │ └── static/ CSS, JS (htmx)
├── deploy/ ├── deploy/
│ ├── <service>-rift.toml MCP service definition (reference)
│ ├── docker/ Docker Compose configuration │ ├── docker/ Docker Compose configuration
│ ├── examples/ Example config files │ ├── examples/ Example config files
│ ├── scripts/ Install, backup, migration scripts │ ├── scripts/ Install, backup, migration scripts
@@ -217,6 +218,11 @@ Services expose two synchronized API surfaces:
- Use `google.protobuf.Timestamp` for all time fields (not RFC 3339 strings). - Use `google.protobuf.Timestamp` for all time fields (not RFC 3339 strings).
- Run `buf lint` and `buf breaking` against master before merging proto - Run `buf lint` and `buf breaking` against master before merging proto
changes. changes.
- **Input validation**: gRPC handlers must validate input fields (non-empty
required strings, positive IDs, valid enum values) and return
`codes.InvalidArgument` with a descriptive message on failure. This mirrors
the validation that REST handlers perform and ensures both API surfaces
reject bad input consistently.
### REST (Secondary) ### REST (Secondary)
@@ -307,6 +313,9 @@ File permissions: `0600`. Created by the service on first run.
sequentially at startup. sequentially at startup.
- Each migration is idempotent — `CREATE TABLE IF NOT EXISTS`, - Each migration is idempotent — `CREATE TABLE IF NOT EXISTS`,
`ALTER TABLE ... ADD COLUMN IF NOT EXISTS`. `ALTER TABLE ... ADD COLUMN IF NOT EXISTS`.
- Seed data migrations must use `INSERT OR IGNORE` (not plain `INSERT`)
to ensure idempotency when the migration runs against a database that
already contains the seed rows.
- Applied migrations are tracked in a `schema_migrations` table. - Applied migrations are tracked in a `schema_migrations` table.
- Never modify a migration that has been deployed. Add a new one. - Never modify a migration that has been deployed. Add a new one.
@@ -548,6 +557,20 @@ Multi-stage builds:
2. **Runtime**: `alpine:3.21`. Non-root user (`<service>`), minimal attack 2. **Runtime**: `alpine:3.21`. Non-root user (`<service>`), minimal attack
surface. surface.
Runtime images MUST include `ca-certificates` (for TLS verification) and
`tzdata` (for timezone-aware logging and scheduling):
```dockerfile
RUN apk add --no-cache ca-certificates tzdata \
&& addgroup -S <service> \
&& adduser -S -G <service> -h /srv/<service> -s /sbin/nologin <service> \
&& mkdir -p /srv/<service> && chown <service>:<service> /srv/<service>
```
The image should declare `VOLUME /srv/<service>` (to document the data
mount point) and `WORKDIR /srv/<service>` (so relative paths resolve
correctly).
If the service has separate API and web binaries, use separate Dockerfiles If the service has separate API and web binaries, use separate Dockerfiles
(`Dockerfile.api`, `Dockerfile.web`) and a `docker-compose.yml` that wires (`Dockerfile.api`, `Dockerfile.web`) and a `docker-compose.yml` that wires
them together with a shared data volume. them together with a shared data volume.
@@ -597,6 +620,136 @@ The web UI unit should use `ReadOnlyPaths=/srv/<service>` instead of
4. Install example config if none exists. 4. Install example config if none exists.
5. Install systemd units and reload the daemon. 5. Install systemd units and reload the daemon.
### Deployment with MCP
The Metacircular Control Plane (MCP) is the standard deployment tool for
container-based services. It manages container lifecycle on target nodes
using rootless Podman.
#### Service Definition Format
MCP service definitions are TOML files stored at
`~/.config/mcp/services/<service>.toml` on the operator workstation. Each
file defines a service with one or more container components:
```toml
name = "metacrypt"
node = "rift"
active = true
path = "metacrypt"
[build]
uses_mcdsl = false
[build.images]
metacrypt = "Dockerfile.api"
metacrypt-web = "Dockerfile.web"
[[components]]
name = "api"
image = "mcr.svc.mcp.metacircular.net:8443/metacrypt:v1.0.0"
network = "mcpnet"
user = "0:0"
restart = "unless-stopped"
ports = ["127.0.0.1:18443:8443", "127.0.0.1:19443:9443"]
volumes = ["/srv/metacrypt:/srv/metacrypt"]
cmd = ["server", "--config", "/srv/metacrypt/metacrypt.toml"]
```
Top-level fields:
| Field | Purpose |
|-------|---------|
| `name` | Service name (matches the project name) |
| `node` | Target host to deploy to |
| `active` | Whether MCP should keep this service running |
| `path` | Source directory relative to the workspace (for builds) |
Build fields:
| Field | Purpose |
|-------|---------|
| `build.uses_mcdsl` | Whether the build requires the mcdsl module |
| `build.images.<name>` | Maps image name to its Dockerfile path |
Component fields:
| Field | Purpose |
|-------|---------|
| `name` | Component name within the service (e.g. `api`, `web`) |
| `image` | Full image reference including MCR registry and version tag |
| `network` | Podman network to attach to |
| `user` | Container user:group |
| `restart` | Restart policy |
| `ports` | Host-to-container port mappings |
| `volumes` | Host-to-container volume mounts |
| `cmd` | Command and arguments passed to the entrypoint |
#### Convention
Projects should include a reference service definition in
`deploy/<service>-rift.toml` as the canonical deployment example. This
file is committed to the repository and kept in sync with the actual
deployment.
#### Key Commands
| Command | Purpose |
|---------|---------|
| `mcp build <service>` | Build and push images for a service |
| `mcp sync` | Push all service definitions to agents; builds missing images if source tree is available |
| `mcp deploy <service>` | Pull image and (re)create containers |
| `mcp stop <service>` | Stop running containers |
| `mcp restart <service>` | Restart containers in place |
| `mcp ps` | List all managed containers and their status |
| `mcp status [service]` | Show detailed status for a specific service |
#### Container User Convention
All containers run as `--user 0:0` (root inside the container). Security
isolation is provided by rootless Podman (the container engine runs as an
unprivileged host user) combined with systemd hardening on the host. This
avoids file permission issues with mounted volumes while maintaining a
strong security boundary at the host level.
#### Image Convention
Container images are pulled from the Metacircular Container Registry (MCR):
```
mcr.svc.mcp.metacircular.net:8443/<service>:<tag>
```
Tags follow semver. Service definitions MUST pin an explicit version tag
(e.g., `v1.1.0`), never `:latest`. This ensures deployments are
reproducible and `mcp status` shows the actual running version.
#### Build and Release Workflow
The standard workflow for releasing a service:
1. **Tag** the release in git (`git tag -a v1.1.0`).
2. **Build** the container images (`mcp build <service>`).
3. **Update** the service definition with the new version tag.
4. **Sync and deploy** (`mcp sync` or `mcp deploy <service>`).
`mcp build` reads the `[build]` section of the service definition to
locate Dockerfiles and the source tree. The workspace root is configured
in `~/.config/mcp/mcp.toml`:
```toml
[build]
workspace = "~/src/metacircular"
```
Each service's `path` field is relative to the workspace. For example,
`path = "mcr"` resolves to `~/src/metacircular/mcr`.
`mcp sync` checks whether each component's image tag exists in the
registry. If a tag is missing and the source tree is available, it
builds and pushes automatically. If the source tree is not available,
it fails with a clear error directing the operator to build first.
### TLS ### TLS
- **Minimum TLS version: 1.3.** No exceptions, no fallback cipher suites. - **Minimum TLS version: 1.3.** No exceptions, no fallback cipher suites.