Openship Stack Detection and Project Types: How the Platform Classifies Repositories

Openship analyzes every repository with a two-step detection pipeline—workspace detection followed by stack detection—to assign one of four canonical project types (app, docker, services, or monorepo) that controls the entire deployment lifecycle.

Openship, an open-source deployment platform in the oblien/openship repository, automatically inspects imported codebases to determine how they should be built and served. Understanding Openship stack detection and project types is essential for teams that want to know why a repository is categorized as a monorepo, a Docker-backed service, or a single framework application. The classification logic lives in the core workspace and stack modules and propagates through the database schema, API layer, and frontend deployment context.

How Openship Detects Repository Types

Openship identifies the nature of a codebase through two distinct phases. First, it checks whether the repository is a monorepo workspace; if not, it falls back to framework-level stack detection.

Workspace Detection for Monorepos

The workspace detection phase relies on the core WORKSPACE_DETECTORS registry defined in packages/core/src/workspaces/index.ts. Each detector implements the WorkspaceDetector interface and declares which manifest files to search for, along with optional sub-project parsing patterns. Individual implementations—such as the Cargo workspace detector in packages/core/src/workspaces/cargo.ts—run cheaply against the repository root via project-root-detector. When a detector matches, it contributes a projectType of "monorepo" and exposes the workspace structure for later UI rendering.

Stack Detection for Application Frameworks

If no workspace is detected, Openship proceeds to stack detection via packages/core/src/stacks.ts. Each entry in the stack definitions provides optional fileMatch and depMatch patterns that trigger recognition of a primary framework such as Next.js, Remix, Vite, or Docker. The result of this step is a projectType classification that drives the rest of the deployment pipeline.

The Four Canonical Project Types

Once detection completes, the repository is assigned one of four canonical values. These types influence database schema defaults, API payloads, and UI behavior throughout the platform.

app — Single Application Source

The app type represents classic web applications that compile into a single containerized unit. This value is declared in the TypeScript ProjectType enum located at apps/dashboard/src/context/deployment/types.ts and is the default classification for most framework-based repositories that do not use workspaces or Docker Compose.

docker — Container-First Deployments

A docker project signals a Dockerfile-based deployment without extra service introspection. This type directly influences the runtimeMode selection and is consumed by the deployment build logic in apps/dashboard/src/context/deployment/useDeploymentBuild.tsx.

services — Multi-Service Compose Projects

The services type applies to Docker Compose projects where each service is treated as a separate deployable unit. It activates service-specific UI paths and is referenced by apps/dashboard/src/context/deployment/useDeploymentConfig.ts when normalizing build and runtime behavior for multi-container setups.

monorepo — Workspace-Backed Repositories

When a workspace detector matches, the repository is labeled monorepo. This classification triggers monorepo-specific UI components such as apps/dashboard/src/components/import-project/MonorepoApps.tsx and informs the build pipeline in apps/dashboard/src/context/deployment/useDeploymentBuild.tsx that multiple sub-apps may need to be selected and deployed individually.

How Project Type Propagates Through the System

The detected projectType is not merely a label; it acts as a control flag that shapes database records, API contracts, and runtime configuration.

Database Schema and Flags

The project table schema in packages/db/src/schema/project.ts stores flags such as hasServer, hasBuild, framework, packageManager, and composePath. Although the schema does not contain a literal projectType column, the detection outcome determines the default values for these fields, including runtimeMode and routeStrategy.

API Contract and Validation

The public API documentation in apps/web/content/docs/api/projects.mdx lists the allowed enum values as "app" | "docker" | "services" | "monorepo". The backend endpoint for creating a project—POST /projects in apps/dashboard/src/lib/api/projects.ts—accepts and validates this field, ensuring downstream logic receives a supported classification.

Build and Runtime Configuration

Throughout the dashboard, the DeploymentConfig object carries the projectType value. Helper functions such as normalizeBuildStrategy and normalizeRuntimeMode inside apps/dashboard/src/context/deployment/useDeploymentConfig.ts branch on this type to select the correct build command, start command, and routing logic. For example, docker and services projects automatically receive a "dockerfile" build strategy.

Routing and Runtime Selection

Routing defaults are derived directly from the detected type. According to the project schema, routeStrategy defaults to "auto" for app projects, "loopback-port" for services, and "container-ip" for docker deployments. This ensures that networking behavior matches the expected runtime topology without manual intervention.

Practical Code Examples

The following snippets demonstrate how detection results are consumed across the Openship codebase.

// Example: Determining the project type while scanning a repo (core)
import { detectStack } from "@repo/core";
import { detectWorkspace } from "@repo/core/workspaces";

async function classifyRepo(rootPath: string) {
  const workspace = await detectWorkspace(rootPath);
  const stack = await detectStack(rootPath);
  // Workspace detection overrides stack detection for monorepos
  const projectType = workspace ? "monorepo" : stack?.projectType ?? "app";
  return projectType;
}
// Example: Creating a project via the dashboard API (frontend)
import { api } from "@/lib/api";

await api.post("/projects", {
  name: "My Next App",
  slug: "my-next-app",
  projectType: "app",               // ← one of the four enum values
  framework: "nextjs",
  packageManager: "pnpm",
  // …other fields are auto‑filled based on detection
});
// Example: Normalizing build strategy based on project type (dashboard)
function normalizeBuildStrategy(
  projectType: DeploymentConfig["projectType"],
  stackDef?: StackDefinition
): BuildStrategy {
  if (projectType === "docker" || projectType === "services") {
    return "dockerfile";
  }
  // fallback to stack‑specific defaults for app / monorepo
  return stackDef?.defaultBuildStrategy ?? "npm";
}

Summary

  • Openship classifies repositories via workspace detection in packages/core/src/workspaces/index.ts and stack detection in packages/core/src/stacks.ts.
  • The four canonical types are app, docker, services, and monorepo.
  • The detected type flows through the database schema, API contracts, and deployment configuration helpers such as normalizeBuildStrategy.
  • Routing defaults like routeStrategy are derived directly from the project type classification.

Frequently Asked Questions

What are the four Openship project types?

Openship assigns every repository one of four canonical values: app for single web applications, docker for Dockerfile-based containers, services for Docker Compose multi-service projects, and monorepo for workspace-based repositories containing multiple sub-apps.

How does Openship detect a monorepo?

The core WORKSPACE_DETECTORS registry in packages/core/src/workspaces/index.ts enumerates detectors such as the Cargo detector in packages/core/src/workspaces/cargo.ts. Each detector implements the WorkspaceDetector interface and scans the repo root for manifest files; a match returns projectType = "monorepo".

Which source file handles stack detection?

Generic stack detection logic resides in packages/core/src/stacks.ts, where each stack definition supplies optional fileMatch and depMatch patterns to identify frameworks like Next.js, Remix, or Vite.

How does project type affect runtime routing?

According to the project schema in packages/db/src/schema/project.ts, the routeStrategy field defaults to "auto" for app projects, "loopback-port" for services, and "container-ip" for docker deployments, ensuring the correct networking mode is applied automatically.

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 →