Skip to content

Container tiering — backend-core is the application tier

Status

adopted-from-hub — the canonical decision is ARC-ADR-023 in the AgentArmy hub (docs/decisions/ARC-ADR-023-container-tiering-strategy.md). This file is a one-page shadow so a coding agent working in this spoke can see the rule without leaving the repo.

Decision (as it applies here)

backend-core is the application tier. One container per spoke, stateless, rolling deploys. It does not own or ship platform-tier services.

Concretely:

  • image.json (root) declares "tier": "application".
  • llm-gateway/image.json declares "tier": "function" (the sibling small stateless gateway image).
  • No image.json in this repo declares "tier": "platform".
  • docker-compose.yml does not include arcadedata/*, postgres*, nats*, or stain/jena-fuseki* as services. Those are platform images and live in the hub's templates/local-stack/.
  • This spoke does not carry a platform Dockerfile, Bicep template, or PowerShell deploy script outside the hub-synced templates/ paths. The ArcadeDB ACI Bicep + deploy/deploy.ps1 that used to live here moved to the hub when ARC-ADR-023 landed.

backend-core reaches platform services over the network — the operator brings the hub's local-stack up first, then docker compose up backend-core. The connection URLs are env vars (ARCADEDB_URL, POSTGRES_URL, NATS_URL, FUSEKI_URL).

Enforcement

The rule is enforced two ways:

  1. CLAUDE.md carries the rule under "Spoke-specific guidance" — the one place an AI coding agent reads before editing this repo.
  2. CI gatescripts/check_tier_separation.py (data-driven config at scripts/tier_separation.json) runs in contract-provider.yml's drift-and-validity job. It fails on:
  3. any image: line in docker-compose.yml matching the platform-image denylist outside an allowlisted templates/ path,
  4. any image.json declaring "tier": "platform",
  5. any Dockerfile / setup.sh under a path named after a platform image (e.g. arcadedb-image/) outside hub-synced paths.

A self-test under tests/test_tier_separation.py proves the gate fires on a bad fixture and stays silent on the good case — same pattern as the .semgrep/no-persisted-credential.yml self-test in ci.yml.

What is not covered by this rule

  • The conformance job in contract-provider.yml may spin up an ArcadeDB container as a CI service — that is an ephemeral test dependency, not a deploy, and is fine.
  • Issue #93 (collapsing backend-core's two application containers into one) is an application-tier consolidation and is complementary to this rule, not blocked by it.

Refs

  • Hub: ARC-ADR-023 — container tiering + Platform Image Ownership amendment.
  • PR #98 — removes vendored templates/arcadedb-image/ (first slice of cleanup).
  • Commits 4bc6546 (function-tier llm-gateway) and b9509c5 (application-tier image.json + doctor).
  • templates/image-schema.jsontier enum: platform | application | function.