Openship Platform Factory Runtime Configuration: How the Control-Plane Boots Across Deployment Targets
The Openship Platform factory runtime configuration uses a tree-shakable createPlatform() factory in platform.ts to compose a concrete Platform instance—bundling Runtime, Routing, SSL, System, and Executor layers—based on the deployment target (cloud, selfhosted, or desktop) and runtime mode (docker or bare).
The Openship control-plane from the oblien/openship repository relies on a sophisticated factory pattern to handle diverse deployment scenarios. Understanding the Openship Platform factory runtime configuration is essential for developers extending the platform or debugging startup behavior, as it determines how the system initializes Docker containers, manages SSL certificates, and executes commands across cloud, self-hosted, and desktop environments.
How the Factory Works
The factory implementation in packages/adapters/src/platform.ts serves as the single entry point for platform initialization. It reads a configuration object and assembles the appropriate concrete implementations for the target environment.
Configuration Input (PlatformConfig)
The factory expects a PlatformConfig object that specifies the deployment characteristics. According to the type definition at lines 56‑84 of platform.ts, this includes:
target(cloud|selfhosted|desktop) – Determines the overall deployment modelruntime(docker|bare) – Optional, only applicable forselfhostedtargets- Connection details – Docker socket paths, SSH credentials, Nginx options, or cloud API tokens
This configuration drives the conditional instantiation logic throughout the factory.
Target Selection Logic
At lines 28‑38 of platform.ts, the createPlatform() function uses a switch statement to branch into specialized factory functions:
switch (config.target) {
case "cloud": return createCloudPlatform(config);
case "desktop": return createDesktopPlatform(config);
default: return createSelfHostedPlatform(config);
}
Each branch returns a fully configured Platform object tailored to the target environment.
Self-Hosted Path Details
The createSelfHostedPlatform() function handles the most complex initialization sequence. It determines whether the control-plane runs locally or remotely via SSH, then instantiates the appropriate command executor (LocalExecutor or pooled SshExecutor) through createExecutor().
At lines 70‑79, the factory selects the runtime implementation:
// Runtime selection (platform.ts lines 70-79)
const runtime = config.runtime === "docker"
? await DockerRuntime.create(config, executor)
: new BareRuntime(config, executor);
Following runtime creation, lines 91‑98 invoke createInfraProvider to assemble routing and SSL handling, typically using TraefikProvider for Docker environments or Nginx configurations for bare-metal setups. The factory also instantiates a SystemManager to perform prerequisite checks and install required dependencies (Docker, Git, Node.js) unless running in Docker-edge mode.
Cloud and Desktop Paths
For cloud deployments, the factory returns lightweight stubs: CloudRuntime paired with CloudInfraProvider, which delegates infrastructure management to the Oblien API rather than manipulating local systems. Desktop targets use BareRuntime with a NoopInfraProvider, as local development requires no reverse-proxy or SSL provisioning.
Singleton Caching Mechanism
After creation, the platform instance is stored in a module-level variable _platform (lines 81‑93). The system exposes a synchronous getPlatform() accessor for the rest of the codebase to retrieve the initialized instance without async overhead. For testing scenarios, resetPlatform() clears the cache to allow re-initialization (lines 95‑105).
Platform Layers and Responsibilities
The Openship Platform factory runtime configuration composes five distinct layers, selecting concrete implementations based on the deployment matrix:
| Layer | Responsibility | Self-Hosted (Docker) | Self-Hosted (Bare) | Cloud | Desktop |
|---|---|---|---|---|---|
| Runtime | Build, deploy, and lifecycle management | DockerRuntime (Dockerode, socket/SSH/TLS) |
BareRuntime (local/SSH executor) |
CloudRuntime (Oblien API) |
BareRuntime (local executor) |
| Routing | Reverse-proxy configuration | TraefikProvider (writes YAML via executor) |
TraefikProvider |
CloudInfraProvider (API) |
NoopInfraProvider |
| SSL | TLS provisioning | TraefikProvider (ACME) |
TraefikProvider |
CloudInfraProvider |
NoopInfraProvider |
| System | Prerequisite checks and installers | SystemManager (docker, git, node) |
SystemManager |
none | none |
| Executor | Command execution on target host | LocalExecutor or SshExecutor |
LocalExecutor |
none | none |
The factory is tree-shakable, meaning only the code paths required for the selected target are imported into the final bundle. This keeps server startup times minimal and reduces memory footprint across different deployment scenarios.
Implementation Example: Initializing and Using the Platform
The following pattern demonstrates how to initialize the Openship Platform factory runtime configuration and interact with the composed platform instance:
// 1️⃣ Initialise the platform on server start (once)
import { initPlatform } from "@repo/adapters";
await initPlatform({
target: "selfhosted", // or "cloud"/"desktop"
runtime: "docker", // only for self‑hosted
docker: { socketPath: "/var/run/docker.sock" },
nginx: { /* Nginx options */ },
// Optional SSH config for remote self‑hosted boxes
// ssh: { host: "my.server", user: "root", privateKey: "..."}
});
// 2️⃣ Obtain the cached platform anywhere in the codebase
import { getPlatform } from "@repo/adapters";
const { runtime, routing, ssl, system, executor } = getPlatform();
// 3️⃣ Build a project (example from the CLI)
await runtime.build(buildConfig, (msg) => console.log(msg));
// 4️⃣ Register a reverse‑proxy route
await routing.registerRoute({
domain: "app.example.com",
targetUrl: "http://127.0.0.1:3000",
tls: true,
});
// 5️⃣ Provision an TLS certificate (ACME via Traefik/OpenResty)
await ssl.provisionCert("app.example.com");
// 6️⃣ Run a one‑off command on the target host (e.g. git pull)
if (executor) {
const result = await executor.exec("git -C /srv/app pull");
console.log(result);
}
This code works identically across Docker-based, Bare-runtime, Cloud, or Desktop deployments because the factory abstracts the concrete implementations behind stable interfaces.
Key Source Files
Understanding the Openship Platform factory runtime configuration requires familiarity with these specific modules:
packages/adapters/src/platform.ts– Contains the factory implementation (createPlatform,initPlatform,getPlatform), thePlatformtype definition, and the singleton cache logicpackages/adapters/src/runtime/docker.ts– ImplementsDockerRuntimewith methods for building images, deploying containers, and executing health checks via Dockerodepackages/adapters/src/runtime/bare.ts– ImplementsBareRuntimefor executor-based process management on host systems without containerizationpackages/adapters/src/infra/traefik.ts– DefinesTraefikProvider, which writes YAML configuration files to the target host via the executor to manage reverse-proxy rulespackages/adapters/docs/ARCHITECTURE.md– High-level architectural overview mapping the relationship between factory componentsapps/api/src/lib/deployment-runtime.ts– Reference implementation showing how the API layer consumes the platform instance
Summary
- The Openship Platform factory runtime configuration centers on the
createPlatform()factory function inpackages/adapters/src/platform.ts, which switches betweencloud,desktop, andselfhostedinitialization paths. PlatformConfigdrives the composition of five layers: Runtime, Routing, SSL, System, and Executor, with implementations ranging fromDockerRuntimetoBareRuntimeandTraefikProvidertoNoopInfraProvider.- The factory instantiates either a
LocalExecutororSshExecutorfor self-hosted targets to handle remote command execution, while cloud deployments use API-based providers. - After async initialization,
getPlatform()provides synchronous access to the cached platform instance throughout the application lifecycle. - The architecture is tree-shakable, ensuring that only the code necessary for the specific deployment target is loaded into memory.
Frequently Asked Questions
What is the role of createPlatform() in Openship?
createPlatform() is the central factory function exported from packages/adapters/src/platform.ts (lines 28‑38). It examines the target property of the PlatformConfig object and delegates to specialized creators like createCloudPlatform(), createDesktopPlatform(), or createSelfHostedPlatform() to assemble the correct combination of runtime, routing, and executor components for the deployment environment.
How does Openship handle different deployment targets?
Openship handles targets through conditional instantiation. For selfhosted, it constructs either a DockerRuntime or BareRuntime paired with a SystemManager and TraefikProvider. For cloud, it returns CloudRuntime and CloudInfraProvider stubs that communicate with the Oblien API. For desktop, it uses BareRuntime with a NoopInfraProvider since no reverse-proxy or system management is required locally.
What is the difference between DockerRuntime and BareRuntime?
DockerRuntime, defined in packages/adapters/src/runtime/docker.ts, uses Dockerode to interact with Docker sockets or remote daemons over SSH/TLS for containerized builds and deployments. BareRuntime, from packages/adapters/src/runtime/bare.ts, executes commands directly on the host system using a configured executor (local or SSH) without containerization, suitable for legacy servers or desktop environments.
How do I access the platform instance after initialization?
Call the synchronous getPlatform() function imported from @repo/adapters. This retrieves the module-level singleton created by the initial initPlatform() call. If you need to reinitialize—for example, during integration testing—use resetPlatform() to clear the internal _platform cache before calling initPlatform() again with new configuration.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →