AkurAI Build
Menu

AkurAI-Build

public

Latest change 1b429348c988e59d4f46dcea44c8921db363b6ea - docs: define machine-readable suite production delivery policy by Ólafur Búi Ólafsson

# Suite production delivery policy

Defines the one machine-readable contract every active app in
`AkurAI-Framework/AKURAI_APPS.toml` must satisfy to deploy to production
through AkurAI Build. No second deploy engine: every rule below is checked
against the existing `.akurai.yml` schema (`src/config.rs::JobSpec`) and the
existing run lifecycle (`src/runner.rs`, `src/db.rs`), the same objects
AkurAI Build already parses and persists.

Scope: rules apply only to jobs that declare `environment: production`.
Non-production jobs (verify-only, PR CI, staging) are unaffected — this is
how `AkurAI-Build/.akurai.yml`'s own `verify`/`package` jobs stay unrestricted
while `deploy-production` is gated.

## Rule table

Each rule has a violation identifier (stable, for audit/error reporting),
the JobSpec/run fields it reads, and whether it is a **pre-queue/promotion**
check (static, evaluated by `config::parse` / `akurai_pipeline_validate`
before a run is queued or promoted) or a **runtime evidence** check
(evaluated from the persisted `Run`/job rows after execution — declaring
something in YAML is never accepted as proof it happened).

| ID | Requirement | Kind | Fields / evidence |
|---|---|---|---|
| SDP-001 `CANONICAL_GIT` | Queued/promoted commit is a full 40/64-hex object id that matches the hosted branch head returned by `akurai_repo_branches` at queue time. | pre-queue | `Runner::queue` commit param (`config::validate_commit`); caller must pass the exact SHA, not a floating ref, for any production-bound run. |
| SDP-002 `PIPELINE_ORDER` | Pipeline contains named jobs `verify`, `package`, and a production job (conventionally `deploy` or `deploy-production`) with `needs` forming `verify -> package -> deploy`. | pre-queue | `JobSpec.name`, `JobSpec.needs` graph from `config::parse`. |
| SDP-003 `IMMUTABLE_PACKAGE` | The `package` job is distinct from `verify` and the production job, and declares at least one `artifacts` glob. | pre-queue | `JobSpec.artifacts` non-empty on the job satisfying SDP-002's package role. |
| SDP-004 `SAME_ARTIFACT` | The production job `needs` the package job (already required by SDP-002) and its `run` text does not invoke a build command (`cargo build`, `bun build`, `npm run build`, `go build`, etc.) — it must consume the artifact `package` produced, never rebuild or ad hoc `cp`/`rsync` from an unrelated path. | pre-queue | `JobSpec.command` heuristic scan on the production job only. |
| SDP-005 `PROD_ENV_APPROVAL` | A job with `environment: production` also declares `approval: true`, unless it is the repository's own self-hosting pipeline explicitly documented as unattended (see exception below). | pre-queue | `JobSpec.environment`, `JobSpec.approval`. Extends the existing `config.rs` rule (approval requires *an* environment) to also require approval *when* the environment is exactly `production`. |
| SDP-006 `HOST_AGENT_SAFEGUARDS` | Production deploy commands hand off to a root-owned host agent (checksum verify, bounded restart, public health gate, automatic rollback) instead of mutating the running service in place. Concretely: the job's `run` text must reference one of the sanctioned handoff paths — `deploy/service-deploy.sh`, `ec2 release`, `ec2 deploy-binary`, or an app-owned `deploy.sh`/`scripts/provision-*.sh` that itself shells out to one of those. | pre-queue (pattern) + runtime (evidence) | Static: command text pattern match. Runtime: the persisted run's job log for the production job must show the handoff command actually executed and exit 0 — a job that merely printed the intent does not satisfy this rule. |
| SDP-007 `MIGRATION_RECOVERY` | If the app's `AKURAI_APPS.toml` entry declares `db_snapshot`, the production job's `run` text must take a backup (`.backup`/`cp`/`sqlite3 ... .backup`) and verify it (`PRAGMA integrity_check` or equivalent, non-empty size check) before mutating state. | pre-queue (pattern) + runtime (evidence) | Static: command text pattern match keyed off `AKURAI_APPS.toml[app].db_snapshot`. Runtime: backup file path/size appears in the job log. |
| SDP-008 `SECRET_REDACTION` | Jobs reference secrets only via the `secrets:` list (resolved from `AKURAI_SECRET_<NAME>`); no literal secret value may appear in `run` text, artifacts, or retained logs. | pre-queue + runtime | Static: `JobSpec.secrets` names are valid env names (already enforced); reject any `run` line containing a literal value matching a currently configured secret. Runtime: log redaction already strips exact secret values — audit that no unredacted match slips through. |
| SDP-009 `PERSISTED_SUCCESS` | A production deployment is "done" only when the run's persisted status is exactly `succeeded`. `queued`, `running`, `waiting`, `canceled`, `failed`, and "verify passed"/"package built" partial states are all incomplete. | runtime | `Run.status` in `db.rs`; `waiting` additionally requires an `akurai_run_promote` event before it can reach `succeeded`. |
| SDP-010 `POST_DEPLOY_VERIFY` | The production job's `run` text includes an app-specific behavior/health check (e.g. `curl .../api/health` asserting `status: ok`, or `scripts/validate.sh`) executed after the handoff step, tied to the commit that was just deployed. | pre-queue (pattern) + runtime (evidence) | Static: command text pattern match for a health/validate invocation after the deploy handoff. Runtime: that command's exit code in the job log. |

Exception to SDP-005: `AkurAI-Build/.akurai.yml`'s own `deploy-production` job
is intentionally unattended (`approval: false`) because the job cannot
promote itself — it runs inside the service it replaces and hands off
immediately to the host agent, which is the actual gate (checksum + health +
rollback). This is the one documented, allow-listed exception; a fleet audit
must call it out by name rather than silently ignoring it.

## Conforming example

`AkurAI-Build/.akurai.yml` already satisfies SDP-002/003/004/006/009/010 by
construction (see file for full text); its `deploy-production` job is the
canonical example of a production job that hands off to the host agent:

```yaml
- name: deploy-production
  needs: [package]
  executor: native
  branches: [main]
  environment: production
  run: H=${AKURAI_BUILD_HOME:-...}; AKURAI_HOST_BIN=$H/.local/bin sh deploy/service-deploy.sh
```

`deploy/service-deploy.sh` stages the artifact for `deploy/host/akurai-deploy-agent.sh`
(root-owned, systemd-triggered), which checksums, installs, restarts,
health-gates on `/api/health`, and rolls back to `$bin.previous` automatically
on failure — this is the SDP-006 reference implementation every other app's
deploy step should match or delegate to.

## Integration points (for the enforcement task)

No new engine or schema is introduced. Enforcement extends the existing
parse/queue/audit surfaces:

- `src/config.rs::parse` / `validate_job` — add SDP-002..005, 007, 008, 010
  as additional `ensure!` checks gated on `job.environment.as_deref() ==
  Some("production")`, returning a `{rule_id}: {detail}` message so callers
  can surface the exact violation identifier. Non-production jobs skip these
  checks entirely (preserves existing CI use).
- `src/mcp.rs::validate_pipeline` — already the pre-queue path
  (`akurai_pipeline_validate`); once `config::parse` enforces the rules above,
  this tool rejects non-conforming production pipelines before `akurai_run_queue`
  with no additional code.
- `src/runner.rs::queue` — already validates ref/commit (SDP-001); no change
  needed beyond ensuring the fleet-audit caller always passes an exact commit
  for production runs rather than a floating branch name.
- `src/db.rs` (`Run.status`, job log rows) — the source of truth for SDP-006/007/009/010
  runtime evidence; a new fleet-audit command reads `AKURAI_APPS.toml`, resolves
  each app's registered repository, calls the existing pipeline-validate and
  run-history queries, and reports per-app violations plus missing runtime
  evidence without adding a persistence layer.
- `AkurAI-Framework/AKURAI_APPS.toml` — existing `db_snapshot` field is the
  input SDP-007 keys off; no new field required.

## Baseline findings (13 registered apps)

Reviewed each app's registered `.akurai.yml` against the rule table above.
"Needs verification" means the referenced `deploy.sh`/script content was not
inspected line-by-line in this pass — the fleet-audit command (t_8eac946c)
should confirm SDP-006/010 runtime evidence, not just the static pattern.

| App | 002 order | 003 pkg | 004 no-rebuild | 005 approval | 006 host-agent | 007 migration | 009/010 | Notes |
|---|---|---|---|---|---|---|---|---|
| akurai-platform | ok | ok | ok | ok | ok (`ec2 deploy-binary`) | n/a | health curl present | catalog `deploy` field still names raw `akurai-ec2 release` (t_b6961691 scope) |
| akurai-notes | ok | ok | ok | ok | ok (backup+install via `ec2 ssh`) | ok (`.backup` + rollback on health-check failure) | health + `/mcp` 405 check | reference-quality migration handling |
| akurai-crm | ok | ok | ok | ok | ok (`ec2 release`) | n/a | `validate.sh` | catalog deploy field stale |
| akurai-passvault | ok | ok | ok | ok | ok (`ec2 release`) | n/a | `validate.sh` | catalog deploy field stale |
| akurai-vpn-site | ok | ok | ok | ok | ok (`ec2 ship`+`ssh rsync`) | n/a | curl health + content check | epic notes claimed this app had no `.akurai.yml`; it now does — stale note, not a current violation |
| akurai-tasks | ok | ok | ok | ok | ok (`ec2 release` w/ sha256) | n/a | `validate.sh --live` | catalog deploy field stale |
| akurai-idp | ok | ok | ok | ok | ok (`./deploy.sh publish`) | ok (sqlite `.backup` + integrity_check) | `validate.sh` | — |
| akurai-router | ok | ok | ok | ok | needs verification (`./deploy.sh`) | n/a | needs verification | deploy.sh not inspected in this pass |
| akurai-dns | ok | ok | ok | ok | needs verification (`./deploy.sh --publish-only`) | n/a | needs verification | deploy.sh not inspected in this pass |
| akurai-vpn-control | ok | ok | ok | ok | needs verification (`./deploy.sh ec2`) | n/a | needs verification | deploy.sh not inspected in this pass; approval is deliberate here (mesh criticality) |
| rust-monitor | ok | ok | ok | ok | needs verification (`akurai-ec2 release`) | n/a | `validate.sh` | epic notes claimed missing `environment: production`; pipeline already declares it — stale note |
| akurai-mail-api | ok | ok | ok | ok | ok (`ec2 ssh` backup+config, `ec2 release` w/ sha256) | ok (`tar` snapshot of app+config to timestamped backup) | `validate.sh` | epic notes claimed no distinct package job; pipeline already has one — stale note |
| akurai-drive-api | ok | ok | ok | ok | ok (`./deploy.sh publish` x2, sha256-checked) | ok (sqlite `.backup` + integrity_check) | `validate.sh` | multi-binary (backend + Framework + desktop) release, most complex conforming example |

Fleet-wide: all 13 apps satisfy SDP-002/003/004/005 as written today. The one
suite-wide gap the epic notes still call correctly is **SDP-006 evidence, not
declaration**: every app's `AKURAI_APPS.toml` `deploy` field still names a
direct `akurai-ec2 release ...` command rather than an AkurAI Build
run/promotion reference. That field is informational/manual-fallback tooling
today, not something Build enforcement reads — but it invites bypassing Build
by hand, so t_b6961691 should still replace it. The three "needs
verification" rows (router, dns, vpn-control) are the fleet audit's first
job: confirm their `deploy.sh` scripts satisfy SDP-006/010 with the same
rigor as the reference apps above, and note the discrepancies from this
document if any of the three "stale note" findings turn out to be wrong on
closer inspection.