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 model
  • runtime (docker | bare) – Optional, only applicable for selfhosted targets
  • 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:

Summary

  • The Openship Platform factory runtime configuration centers on the createPlatform() factory function in packages/adapters/src/platform.ts, which switches between cloud, desktop, and selfhosted initialization paths.
  • PlatformConfig drives the composition of five layers: Runtime, Routing, SSL, System, and Executor, with implementations ranging from DockerRuntime to BareRuntime and TraefikProvider to NoopInfraProvider.
  • The factory instantiates either a LocalExecutor or SshExecutor for 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →