Skip to content
SENS Platform Docs

ADR-0012 Source Monorepo, GitOps Separation, and Management-Plane Topology

Accepted as the target architecture on 2026-08-14 and clarified on 2026-09-02. Source and infrastructure implementation is pending.

This ADR records the target architecture and its approved Greenfield rebuild. It does not claim that the current Coolify runtime, in-cluster Argo CD, in-cluster OpenBao, or the existing three repositories have already been replaced. The exact preparation, execution, verification, and cleanup sequence is defined in the SENS Cloud and SENS Ops rollout plan.

The current platform has three repositories with different responsibilities:

  • sens-platform is already a pnpm monorepo for the product applications, services, packages, documentation, Dockerfiles, release tooling, and the sens-platform Helm chart;
  • sens-platform-infra is the private GitOps desired-state repository for the current k3s cluster, environment values, release selection, Argo CD Applications, OpenBao, External Secrets Operator, monitoring agents, and operator runbooks;
  • sens-platform-ops contains the external operations portal, release controller, monitoring data plane, and the current Coolify Docker Compose deployment.

This separation makes cross-repository work and AI-assisted changes harder to review and publish. At the same time, placing application source and the live desired state for every managed or customer installation in one repository would collapse an important write and review boundary.

The target operating model must also support:

  • replacing Coolify through a controlled hard cut before customer Production data exists;
  • one independently failing platform data plane and one independently failing operations management plane;
  • SENS-hosted dedicated customer installations;
  • later customer-owned Kubernetes and on-premise installations;
  • one portable product release without customer-specific code forks;
  • independent customer upgrade schedules;
  • explicit backup, restore, security, and support boundaries;
  • GitHub-hosted CI and immutable release artifacts;
  • a path from the current single-node environments to later high availability without describing two servers as an HA system.

Adopt two independent physical servers and two independent k3s clusters as the first target topology:

  • sens-ops is the SENS management and operations cluster;
  • sens-cloud is the first SENS-hosted installation of the SENS Platform.

Adopt two Git repositories as the long-term default:

  • sens-platform becomes the source monorepo for all SENS-developed product and operations software, first-party Helm charts, tests, CI workflows, release tooling, and documentation;
  • sens-platform-infra remains the private GitOps desired-state repository and may later be renamed to describe its fleet responsibility.

The repository boundary is independent of the runtime boundary. One source monorepo can build artifacts for multiple clusters without deploying all components together.

The sens-ops server runs a dedicated single-node k3s management cluster in the initial target topology. It owns:

  • Argo CD;
  • OpenBao;
  • the Ops portal and durable Ops controller;
  • Valkey for Ops identities, sessions, audit, release jobs, and scale leases;
  • VictoriaMetrics and VictoriaLogs;
  • vmauth, vmalert, Alertmanager, and Blackbox Exporter;
  • the public status surface and external platform probes;
  • backup orchestration, but not the only backup copy;
  • private administrative ingress and tightly bounded public ingress.

It does not run customer telemetry ingestion, the platform database, NATS, or ordinary product workloads.

The sens-cloud server runs a separate single-node k3s platform cluster in the initial target topology. It is the first managed installation of the portable sens-platform product and owns:

  • platform-api, web-console, and customer-facing documentation;
  • MQTT ingestion and raw-storage workers;
  • flow, sandbox, and action workers;
  • PostgreSQL/TimescaleDB and database migrations;
  • NATS JetStream and its bootstrap resources;
  • product ingress, certificates, and persistent volumes;
  • namespace-scoped External Secrets Operator and Reloader instances;
  • vmagent, Node Exporter, kube-state-metrics, and Fluent Bit or equivalent cluster-side collectors.

sens-cloud is an installation name, not the product name. Product code, images, contracts, and the portable Helm chart remain named sens-platform so the same artifacts can run in a SENS-hosted dedicated cluster or a customer-owned Kubernetes environment.

The management plane may administer and observe the platform plane, but the platform request and ingestion paths must not require the management plane to be available.

The intended failure behavior is:

FailureRequired behavior
sens-cloud unavailablesens-ops remains reachable, retains prior monitoring history, raises external alerts, and supports diagnosis and recovery.
sens-ops unavailableExisting sens-cloud workloads, local Kubernetes reconciliation, ingestion, API access, NATS, TimescaleDB, and existing Kubernetes Secrets continue. Releases, central alerting, new Secret synchronization, rotation, and central support operations pause.
GitHub or GHCR temporarily unavailableRunning workloads continue. New verified builds, image pulls not already cached, and deployments may pause.
Both servers or their shared site unavailableRecovery uses Git plus encrypted backups stored in a third failure domain.

The required network flows are explicit and narrowly firewalled:

  • sens-ops to the sens-cloud Kubernetes API for Argo CD and bounded Ops access;
  • sens-cloud to sens-ops for OpenBao, metrics Remote Write, and normalized log delivery;
  • both clusters to the explicitly required GitHub, GHCR, DNS, ACME, SMTP, and backup endpoints;
  • administrative UIs through a private network or explicitly approved ingress, not through assumed trust in a shared internal network.

The sens-platform repository remains rooted at its current product layout. Its existing product commit identities and release audit references remain valid while the active Ops source enters as a new snapshot. The target logical layout is:

sens-platform/
├── apps/
├── services/
├── packages/
├── operations/
│ └── sens-ops/
├── deploy/
│ └── charts/
│ ├── sens-platform/
│ └── sens-ops/
├── docs/
├── scripts/
└── .github/workflows/

Copy the active sens-platform-ops source into operations/sens-ops/ as a clean snapshot. Git history does not need to be imported, and nested .git directories are forbidden. Workflow files that GitHub must discover move to the root .github/workflows/ directory with unambiguous product, Ops, and release names.

The resulting source repository uses one root pnpm workspace and one lockfile. This shared dependency graph enables transitive affected-scope CI without coupling release versions. The root package.json remains the base version for SENS Platform, while operations/sens-ops/package.json owns the independent SENS Ops base version.

The GitOps repository remains separate and contains no application source or Secret values. Its target logical layout distinguishes management clusters, platform clusters, and installations:

sens-platform-infra/
├── clusters/
│ ├── sens-ops/
│ └── sens-cloud/
├── installations/
│ └── sens-cloud/
│ ├── test/
│ └── production/
├── config/
└── docs/

The desired-state repository is rebuilt to this layout as part of the hard cut. Old live paths stop being authoritative when their replacement Applications have passed the phase gate. No compatibility layer or dual-write period is required.

The GitOps repository records:

  • cluster and Argo CD Application definitions;
  • non-secret environment and installation values;
  • exact released chart and image selections;
  • Secret names, SecretStore contracts, and ExternalSecret mappings without values;
  • release audit metadata;
  • approved upstream chart versions;
  • operational bootstrap and recovery documentation.

This boundary remains deliberate because GitHub repository write permission cannot be limited to one folder. A workflow allowed to publish GitOps desired state must not thereby gain an equivalent write path to every product source file. This becomes more important when the desired state controls several dedicated customer installations.

GitHub-hosted Actions remain the default CI execution environment. Do not run a permanently privileged general-purpose self-hosted runner on sens-ops, where a build could share a host with OpenBao, Argo CD, and production credentials. A future self-hosted runner must be ephemeral and isolated on a separate worker or VM.

The source-monorepo CI always creates one stable, discoverable overall result. An explicit impact resolver maps changed paths through the pnpm dependency graph to affected workspaces and deployables:

  • a leaf service change validates that service and the tests that consume it;
  • a shared package change validates every transitive consumer;
  • product chart or product release-tooling changes validate the product release surface;
  • Ops source, Ops configuration, or the Ops chart validate SENS Ops;
  • root lockfile, shared CI, dependency-policy, impact-resolver, or unknown path changes fail safe to the complete validation suite;
  • skipped domains report a successful conditional job rather than relying on a missing required workflow status.

Pull requests build affected container images without publishing them. A scheduled full CI run and the stable SENS Platform Production promotion both validate the complete product surface. SENS Platform remains one atomic product release; affected-scope CI does not create independently versioned production microservices.

Do not introduce Nx, Turborepo, a generic build graph, or another orchestration framework solely for the repository import. Existing pnpm and explicit scripts remain sufficient until measured CI cost creates a concrete requirement.

The SENS Platform release flow becomes:

source change
-> CI, tests, builds, and Helm rendering
-> immutable container images and OCI Helm chart
-> automated GitOps Test release selection
-> Argo CD on sens-ops
-> remote sens-cloud Test synchronization
-> reviewed Production approval in sens-ops
-> stable artifacts and Production GitOps selection
-> manual sens-cloud Production synchronization
-> live version and readiness verification

GitOps automation may update the separate desired-state repository after a verified build. Ordinary product development still requires one human source push; the second GitOps write is an automated, auditable release action. Manual cluster configuration remains a separate reviewed GitOps change by design.

The first-party SENS Ops release flow is independent and automatic:

Ops-affecting merge to main
-> affected Ops CI, tests, container build, and Helm rendering
-> immutable Ops image and OCI Helm chart
-> serialized GitOps release-selection update
-> automated local Argo CD synchronization on sens-ops
-> live version and readiness verification

Product and Ops publishers submit separate release selections. One serialized, idempotent infrastructure-writer workflow applies them to sens-platform-infra so concurrent successful builds cannot overwrite each other. Automatic SENS Ops release applies only to first-party SENS Ops artifacts; upstream Argo CD, OpenBao, VictoriaMetrics, and similar dependencies remain explicitly pinned and reviewed.

The platform release model distinguishes:

  • the stable base version in the root package.json;
  • the concrete release-channel version produced by CI;
  • the Helm chart package version;
  • the chart appVersion;
  • individual container image tags;
  • the version selected by each GitOps installation.

The root package.json remains the only human-selected base version for the next platform release line. A human must explicitly choose major, minor, or patch; importing repositories or moving clusters does not authorize a version bump.

The current chart is read from a Git path at an immutable product commit, so its Chart.yaml version is not currently Argo CD’s deployment selector. The GitOps release files select the product commit and explicit image tags.

Publish the first-party sens-platform Helm chart as an immutable OCI artifact in GHCR alongside the platform images. The concrete release version is shared by all artifacts produced from one tested platform commit.

Example Test release:

ghcr.io/sens-gmbh/sens-platform-platform-api:0.3.0-alpha.7.1
ghcr.io/sens-gmbh/sens-platform-web-console:0.3.0-alpha.7.1
ghcr.io/sens-gmbh/sens-platform-flow-worker:0.3.0-alpha.7.1
ghcr.io/sens-gmbh/charts/sens-platform:0.3.0-alpha.7.1

Example Production release:

ghcr.io/sens-gmbh/sens-platform-platform-api:0.3.0
ghcr.io/sens-gmbh/sens-platform-web-console:0.3.0
ghcr.io/sens-gmbh/sens-platform-flow-worker:0.3.0
ghcr.io/sens-gmbh/charts/sens-platform:0.3.0

CI packages the chart with the release version and appVersion supplied from the tested release metadata. Source metadata may mirror the stable base version, but it is derived and validated rather than selected independently. Operators must not copy versions manually between files.

The release validation must prove at least:

OCI chart version
= OCI chart appVersion
= selected platform image tags
= SENS_PLATFORM_VERSION
= GET /version

GitOps release metadata may continue to list every image tag explicitly. This intentional redundancy provides auditability and permits a future reviewed component-specific recovery, while generation and validation prevent drift.

The sens-ops application and its first-party chart have an independent, automatic release lifecycle. A successful Ops-affecting merge to main derives a concrete version such as MAJOR.MINOR.PATCH-main.RUN.ATTEMPT from the Ops base version and publishes the image and chart with the same immutable version. The chart version, chart appVersion, image tag, and SENS_OPS_VERSION must match. A source monorepo does not imply one global version for unrelated deployables. OpenBao, External Secrets Operator, VictoriaMetrics, Reloader, Argo CD, and other upstream components retain independently reviewed and pinned upstream chart versions.

Argo CD moves to sens-ops and manages:

  • its local management-cluster Applications; and
  • sens-cloud as an explicitly registered remote cluster.

All platform Applications that currently target https://kubernetes.default.svc must be changed to the explicit remote sens-cloud destination before the old Argo CD instance is retired. Management Applications may continue to target the local cluster.

Argo Projects, cluster credentials, repository credentials, and destination allowlists remain separated by responsibility. SENS Cloud Production keeps its manual product sync policy. SENS Cloud Test uses automated sync after the remote-cluster path has passed render, permission, initialization, and readiness validation. The local first-party SENS Ops Application automatically syncs each verified Ops release selected by the release automation.

The management cluster stores credentials capable of changing the platform cluster. It therefore becomes a higher-security trust boundary than an ordinary application host and requires encryption at rest, least privilege, private administration, credential rotation, audit, and tested recovery.

OpenBao is initialized as a fresh service on sens-ops; no old OpenBao data is migrated. External Secrets Operator remains in the destination platform cluster and writes namespace-local Kubernetes Secrets. Backup and isolated restore are not prerequisites for this empty pre-customer rebuild, but they become mandatory before real customer Production data or irreplaceable Production credentials are accepted.

The cross-cluster authentication contract must define:

  • the exact remote Kubernetes API used for TokenReview;
  • TLS trust and rotation;
  • short-lived ServiceAccount TokenRequest audiences;
  • one explicit authentication and policy boundary per managed cluster and environment;
  • behavior during OpenBao, network, or TokenReview outages;
  • retention of last synchronized Kubernetes Secrets;
  • monitoring, alerting, and recovery without logging Secret values.

Do not create a two-member OpenBao Raft cluster across sens-ops and sens-cloud. Two voters require both members for quorum and do not provide the desired one-node-failure availability. The initial single OpenBao instance uses encrypted off-host snapshots and tested restore. Later OpenBao HA requires at least three real failure domains and a separately approved topology.

Dedicated and customer-owned installations

Section titled “Dedicated and customer-owned installations”

The same platform images and sens-platform OCI chart are promoted to every installation. Dedicated capability must not create codebase forks.

A SENS-hosted dedicated customer receives an isolated platform installation, preferably a dedicated cluster when that isolation level is sold. It has its own:

  • PostgreSQL/TimescaleDB state;
  • NATS streams and credentials;
  • Kubernetes identities and Secrets;
  • OpenBao mounts or a separate approved Secret backend;
  • domains, certificates, values, backups, and release selection;
  • Argo Project, cluster credentials, and destination permissions;
  • monitoring labels and access policy.

The shared sens-ops management plane may manage several such installations, but a permission or configuration defect for one installation must not grant access to another.

A customer-owned installation consumes the same immutable artifacts and may use:

  • a customer-owned GitOps repository;
  • a local Argo CD instance;
  • customer-provided Kubernetes Secrets or another supported Secret adapter;
  • a customer registry mirror;
  • local monitoring with optional bounded outbound operational signals.

Central SENS connectivity must not be a runtime requirement for ingestion, storage, API access, or local recovery. Advanced air-gapped delivery remains a later capability, but the artifact and configuration contracts must not make it impossible.

When the first real additional installation is implemented, operational models must distinguish:

installation -> cluster -> environment -> workloads

Current hard-coded Test and Production assumptions may be refactored only with that concrete installation requirement. Do not build a speculative generic fleet framework before the first supported additional installation.

Installations may run different supported stable platform versions. Release metadata, upgrade compatibility, database migration sequencing, support windows, and rollback or forward-fix guidance therefore become installation-scoped.

Two physical servers provide failure and security isolation, not high availability. Each initial single-node cluster remains a single point of failure. Do not combine the two servers into one two-node k3s cluster and call it HA.

Later embedded-etcd k3s HA requires at least three server nodes with an odd quorum. OpenBao HA similarly requires a separately designed odd quorum across real failure domains. Those expansions are later operational decisions and do not block the initial two-server separation.

The current rebuild starts with empty state and does not migrate or preserve existing data. It therefore has no backup or restore prerequisite and retains no old runtime as rollback. A failed phase is repaired forward or rebuilt from Git and the bootstrap instructions.

Before real customer Production data or irreplaceable Production credentials are accepted, backups must use a third failure domain. The other physical server, a RAID mirror, a Kubernetes PVC, or a local volume is not the only acceptable backup. Define and prove:

  • encrypted external destinations;
  • PostgreSQL full backup and WAL strategy;
  • k3s datastore backup;
  • OpenBao Raft snapshots and recovery material;
  • Ops Valkey and required encryption-key recovery;
  • retention, RPO, RTO, monitoring, and responsible roles;
  • isolated restores for platform data, OpenBao, and the management plane.

The change is a gated hard cut, not a compatibility migration. Each phase ends in a directly verifiable state before the next phase starts:

  1. Finish and verify the current Test and Production CI/release baseline.
  2. Record terminology, boundaries, ownership, and the execution plan.
  3. Copy active Ops source into the unified root pnpm workspace without Git history or old runtime data.
  4. Introduce affected-scope CI with transitive dependency routing and fail-safe full validation.
  5. Publish independent immutable SENS Platform and SENS Ops OCI releases.
  6. Rebuild sens-platform-infra as the exact desired state for both clusters.
  7. Prepare network, firewall, DNS, operating systems, and both physical servers.
  8. Initialize the sens-ops cluster and its fresh management services.
  9. Initialize the sens-cloud cluster with isolated Test and Production state.
  10. Deploy Test automatically, promote Production manually, and switch DNS only after live verification.
  11. Prove selective CI, automatic Ops release, dependency direction, and both one-server failure cases.
  12. Remove Coolify and archive obsolete repository and runtime paths.
  13. Rehearse a clean customer-owned installation from released artifacts and documented inputs only.

The authoritative command-level checklist, responsibility split, DNS table, gates, and evidence record are maintained in the rollout plan.

  • Platform and management failures no longer share one physical host or Kubernetes control plane.
  • Operations history and recovery tooling can remain available during a platform outage.
  • Most application and Ops development uses one source repository, one branch, one review, and one human push.
  • The GitOps fleet write boundary remains separate from ordinary source changes.
  • OCI charts and images form immutable, portable, auditable release artifacts.
  • Dedicated and customer-owned installations consume the same release without code forks.
  • Each installation can select a supported version independently.
  • The same architecture can grow from the first two servers to additional platform clusters and later HA.
  • Coolify is removed without replacing it with a proprietary runtime dependency.
  • Two k3s clusters require separate bootstrap, upgrades, backups, network policy, certificate, storage, and recovery procedures.
  • Argo CD and OpenBao cross-cluster access introduce sensitive remote API and TLS contracts.
  • The management server becomes a high-value trust boundary and a fleet-level operational single point of failure until later HA.
  • A source-monorepo CI needs explicit change routing and independent product and Ops release lifecycles.
  • A separate GitOps repository means some infrastructure-only work still needs a separate reviewed commit.
  • OCI chart publication, authentication, immutability, and provenance add a new release contract.
  • Multiple installations introduce version skew, support-window, migration, monitoring-label, and customer-isolation responsibilities.
  • The hard cut intentionally gives up data migration and old-runtime rollback; failed cutovers require a forward fix or a clean rebuild.

Keep three source repositories permanently

Section titled “Keep three source repositories permanently”

Rejected as the target because product and Ops development, documentation, AI-assisted review, and coordinated source changes are unnecessarily split. The GitOps repository is retained because its write boundary is materially different.

Put source and all live GitOps desired state in one repository

Section titled “Put source and all live GitOps desired state in one repository”

Rejected because repository-level write credentials cannot be restricted to a GitOps folder, the desired state will later control multiple customer installations, and source and fleet changes require different review and audit rules.

Rejected as the production target because power, disk, kernel, network, host compromise, maintenance, or resource exhaustion would remove the product and the tools needed to diagnose and recover it together. Two VMs on one host remain an acceptable temporary development or migration topology, not physical fault isolation.

Build one two-node k3s or OpenBao Raft cluster

Section titled “Build one two-node k3s or OpenBao Raft cluster”

Rejected because it couples management and platform failure domains without an odd quorum and does not provide the claimed HA behavior.

Rejected because general build jobs execute repository code and dependencies. They must not share a privileged host boundary with fleet deployment and Secret management unless they are ephemeral and strongly isolated.

Rejected because independently edited chart, image, runtime, and GitOps versions can drift. CI derives and validates every concrete artifact version from the one approved platform release line.

Create customer-specific code or chart forks

Section titled “Create customer-specific code or chart forks”

Rejected because forks prevent controlled promotion, security fixes, support, and replaceable customer-owned deployment. Installation differences belong in values, Secret references, topology, and supported adapters.

The repository consolidation, CI routing, and two-cluster infrastructure rebuild have an expected platform SemVer impact of none when they do not change product runtime or public contracts.

Publishing an officially supported OCI Helm artifact and a dedicated installation contract is expected to have minor release impact. This ADR does not authorize that version bump. The required human must still select the SemVer component through the existing version workflow.

The initial topology requires no application database migration. Later fleet identity, Ops state, Secret topology, or customer-installation work may require explicit migrations and compatibility notes when its concrete contract is approved.

Implementation is not complete until:

  • the source snapshot is complete, no nested repository or second lockfile remains, and unrelated working-tree changes are preserved;
  • product, Ops, GitOps, and release CI report the correct stable required status for leaf-service, transitive-shared-package, chart-only, Ops-only, docs-only, root-lockfile, shared-CI, and unknown-path changes;
  • scheduled full validation and full validation before stable SENS Platform Production promotion are active;
  • an Ops-affecting merge automatically publishes, selects, deploys, and verifies one version-consistent first-party SENS Ops release;
  • Test and Production Helm renders validate exact versions and runtime configuration;
  • OCI chart pulls, authentication, immutability, and provenance are tested;
  • Argo CD manages local sens-ops and remote sens-cloud only within approved projects and destinations;
  • an sens-ops outage leaves the existing platform runtime functional;
  • an sens-cloud outage remains observable from sens-ops;
  • OpenBao, Kubernetes Secret retention, TokenReview, and credential rotation are tested across a network outage;
  • no Secret values enter Git, Helm values, logs, metrics, CI artifacts, or documentation;
  • both clusters can be rebuilt from clean state without relying on the retired runtime or undocumented manual state;
  • backup and isolated restore gates for platform data, OpenBao, and the management plane are documented as mandatory before real customer Production data is accepted;
  • the current and target operating models, hard cut, forward-fix strategy, security, configuration, and Secret handoff are documented;
  • a dedicated-installation fixture proves that one release can be deployed without customer-specific code or chart forks.