Platform Compiler — Projection Model¶
Document: 3 of 4 Status: Canonical Depends on: Core Object Model (arch_01), Contract Model (arch_02) Required by: Receipt & Provenance Model
Purpose¶
This document defines how the Platform Compiler transforms authoritative objects into generated artifacts. It specifies the compiler's internal pipeline stages, the Planner/Renderer separation that keeps the compiler runtime-agnostic, all defined projection types, and the rules governing generated repositories.
Core Principle¶
The Platform Compiler never knows Cloudflare. It never knows Docker. It never knows Terraform.
It knows:
- Projection Types — abstract descriptions of what to generate
- Renderers — pluggable implementations that emit a specific tool's artifact for a given projection type
Changing from Terraform to Pulumi to Crossplane means swapping a Renderer. The Planner, the Object Graph, and the Projection Types remain unchanged.
Compiler Internal Pipeline¶
Readers
│ read contracts/, objects/, providers/
│ produce typed in-memory representations
▼
Normalizers
│ validate objects against schemas
│ resolve $refs and cross-object relationships
│ apply trust levels (owned vs vendor vs community)
│ detect conflicts and drift
▼
Object Graph
│ complete in-memory graph of all objects and relationships
│ Authority → Capability → Contract
│ Capability → Deployment → Runtime → Tunnel
│ Provider → Contract
▼
Planners
│ read the Object Graph
│ produce ProjectionPlans — abstract, renderer-agnostic
│ apply state filters (skip legacy, skip retired)
│ detect duplicate projections
│ validate intent (no two canonical deployments on same hostname)
▼
Generators
│ select Renderer per (ProjectionPlan, target)
│ Renderer emits the final artifact bytes
│ Renderer is replaceable: Terraform ↔ Pulumi ↔ Crossplane ↔ YAML
▼
Emitters
│ write artifacts to generated/ in the registry
│ open MRs to generated repositories
│ sign artifacts with hash
▼
Receipts
record what was read, what was planned, what was generated
stored in Beads → Dolt
Projection Types¶
A Projection Type is an abstract description of an artifact kind. It has no tool knowledge. A Renderer binds a Projection Type to a specific tool.
Exposure Projections¶
| Projection Type | Consumes | Emits (abstract) |
|---|---|---|
exposure:tunnel-ingress |
Deployment + Tunnel | Tunnel ingress rules for all deployments routed through a tunnel |
exposure:dns |
Deployment (exposed=true) | DNS records pointing hostnames at tunnel endpoints |
exposure:zero-trust-access |
Deployment (zeroTrust=true) | Identity-aware access policies per hostname |
exposure:tls |
Deployment + Tunnel | TLS certificate and termination configuration |
Runtime Projections¶
| Projection Type | Consumes | Emits (abstract) |
|---|---|---|
runtime:compose |
Deployment + Runtime (type=container) | Container service definition with image, ports, volumes, env |
runtime:k8s-manifest |
Deployment + Runtime (type=k8s) | Kubernetes Deployment, Service, ConfigMap |
runtime:systemd |
Deployment + Runtime (type=vm) | systemd unit file |
runtime:gascity-pack |
Capability + Contract | Gas City Pack TOML definition |
runtime:gascity-city |
Platform + Runtime + Pack refs | Gas City City TOML definition |
Network Projections¶
| Projection Type | Consumes | Emits (abstract) |
|---|---|---|
network:tailscale-acl |
Runtime (network=tailscale) + trust rules | Tailscale ACL policy (HuJSON) |
network:tailscale-tunnel |
Deployment + Runtime (type=nas) | cloudflared tunnel config for NAS |
network:subnet-route |
Runtime (subnet-router=true) | Tailscale subnet route declaration |
Security Projections¶
| Projection Type | Consumes | Emits (abstract) |
|---|---|---|
security:gitlab-policy |
Security policy objects | GitLab security policy YAML |
security:cedar-policy |
Authorization capability + resource definitions | Cedar policy file |
security:opa-policy |
Governance capability + rules | Rego policy file |
CI/CD Projections¶
| Projection Type | Consumes | Emits (abstract) |
|---|---|---|
cicd:gitlab-component |
CI component object | GitLab CI component YAML |
cicd:pipeline-template |
CI pipeline object | GitLab CI include template |
Catalog Projections¶
| Projection Type | Consumes | Emits (abstract) |
|---|---|---|
catalog:service-catalog |
All capabilities + deployments | service-catalog.json |
catalog:control-plane |
All capabilities + contracts | control-plane.json |
catalog:url-index |
All deployments | urls.json (replaces ai.json) |
catalog:endpoint-index |
All contracts (type=openapi) | endpoints-index.json |
catalog:typescript-types |
All contracts (type=openapi) | *.types.ts |
Renderers¶
A Renderer is a pluggable implementation that binds to one or more Projection Types and emits a specific tool's artifact format.
Renderer
id: renderer.<tool>.<projection-type>
projection: → ProjectionType (one or more)
tool: terraform | pulumi | crossplane | yaml | toml | hcl | json | typescript
version: which tool version this renderer targets
stable: boolean
Defined Renderers¶
| Renderer | Tool | Projection Types | Notes |
|---|---|---|---|
renderer.terraform.cloudflare-dns |
Terraform + Cloudflare provider v5 | exposure:dns |
Active |
renderer.terraform.cloudflare-tunnel |
Terraform + Cloudflare provider v5 | exposure:tunnel-ingress |
Blocked (token permission) |
renderer.terraform.cloudflare-zero-trust |
Terraform + Cloudflare provider v5 | exposure:zero-trust-access |
Active |
renderer.terraform.oci |
Terraform + OCI provider v6 | runtime:vm-provision |
Active |
renderer.yaml.cloudflare-tunnel |
cloudflared YAML | exposure:tunnel-ingress |
Active (current fallback) |
renderer.yaml.tailscale-acl |
Tailscale HuJSON | network:tailscale-acl |
Active |
renderer.yaml.tailscale-tunnel |
cloudflared YAML (NAS) | network:tailscale-tunnel |
Active |
renderer.yaml.docker-compose |
Docker Compose v3 | runtime:compose |
Active |
renderer.toml.gascity-pack |
Gas City Pack v2 | runtime:gascity-pack |
Active |
renderer.toml.gascity-city |
Gas City City | runtime:gascity-city |
Active |
renderer.yaml.gitlab-policy |
GitLab Security Policy | security:gitlab-policy |
Active |
renderer.yaml.gitlab-component |
GitLab CI Component | cicd:gitlab-component |
Active |
renderer.typescript.types |
TypeScript (openapi-typescript) | catalog:typescript-types |
Active |
renderer.json.service-catalog |
JSON | catalog:service-catalog |
Active |
Swapping Terraform for Pulumi means registering renderer.pulumi.cloudflare-dns and removing renderer.terraform.cloudflare-dns. The Projection Type exposure:dns does not change. The Planner does not change. The Object Graph does not change.
Planner Output: ProjectionPlan¶
The Planner produces a ProjectionPlan — a declaration of what must be generated. The ProjectionPlan is itself an artifact that can be reviewed before Generators run.
# Example ProjectionPlan fragment
projections:
- type: exposure:tunnel-ingress
tunnel: tunnel.cloudflare.oracle-platform
renderer: renderer.yaml.cloudflare-tunnel
deployments:
- deployment.compliance.oracle.production
- deployment.authentication.oracle.production
- deployment.mesh.oracle.production
# ... all canonical deployments on this tunnel
exclude:
- deployment.compliance.oracle.legacy-3014 # state: legacy
- deployment.adash.oracle.legacy-8081 # state: legacy
output: generated/projections/cloudflare/tunnel-config.yml
- type: exposure:dns
renderer: renderer.terraform.cloudflare-dns
deployments:
- deployment.compliance.oracle.production
# ...
output: generated/projections/terraform/cloudflare/dns.tf
The ProjectionPlan makes the compiler's intent explicit and auditable. A human can review a ProjectionPlan before running Generators.
Generated Repository Ownership¶
Each generated repository receives artifacts from specific Projection Types:
| Repository | Receives projections of type | Via |
|---|---|---|
iac |
exposure:dns, exposure:tunnel-ingress, exposure:zero-trust-access, exposure:tls, runtime:vm-provision |
Terraform renderers |
agent-docker |
runtime:compose, runtime:k8s-manifest |
Docker Compose + k8s YAML renderers |
agent-tailscale |
network:tailscale-acl, network:tailscale-tunnel, network:subnet-route |
Tailscale YAML renderers |
blucity-packs |
runtime:gascity-pack |
Gas City TOML renderer |
bluefly-city |
runtime:gascity-city |
Gas City TOML renderer |
security-policies |
security:gitlab-policy, security:cedar-policy, security:opa-policy |
YAML + Rego renderers |
gitlab_components |
cicd:gitlab-component, cicd:pipeline-template |
GitLab CI YAML renderer |
The compiler writes to these repositories via:
1. Compiler runs, emits artifacts to generated/projections/ within api-schema-registry
2. Compiler opens one MR per generated repository with the relevant artifact subset
3. CI in the generated repository runs terraform plan / docker compose config / gc formula list / etc.
4. Owner reviews the plan output, approves MR
5. MR merges → deploy pipeline runs
6. Deployment emits a Receipt
Compiler Trigger¶
Merge to api-schema-registry main
│
▼
CI: api-schema-registry pipeline
│
├── validate: schemas valid
├── validate: all objects conform to schemas
├── validate: no two canonical deployments on same hostname
├── validate: all provider manifests hash-verified
│
▼
generate: run compiler
│
├── emit ProjectionPlan (stored as artifact)
├── emit generated/ artifacts
│
▼
open-mrs: one MR per generated repo
│
▼
(human reviews ProjectionPlan + MR diff)
│
▼
merge → deploy
Conflict Detection¶
The Planner detects and rejects the following before any artifact is generated:
| Conflict | Planner behavior |
|---|---|
Two canonical deployments on the same hostname |
Fail — must resolve in objects before compile |
A canonical deployment and a legacy deployment on the same hostname |
Warn — emit canonical only, legacy excluded from projections |
A deployment on a tunnel not declared in objects/tunnels/ |
Fail |
| A deployment referencing a retired runtime | Fail |
| Two tunnels routing to the same hostname | Fail |
A legacy deployment with no planned retirement date |
Warn |
The current 27 Cloudflare route conflicts will cause Planner failures until each conflict is resolved in objects/deployments/ by classifying one as canonical and the other as legacy.
Enforcement in Generated Repositories¶
Once the compiler is the sole writer to a generated repository:
CODEOWNERS: compiler service account owns all generated files; human ownership limited toREADME.mdand CI config- Branch protection: no direct push to main; only compiler-opened MRs merge
- CI drift detection: scheduled
terraform planor equivalent; alert if runtime diverges from generated state - Pre-receive hook: reject any commit not authored by the compiler service account on compiler-owned files
Invariants¶
- The Planner produces a ProjectionPlan before any Generator runs.
- No artifact is emitted without a corresponding ProjectionPlan entry.
- No Renderer knows more than one target tool.
- Swapping a Renderer requires no changes to the Planner, Object Graph, or Projection Types.
- The compiler emits artifacts only to
generated/withinapi-schema-registryand to generated repository MRs — never directly to a runtime. - A
legacydeployment is never included in a Projection output. - A Planner conflict causes the entire compilation to fail — no partial artifact emission.