Design CI Plugin System

Medium40 min
1 / 30
understanding6 min read

Problem Statement: Extensible CI Plugin Platform

Problem Statement: Extensible CI Plugin Platform — CI plugin platform interview depth

Problem Statement: Extensible CI Plugin Platform

You are designing the plugin layer of a CI platform used by thousands of repositories—not the entire CI/CD product. Publishers ship versioned step bundles; pipeline authors reference them in YAML; the platform resolves, sandboxes, and executes each step with least privilege.

What interviewers want

  • A marketplace/registry with signing and moderation tiers
  • Plan-time version resolution to exact bundle digests (not floating latest)
  • Sandboxed workers that never mount the host Docker socket or platform credentials
  • A secret broker that maps manifest scopes to short-lived OIDC tokens

Scale anchors (12k repos, ~4k marketplace plugins, ~180k pipeline runs/day, ~1.2M plugin step executions/day, p95 +45s overhead vs native built-ins.)

Registry API peaks under 800 QPS; bundle CDN moves ~25 TB/day; centralized log ingest ~8 MB/s when 400 sandboxes stream concurrently.

Staff framing

Compare honestly to GitHub Actions (pinned action refs), Jenkins (shared libraries with classpath control), and CircleCI orbs (packaged reusable steps). Your differentiator is defense in depth: extensibility without giving publishers the keys to the control plane.

javaOne Dark Pro
1public final class PluginResolvePlan {
2 public ResolvedStep pin(String pluginId, String range, Map<String, String> digests) {
3 String digest = Semver.maxSatisfying(digests.keySet(), range)
4 .map(digests::get)
5 .orElseThrow(() -> new IllegalStateException("unresolved " + pluginId));
6 return new ResolvedStep(pluginId, digest);
7 }
8}
pythonOne Dark Pro
1def broker_token(step_id: str, scopes: list[str], vault: VaultClient) -> str:
2 if not scopes:
3 raise ValueError("empty scopes")
4 return vault.issue_oidc(step_id=step_id, scopes=scopes, ttl_seconds=900)
typescriptOne Dark Pro
1export interface PluginManifest {
2 pluginId: string;
3 inputs: Record<string, string>;
4 secretScopes: string[];
5 runtimeClass: "wasm" | "gvisor" | "vm";
6}
7
8export function validateManifest(m: PluginManifest): boolean {
9 return m.secretScopes.length > 0 && m.runtimeClass.length > 0;
10}

How to open this one

The framing that lands for an extensible CI plugin platform is a sandboxed extension model with a stable contract: third-party plugins run in isolation with scoped permissions, communicate through a versioned API, and cannot crash or compromise the core pipeline. Lead with the trust boundary — a plugin is untrusted code running inside your CI — and the failure story that proves it: a buggy or malicious plugin tries to read secrets or hang the build, and sandboxing plus resource limits contain it. That shows you understand extensibility and security are the same problem here.

Key Highlights

  • Pin plugin digests at plan time for reproducibility
  • Sandbox every step; broker secrets with scoped OIDC
  • Marketplace tiers: community vs certified publishers
  • Quarantine versions with elevated crash or egress rates
Say this aloud
I optimize **critical path** with registry CDN hits and parallel action width, then cap tail latency with preemption policy.
Production tip
Publish cache lookup latency histograms per region—registry CDN misses dominate monorepo pain.

Section Rescue Kit

Buzzwords to use:

Plugin ManifestContent-Addressed BundleSandbox Class

Safe statements:

  • "I separate **registry**, **scheduler**, and **sandbox workers** so a bad plugin cannot own the control plane."
  • "If signing or SBOM checks fail, I block publish rather than weaken verification."
Design CI Plugin System - System Design | WinJob | WinJob