Skip to content

fly-deployment — The container image is a projection of the repository model, and its smoke is its evidence

One canonical deployment object under .ai/repo/deployments/ describes the image; a typed Rust model parses and validates it; the Dockerfile, its ignore file, the GHCR image that docker-image.yml and release.yml build, its smoke derived from the route registry, the doctor checks, the documentation and the cockpit view are projections of that object and of the capability registry, each checked for drift. The published image runs as a non-root user, carries no secret and no developer path, answers live and ready in its smoke, and has measured size, cold-start and memory baselines. Re-scoped 2026-10-14 from a Fly.io deployment, which the owner disconnected on 2026-10-12.

ACTIVE 5 of 29 issues done

ready
9
blocked
15
active
0
verify
0
done
5
cancelled
9

Problem

Majordomus has no deployment at all: no container, no hosting provider, no way for anyone who is not standing in front of this checkout to see the layer it serves. The obvious repair is the wrong one. A Dockerfile and a fly.toml written by hand would be a second description of facts the repository already holds — the port the server binds, the routes it registers, the assets the site build produces, the binary the workspace names — and every one of those pairs would drift the first time one half changed. The repository already refuses that shape everywhere else: capabilities are declared once and projected into CLI, HTTP, MCP, OpenAPI, docs and the site, and generate --check fails when a projection is stale. A deployment must arrive the same way or not at all.

Outcome

One canonical deployment object under .ai/repo/deployments/ describes the image; a typed Rust model parses and validates it; the Dockerfile, its ignore file, the GHCR image that docker-image.yml and release.yml build, its smoke derived from the route registry, the doctor checks, the documentation and the cockpit view are projections of that object and of the capability registry, each checked for drift. The published image runs as a non-root user, carries no secret and no developer path, answers live and ready in its smoke, and has measured size, cold-start and memory baselines. Re-scoped 2026-10-14 from a Fly.io deployment, which the owner disconnected on 2026-10-12.

Current state

Measured 2026-10-14 on master e28daccd9b: .ai/repo/deployments/majordomus.yaml is the canonical object; deploy/Dockerfile and its ignore file are generated from it (Alpine, uid 10001, exec-form entrypoint); docker-image.yml builds and smoke-tests the image on pull requests and on master, and release.yml publishes it to GHCR; .github/actions/image-smoke checks the image runs as non-root and answers /api/v1/live and /api/v1/ready; deploy.list, deploy.get, deploy.check and deploy.observed are capabilities. Missing: a smoke derived from the route registry, a secret and developer-path scan of the image, an SBOM, size, cold-start and memory baselines, a documentation page. The object still carries region, machines and resources, which nothing reads since Fly.io was disconnected on 2026-10-12.

Desired state

The GHCR image is a projection of the canonical object like every other artifact: generated, checked for drift, smoke-tested from the route registry, scanned for secrets and developer paths, measured against recorded budgets, documented and shown in the cockpit. Whether region, machines and resources leave the deployment/v1 schema is decided and recorded.

Scope

  • .ai/repo/deployments
  • .ai/repo/adrs
  • .ai/repo/rules/project
  • .ai/repo/policy.yaml
  • apps/majordomus-cli/src/deploy
  • apps/majordomus-cli/src/capability/builtin
  • apps/majordomus-cli/src/http
  • deploy
  • share/kinds.yaml
  • share/allow
  • share/schemas
  • docs
  • site
  • test/cases
  • .github/workflows

Out of scope

  • Any dependency, in code, script, documentation, CI or default, on ~/dev/prismatic-platform
  • A provider abstraction covering hosts other than the one this repository deploys to
  • Kubernetes, Nomad, buildpacks, Nixpacks or a second container implementation
  • A persistent volume, a database or any stateful service, unless a measured requirement forces one
  • Mutating deployment operations exposed to unauthenticated HTTP or to the browser
  • Profile-guided optimisation

Acceptance criteria

  • One canonical deployment object is the only place the port, the region, the resources, the Machine count, the health routes, the build inputs and the budgets are stated
  • Every image artifact — the Dockerfile and its ignore file — is generated, marked as generated with its source and its regeneration command, and fails generate --check when edited by hand
  • The deployment reads are declared once in the capability registry and appear in the CLI, the HTTP API, MCP, OpenAPI and the generated documentation without a second registration
  • The smoke suite is derived from the route registry; no second list of endpoints exists anywhere
  • The production image runs as a non-root user, contains no build toolchain, no repository history, no secret and no absolute developer path
  • The published image answers live and ready in its smoke, run from outside the container on a free host port
  • Image size, binary size, build context size, cold start, resident memory and endpoint latency are measured, recorded as baselines and gated against unexplained regression
  • No credential is written to the repository, to a Docker layer, to a generated file, to the site or to a log, and the image is published with the workflow's own token
  • Every rule this milestone states is enforced by a check that CI executes

Validation

  • bash test/run.sh
  • bin/majordomus doctor
  • cargo test --workspace --all-features
  • cargo clippy --workspace --all-targets --all-features -- -D warnings
  • majordomus generate --check

Evidence required

  • canonical_object
  • typed_model
  • generated_projections
  • registry_derived_smoke
  • container_minimal
  • image_smoke
  • budgets_measured
  • secrets_isolated
  • rules_enforced

Risks

  • Generation can hide the Dockerfile from the people who must read it. The generated file has to stay idiomatic and reviewable; the source describes intent, the output describes mechanics, and a contributor who edits the output is told which file to change instead.
  • The route topology and the served surfaces are being rewritten on other branches under ADR 0013. A smoke suite pinned to today's paths would be stale on arrival, so it must read the route registry rather than a path list.
  • 256 MB is a hypothesis. It is held only as long as a measured run under that limit passes; the evidence, not the preference, decides the final number.
  • A deployment touches money. The first deploy must not leave two Machines running, and the Machine inventory is asserted rather than assumed.
  • The bootstrap credential comes from outside this repository. It may be used to obtain an authenticated session and nowhere else; anything else would make Majordomus depend on Prismatic Platform, which is forbidden.

Issues

Each one is an execution contract with its own acceptance criteria, validation command and required evidence. The wave column is the layer of the dependency graph it sits in.

issuestatuswavedepends ontitle
I0901 DONE 0 — The deployment description is a kind of the layer, not a provider file
I0902 DONE 1 I0901 The deployment object becomes typed Rust that refuses nonsense
I0903 DONE 2 I0902 Deployment reads are declared once and projected everywhere
I0904 DONE 2 I0902 The listen address is one fact with two projections
I0905 DONE 2 I0902 Liveness and readiness are registered capabilities, not router hacks
I0906 READY 3 I0904 Termination is graceful and proved against the real container
I0907 READY 2 I0902 The running process says what it is, from an allowlist
I0908 READY 2 I0902 A hosted process has no repository, and says so instead of failing
I0909 READY 3 I0902, I0904, I0905 The production image is a generated projection of the build specification
I0910 BLOCKED 4 I0909 The release profile is chosen by measurement, not by folklore
I0911 BLOCKED 4 I0909 A source change does not rebuild the world, and the context stays small
I0912 BLOCKED 4 I0909 The image serves the site the canonical pipeline produced
I0913 CANCELLED 3 I0902, I0903 The provider configuration is generated, and the cheap profile is declared
I0914 CANCELLED 4 I0903, I0909, I0913 One deployment core, one command, and a dry run that mutates nothing
I0915 CANCELLED 5 I0914 Deployment joins the diagnostics that already exist
I0916 BLOCKED 5 I0905, I0914 The smoke suite is derived from the route registry, never written twice
I0917 CANCELLED 5 I0914 A bad deployment can be undone deterministically
I0918 CANCELLED 2 I0902 The bootstrap credential is used once and belongs to nothing
I0919 BLOCKED 4 I0909 Nothing secret and nothing local leaves in the image
I0920 BLOCKED 4 I0909 The image's dependencies are known and its advisories are policy
I0921 BLOCKED 5 I0910, I0911 Size is measured first and budgeted second
I0922 CANCELLED 6 I0930 The memory hypothesis is tested, not asserted
I0923 BLOCKED 6 I0930 Cold start and endpoint cost are benchmark targets like everything else
I0924 CANCELLED 1 I0901 The decision is recorded where architecture decisions are recorded
I0925 BLOCKED 6 I0915 Every deployment rule is a check that runs
I0926 BLOCKED 4 I0903, I0913, I0924 The deployment documentation is generated and reachable
I0927 READY 3 I0903 The cockpit shows the deployment from the same data
I0928 BLOCKED 6 I0919, I0921, I0930 A pull request cannot merge a deployment that would not work
I0929 CANCELLED 8 I0918, I0931 Production deploys from CI with a token scoped to one application
I0930 BLOCKED 5 I0906, I0909, I0912 The real image runs under the real limits before anything is deployed
I0931 CANCELLED 7 I0913, I0914, I0916, I0917, I0918, I0922, I0930 The application exists, one machine runs it, and the URL answers
I0932 BLOCKED 9 I0920, I0923, I0925, I0926, I0927, I0928, I0929, I0931 The milestone is accepted on its evidence
I3244 READY 0 — The generated Dockerfile copies every path crate cargo metadata names, so a new workspace crate needs no edit of the image
I3245 READY 0 — The image build's cold and warm durations and its context size are read from docker-image runs, and the deployment records say what is built today
I3315 READY 0 — The image smoke completes an MCP initialize on /mcp and a WebSocket upgrade on /events, not only a ready probe
I3820 READY 0 — A deployment names the provider that hosts it, and the provider's files are generated from it
I3821 BLOCKED 1 I3820 The tree a provider receives is assembled from the deployment object, and master publishes it after green ci
I3822 BLOCKED 2 I3821 A deployment that names a provider is observed at its own URL, and is active only while it serves master

The graph

fly-deployment dependency graphfrom .ai/repo/project
flowchart LR
    I0901["I0901<br/>The deployment description is a kind of the layer, not a provider file"]:::done
    I0902["I0902<br/>The deployment object becomes typed Rust that refuses nonsense"]:::done
    I0903["I0903<br/>Deployment reads are declared once and projected everywhere"]:::done
    I0904["I0904<br/>The listen address is one fact with two projections"]:::done
    I0905["I0905<br/>Liveness and readiness are registered capabilities, not router hacks"]:::done
    I0906["I0906<br/>Termination is graceful and proved against the real container"]:::ready
    I0907["I0907<br/>The running process says what it is, from an allowlist"]:::ready
    I0908["I0908<br/>A hosted process has no repository, and says so instead of failing"]:::ready
    I0909["I0909<br/>The production image is a generated projection of the build specification"]:::ready
    I0910["I0910<br/>The release profile is chosen by measurement, not by folklore"]:::blocked
    I0911["I0911<br/>A source change does not rebuild the world, and the context stays small"]:::blocked
    I0912["I0912<br/>The image serves the site the canonical pipeline produced"]:::blocked
    I0913["I0913<br/>The provider configuration is generated, and the cheap profile is declared"]:::cancelled
    I0914["I0914<br/>One deployment core, one command, and a dry run that mutates nothing"]:::cancelled
    I0915["I0915<br/>Deployment joins the diagnostics that already exist"]:::cancelled
    I0916["I0916<br/>The smoke suite is derived from the route registry, never written twice"]:::blocked
    I0917["I0917<br/>A bad deployment can be undone deterministically"]:::cancelled
    I0918["I0918<br/>The bootstrap credential is used once and belongs to nothing"]:::cancelled
    I0919["I0919<br/>Nothing secret and nothing local leaves in the image"]:::blocked
    I0920["I0920<br/>The image's dependencies are known and its advisories are policy"]:::blocked
    I0921["I0921<br/>Size is measured first and budgeted second"]:::blocked
    I0922["I0922<br/>The memory hypothesis is tested, not asserted"]:::cancelled
    I0923["I0923<br/>Cold start and endpoint cost are benchmark targets like everything else"]:::blocked
    I0924["I0924<br/>The decision is recorded where architecture decisions are recorded"]:::cancelled
    I0925["I0925<br/>Every deployment rule is a check that runs"]:::blocked
    I0926["I0926<br/>The deployment documentation is generated and reachable"]:::blocked
    I0927["I0927<br/>The cockpit shows the deployment from the same data"]:::ready
    I0928["I0928<br/>A pull request cannot merge a deployment that would not work"]:::blocked
    I0929["I0929<br/>Production deploys from CI with a token scoped to one application"]:::cancelled
    I0930["I0930<br/>The real image runs under the real limits before anything is deployed"]:::blocked
    I0931["I0931<br/>The application exists, one machine runs it, and the URL answers"]:::cancelled
    I0932["I0932<br/>The milestone is accepted on its evidence"]:::blocked
    I3244["I3244<br/>The generated Dockerfile copies every path crate cargo metadata names, so a new workspace crate needs no edit of the image"]:::ready
    I3245["I3245<br/>The image build's cold and warm durations and its context size are read from docker-image runs, and the deployment records say what is built today"]:::ready
    I3315["I3315<br/>The image smoke completes an MCP initialize on /mcp and a WebSocket upgrade on /events, not only a ready probe"]:::ready
    I3820["I3820<br/>A deployment names the provider that hosts it, and the provider's files are generated from it"]:::ready
    I3821["I3821<br/>The tree a provider receives is assembled from the deployment object, and master publishes it after green ci"]:::blocked
    I3822["I3822<br/>A deployment that names a provider is observed at its own URL, and is active only while it serves master"]:::blocked
    I0901 --> I0902
    I0901 --> I0924
    I0902 --> I0903
    I0902 --> I0904
    I0902 --> I0905
    I0902 --> I0907
    I0902 --> I0908
    I0902 --> I0909
    I0902 --> I0913
    I0902 --> I0918
    I0903 --> I0913
    I0903 --> I0914
    I0903 --> I0926
    I0903 --> I0927
    I0904 --> I0906
    I0904 --> I0909
    I0905 --> I0909
    I0905 --> I0916
    I0906 --> I0930
    I0909 --> I0910
    I0909 --> I0911
    I0909 --> I0912
    I0909 --> I0914
    I0909 --> I0919
    I0909 --> I0920
    I0909 --> I0930
    I0910 --> I0921
    I0911 --> I0921
    I0912 --> I0930
    I0913 --> I0914
    I0913 --> I0926
    I0913 --> I0931
    I0914 --> I0915
    I0914 --> I0916
    I0914 --> I0917
    I0914 --> I0931
    I0915 --> I0925
    I0916 --> I0931
    I0917 --> I0931
    I0918 --> I0929
    I0918 --> I0931
    I0919 --> I0928
    I0920 --> I0932
    I0921 --> I0928
    I0922 --> I0931
    I0923 --> I0932
    I0924 --> I0926
    I0925 --> I0932
    I0926 --> I0932
    I0927 --> I0932
    I0928 --> I0932
    I0929 --> I0932
    I0930 --> I0922
    I0930 --> I0923
    I0930 --> I0928
    I0930 --> I0931
    I0931 --> I0929
    I0931 --> I0932
    I3820 --> I3821
    I3821 --> I3822
    classDef done stroke:#16a34a,fill:#dcfce7,color:#14532d,stroke-width:2px
    classDef active stroke:#2563eb,fill:#eff6ff,color:#1e3a8a,stroke-width:2px
    classDef verify stroke:#7c3aed,fill:#f5f3ff,color:#4c1d95,stroke-width:2px
    classDef ready stroke:#0891b2,fill:#ecfeff,color:#164e63,stroke-width:2px
    classDef planned stroke:#0891b2,fill:#ecfeff,color:#164e63,stroke-width:2px
    classDef blocked stroke:#b45309,fill:#fffbeb,color:#78350f,stroke-width:2px
    classDef cancelled stroke:#6b7280,fill:#f9fafb,color:#374151,stroke-width:2px
    classDef superseded stroke:#6b7280,fill:#f9fafb,color:#374151,stroke-width:2px

Canonical record: .ai/repo/project/milestones/fly-deployment.yaml. Read it back with majordomus plan show fly-deployment.

Searches the titles and descriptions of every page this site publishes.