Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
"pages": [
"infrastructure/cloud-compose",
"infrastructure/self-hosted-operations",
"infrastructure/current-release-status",
"infrastructure/release-compatibility"
]
},
Expand Down
27 changes: 21 additions & 6 deletions infrastructure/cloud-compose.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Use the [cloud-compose reference documentation](https://cc.libops.io/) for the e
</Info>

<Warning>
This page is an architecture contract, not a release announcement. Examples use `RELEASE_OR_FULL_SHA` deliberately. Do not configure a provider entrypoint, foundation module, package-version map, or Direct VPC option until the exact cloud-compose tag or commit you selected contains it and its release CI is green.
This page is an architecture contract, not a release announcement. Examples use `RELEASE_OR_FULL_SHA` deliberately. Do not configure a provider entrypoint, foundation module, package-version map, or Direct VPC option until the exact cloud-compose tag or commit you selected contains it and its release CI is green. Check the dated [Current Release Status](/infrastructure/current-release-status) before interpreting a planned integration as released.
</Warning>

## Deployment flow
Expand Down Expand Up @@ -83,6 +83,8 @@ A module ref alone does not make a deployment reproducible. Record and pin:

Core `sitectl` and its plugins have independent releases. In releases with per-package pins, `runtime.sitectl.version` is only the legacy fallback for packages without a package-specific value, and `latest` remains mutable. A release with only one shared version cannot represent an independently released plugin set. On a host with several projects, all projects share one installed package set, so select a set compatible with every application plugin on that host.

A package-version map selects versions; it does not retain their artifacts. The current LibOps package publisher rebuilds repository metadata from current release artifacts and can prune an older package. For a durable production rollback, preserve a repository snapshot or the matching GitHub release packages and `checksums.txt` in controlled storage, then test installation from that retained source.

A branch or tag in `runtime.compose` intentionally follows future repository changes. A full commit is detached and verified before deployment, and cloud-compose records the deployed head for later comparison. A commit identifies repository bytes; downstream branch protection and commit-signing policy still determine whether those bytes were trusted.

## Host-control trust boundary
Expand Down Expand Up @@ -127,6 +129,10 @@ If the selected release does not contain that foundation module, follow its
release-specific IAM instructions. Do not copy an unpublished foundation
module from a development branch into an otherwise pinned production stack.

<Warning>
In the [July 13, 2026 release-status snapshot](/infrastructure/current-release-status), PPB `0.5.0` exists as a source/GitHub release but its versioned container is not published. The power-management paragraphs below define the contract a selected release must satisfy; they do not make `0.5.0` a consumable image or make the managed shared-router/private-PPB path available.
</Warning>

The optional Cloud Run power-management ingress reaches the VM's private
application port through [Direct VPC
egress](https://cloud.google.com/run/docs/configuring/vpc-direct-vpc); it does
Expand All @@ -144,6 +150,14 @@ VPC ingress feature. Cloud Run services do not support Direct VPC ingress.
Public requests enter Cloud Run through its configured service/edge ingress;
Direct VPC egress is only the private hop from that service to the VM.

Cloud-compose configures this Cloud Run attachment with
`vpc_direct_egress = "PRIVATE_RANGES_ONLY"`. Only requests to private
destination ranges use the VPC path. Other outbound traffic follows normal
Cloud Run egress and does not traverse this VPC or its Cloud NAT. Changing the
service to `ALL_TRAFFIC` is not a tuning-only change: it creates a different
routing, NAT, cost, and cold-start contract and requires a separate design
review and hosted smoke test.

When power management is enabled, the subnet must be IPv4 `/26` or larger, use
RFC1918, RFC6598 (`100.64.0.0/10`), or Class E (`240.0.0.0/4`) space, and keep
the Cloud Run default MTU of `1460`. Cloud Run receives ephemeral addresses
Expand Down Expand Up @@ -187,11 +201,12 @@ larger right-edge suffix appended by trusted intermediaries before its larger
depth is accepted. Treat this as a wake-up filter, not as a replacement for
application authentication or a controlled load balancer/Cloud Armor policy.

The LibOps managed platform's depth-zero setting is a separate contract. Its
authenticated shared router replaces any incoming canonical client-IP header,
and the private PPB reads that single value at depth zero; it does not trust the
incoming `X-Forwarded-For` chain. See [Security and
Operations](/platform/security-operations) for that managed request path.
The target LibOps managed platform's depth-zero setting is a separate,
unreleased contract. Its authenticated shared router must replace any incoming
canonical client-IP header, and the private PPB must read that single value at
depth zero rather than trust the incoming `X-Forwarded-For` chain. See [Security
and Operations](/platform/security-operations) for that target managed request
path.

Google recommends an egress-aware startup probe for ordinary Direct VPC
services. The power-button proxy cannot use application reachability as its
Expand Down
56 changes: 56 additions & 0 deletions infrastructure/current-release-status.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
---
title: "Current Release Status"
description: "A dated availability record for the independently released artifacts in the LibOps application and infrastructure stack"
---

<Warning>
There is not yet a published, platform-wide known-good release set for the managed shared-router and private-PPB request path. The architecture pages describe the target contract, but they are not evidence that this integration is generally available. Do not change production DNS or infrastructure for that path until a later status record identifies every immutable artifact and its green end-to-end release gate.
</Warning>

This snapshot was reviewed on **July 13, 2026 at 23:00 UTC**. It is a release record, not a moving "latest" lookup. A later tag does not silently update the compatibility claims on this page.

## Status meanings

| Status | Meaning |
| --- | --- |
| Released dependency | The named independent release exists. Consumers must still verify its release notes, immutable identity, and compatibility with their complete deployment. |
| Released docs set | The exact core and plugin commits were used to generate and validate the published command documentation. |
| Unrecorded | Independently usable artifacts exist, but LibOps has not published an aggregate compatibility record proving one complete cross-repository set. |
| Preview | Code or architecture work exists, but the production integration and rollout gate are incomplete. |
| Blocked | A required release, provenance, CI, or end-to-end gate is not complete. Do not operate the feature as released. |

## Published foundations

| Surface | Dated reference | Status | What this record proves |
| --- | --- | --- | --- |
| Core `sitectl` and application plugins | [Generated documentation dependency manifest](https://github.com/libops/sitectl-docs/blob/de317cf1af3ade6097fbe37470a78790807ad713/scripts/snippet-dependencies.json) | Released docs set | The documentation was generated from the full core and plugin commits in that manifest. Package versions remain independent. |
| cloud-compose | [`0.10.3`](https://github.com/libops/cloud-compose/releases/tag/0.10.3) | Released dependency | This tag exists as a stable self-hosted baseline. Only fields and migrations present in that tag are available to its consumers. |
| Terraform Cloud Run v2 module | [`0.7.0`](https://github.com/libops/terraform-cloudrun-v2/releases/tag/0.7.0) | Released dependency | The reusable module release exists. That does not prove that cloud-compose or the managed control plane has promoted every module capability. |
| Buildkit images and Compose templates | The exact template checkout and each `tag@sha256:digest` it records | Unrecorded | Templates record immutable direct-image identities, but this snapshot does not contain a generated aggregate of every template commit, Buildkit digest, and hosted smoke-test result. |

The table deliberately does not invent an image digest, template commit, signature identity, or CI result that has not been captured by a release manifest. For self-hosted deployments, build a deployment-specific record from the exact template commit and immutable references you test.

## Managed integrations not released in this snapshot

| Integration | Status | Gate that remains |
| --- | --- | --- |
| Versioned PPB support image | Blocked | A [`0.5.0` source/GitHub release](https://github.com/libops/ppb/releases/tag/0.5.0) exists, but its versioned container is **not published**. There is no released PPB `tag@sha256:digest` to consume or verify against the expected publisher identity. The source tag is not a substitute for that container artifact. |
| Cloudflare and load balancer to shared Cloud Run router to private per-site PPB | Blocked / preview | Publish and verify the exact router and PPB image digests and expected keyless publisher identities; merge and release the API/router integration; pin the dependencies in the managed Terraform roots; pass the private-origin, client-IP, authorization-preservation, timeout, Direct VPC, and rollback canaries. |
| Separate request-serving API and `api-worker` Cloud Run services | Preview | Release the managed worker deployment and prove identity bootstrap, database connectivity, readiness, rollout, rollback, and removal of any temporary migration privilege. A process boundary in source or Compose is not proof of this Cloud Run topology. |
| Platform-wide image, template, CLI, plugin, and infrastructure compatibility manifest | Blocked | Generate the aggregate record from release automation and attach hosted CI evidence for the exact references. Until then, each operator owns a deployment-specific record. |

## What a promoted managed set must record

A future status entry can change a managed integration to released only when it names:

- every Buildkit and support-image tag plus immutable digest;
- the expected signing repository, workflow identity, and successful verification result for privileged managed images;
- every template release or full commit;
- core `sitectl` and each plugin's independent package version;
- the cloud-compose release or full commit, reusable Terraform-module releases, and provider lockfiles;
- the API, worker, router, and PPB release identities;
- the exact hosted smoke, upgrade, migration, canary, and rollback evidence.

<Card title="Release compatibility and upgrades" icon="arrows-rotate" href="/infrastructure/release-compatibility">
Follow the dependency order and keep a deployment-specific compatibility and rollback record.
</Card>
8 changes: 8 additions & 0 deletions infrastructure/release-compatibility.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ LibOps applications span several independently released repositories. Treating t

Architecture documentation can describe a contract before every dependent repository has published it. That does not make a tag, package, module, image, or digest available. A compatibility set is consumable only after every selected reference resolves from its release channel and the corresponding hosted CI is green.

<Info>
The dated [Current Release Status](/infrastructure/current-release-status) records which foundations are independently released and which managed integrations remain preview or blocked. It intentionally does not fill missing digests or CI evidence with assumptions.
</Info>

## Release dependency order

Publish and consume changes in this order:
Expand Down Expand Up @@ -69,6 +73,8 @@ Cloud-compose releases that expose `runtime.sitectl.package_versions` can pin ea

Production should not follow a package manager's `latest` channel without a reviewed compatibility record.

The current Linux package publisher rebuilds APT and RPM metadata from the current release artifacts and does not retain older versions in the package repository. A per-package version map is therefore reproducible only while every named package remains downloadable. For a long-lived production rollback, retain a repository snapshot or archive the matching GitHub release packages and `checksums.txt` in controlled storage. A version string without a retained artifact is not a recovery plan.

## Compatibility record

Keep one release record for the complete deployed set. At minimum, record:
Expand Down Expand Up @@ -147,6 +153,7 @@ Keep enough information to reconstruct the last known-good release:
- image tags and digests;
- Composer lockfile;
- core and per-plugin package versions;
- retained native-package or GitHub release assets plus their published checksums, when the package repository does not preserve that version;
- cloud-compose module source and provider lockfile;
- Terraform state version;
- application-consistent data backup and required secrets;
Expand All @@ -164,6 +171,7 @@ Rollback is a tested procedure, not a list of old version numbers. Exercise it i
- Treating a successful Terraform plan as application migration validation.
- Rolling an image back across an irreversible schema migration.
- Migrating Terraform module addresses by allowing resource recreation.
- Assuming a native-package pin remains installable after the package repository has pruned that release.

<CardGroup cols={2}>
<Card title="Downstream forks" icon="code-branch" href="/templates/downstream-forks">
Expand Down
6 changes: 5 additions & 1 deletion platform/adoption-model.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,11 @@ Managed Terraform keeps long-lived ownership narrow:

Creation follows foundation, organization, Vault configuration, project, then edge; deletion reverses that order. A site does not receive an independent Terraform state, and one state must not manage a resource owned by another root.

Controller and site delivery use private network traffic but remain authenticated. Shared project state owns the Cloud Run service identity, network-use permissions, and regional network capacity; runtime state owns instance-scoped power grants and VM firewall rules. The public site path still enters through the declared Cloudflare and load-balancer edge because Direct VPC provides Cloud Run **egress**, not service ingress. See [Security and Operations](/platform/security-operations) for the exact router-to-PPB identity, canonical client-IP, subnet, startup, and timeout contracts.
<Warning>
The shared-router/private-PPB integration is a target architecture and is not generally released in the [current status snapshot](/infrastructure/current-release-status). The ownership boundary remains valid, but the linked router identity, canonical client-IP, and timeout details must not be treated as the current production topology until their release gate is complete.
</Warning>

In that target path, controller and site delivery use private network traffic but remain authenticated. Shared project state owns the Cloud Run service identity, network-use permissions, and regional network capacity; runtime state owns instance-scoped power grants and VM firewall rules. The public site path still enters through the declared Cloudflare and load-balancer edge because Direct VPC provides Cloud Run **egress**, not service ingress. See [Security and Operations](/platform/security-operations) for the target router-to-PPB identity, canonical client-IP, subnet, startup, and timeout contracts.

## Responsibility changes by adoption layer

Expand Down
8 changes: 6 additions & 2 deletions platform/automation-backplane.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ LibOps uses GitHub as an important collaboration surface, but the platform shoul

LibOps is building a webhook-driven automation backplane for events that need to trigger work inside the platform: dependency scanning, CI runner orchestration, deployment follow-up, and repository maintenance.

<Warning>
The separate request-serving API and `api-worker` Cloud Run deployment described below is a **preview target**, not a released managed topology in the [July 13, 2026 status snapshot](/infrastructure/current-release-status). Process separation in source code or a Compose development stack does not prove that the worker service, identity migration, database path, rollout, and rollback have been promoted in production.
</Warning>

## GitHub events in LibOps

The automation backplane listens for GitHub events such as:
Expand Down Expand Up @@ -37,15 +41,15 @@ The service is intentionally event-driven. GitHub is the collaboration surface,

LibOps keeps customer-facing API requests separate from retrying background work. The API request path validates the caller, commits the requested state change, records any required outbox event in MariaDB, and returns. Control-plane workers then drain durable queues for reconciliation, webhook fanout, Slack task replies, and Terraform scheduling.

API-local maintenance work follows the same rule. Organization synchronization and email verification cleanup run under the dedicated `api-worker` process, selected by `LIBOPS_API_WORKER_JOBS`, rather than inside the request-serving API process. The API worker exposes its own `/health` and `/ready` endpoints so it can be scaled, rolled out, and restarted independently.
The target API-local maintenance contract follows the same rule. Organization synchronization and email verification cleanup run under the dedicated `api-worker` process, selected by `LIBOPS_API_WORKER_JOBS`, rather than inside the request-serving API process. The API worker exposes its own `/health` and `/ready` endpoints so a released worker deployment can scale, roll out, and restart independently.

This gives the platform three operational boundaries:

- **Liveness vs readiness:** `/health` only proves that a process is alive. `/ready` reports whether the instance can safely receive traffic, including database availability.
- **Durable work before acknowledgement:** mutating API paths should not report success until both the resource change and the outbox/event record are committed.
- **Last-known-good runtime state:** existing sites continue serving from their last applied configuration if the API or control plane is temporarily unavailable. A control-plane outage should block new changes, not running site traffic.

The request-serving API and `api-worker` deploy as separate Cloud Run services. The API service uses `/health` for liveness and `/ready` as the startup gate. The worker runs with at least one instance and always-allocated CPU because it owns polling loops; moving those loops back into the API process would reintroduce request-path coupling.
In the target managed deployment, the request-serving API and `api-worker` will deploy as separate Cloud Run services. The API service will use `/health` for liveness and `/ready` as the startup gate. The worker requires at least one instance and always-allocated CPU because it owns polling loops; moving those loops back into the API process would reintroduce request-path coupling. Do not use these settings as a rollout instruction until the status record identifies the released worker artifact and migration runbook.

Vault OIDC discovery and JWKS validation follow the same last-known-good rule. Successful live JWKS refreshes write a local cache containing the issuer, JWKS URL, keys, and cache time. If Vault or JWKS discovery is briefly unavailable during startup, the API can start from that cache for the configured trust window and reports `degraded_last_known_good` in `/ready`. After the trust window expires, readiness fails and cached keys are no longer used.

Expand Down
Loading