Skip to content

API Reference

Complete API documentation for tsops.

Core

defineConfig()

Define your tsops configuration with full type safety and runtime helpers.

typescript
import { defineConfig } from 'tsops'

export default defineConfig({
  project: 'demo',

  namespaces: {
    dev: { domain: 'dev.example.com', production: false, replicas: 1 },
    prod: { domain: 'example.com', production: true, replicas: 3 }
  },

  clusters: {
    prod: {
      apiServer: 'https://prod.local:6443',
      context: 'prod',
      namespaces: ['prod']
    }
  },

  images: {
    registry: 'ghcr.io/acme',
    tagStrategy: 'git-sha',
    includeProjectInName: true
  },

  secrets: {
    'api-secrets': ({ production }) => ({
      JWT_SECRET: production
        ? process.env.JWT_SECRET ?? ''
        : 'dev-secret'
    })
  },

  configMaps: {
    settings: { LOG_LEVEL: 'info' }
  },

  apps: {
    api: {
      build: {
        type: 'dockerfile',
        context: './api',
        dockerfile: './api/Dockerfile'
      },
      // Single object format - protocol is optional (auto-detects based on domain)
      ingress: ({ domain }) => ({ domain: `api.${domain}` }),
      // Or with explicit protocol:
      // ingress: ({ domain, production }) => ({
      //   domain: `api.${domain}`,
      //   protocol: production ? 'https' : 'http'
      // }),
      env: ({ secret }) => ({
        JWT_SECRET: secret('api-secrets', 'JWT_SECRET')
        // ✅ In your app: config.url('postgres', 'service')
      }),
      ports: [{ name: 'http', port: 80, targetPort: 8080 }]
    }
  }
})

process.env access happens at config-evaluation time, so make sure the variables you need are defined before running tsops.

defineConfig ensures all namespaces share the same shape and returns an object that preserves your configuration plus runtime helpers (env, dns, url).

TsOps

Programmatic API for planning, building, and deploying.

typescript
import { createNodeTsOps } from '@tsops/node'
import config from './tsops.config.js'

const tsops = createNodeTsOps(config, { dryRun: true })

const plan = await tsops.planWithChanges({ namespace: 'prod' })
const buildResult = await tsops.build({ app: 'api' })
await tsops.deploy({ namespace: 'prod', app: 'api' })

createNodeTsOps bundles the Node adapters (command runner, Docker, kubectl, Git-aware environment provider). If you are targeting a different runtime, instantiate TsOps directly and provide implementations for the DockerClient and KubectlClient ports exported by @tsops/core.

Use plan() for resolved entries only, or planWithChanges() to include cluster validation, diffs, and orphan detection.

CLI

plan

Validate manifests, diff cluster state, and list orphaned resources without applying changes.

bash
tsops plan [options]

Options:

  • -n, --namespace <name> - Target a single namespace
  • --app <name> - Target a single app
  • -c, --config <path> - Config file path (defaults to tsops.config)
  • --dry-run - Skip Docker/kubectl execution, log actions only

The output groups:

  • Global resources (namespaces, secrets, configMaps) validated once per unique artifact
  • Per-app changes with diffs (suppressed when --dry-run is used)
  • Orphaned resources that would be deleted by deploy
  • A summary that fails the command if validation errors occur

build

Resolve image references and invoke Docker builds/pushes.

bash
tsops build [options]

Options:

  • --app <name> - Target a single app
  • -n, --namespace <name> - Use namespace context (influences env functions)
  • -c, --config <path> - Config file path
  • --dry-run - Log Docker commands without executing

deploy

Generate manifests, validate secret placeholders, apply resources atomically, and clean up orphans.

bash
tsops deploy [options]

Options:

  • -n, --namespace <name> - Target a single namespace
  • --app <name> - Target a single app
  • -c, --config <path> - Config file path
  • --dry-run - Log kubectl actions without executing

Context Helpers

Functions available in app configuration.

Metadata

typescript
project: string         // Current project name
namespace: string       // Current namespace
appName: string         // Current app name
cluster: ClusterMetadata // Cluster info
// + all namespace variables (e.g., production, replicas, domain, etc.)

Service Discovery

⚠️ Do NOT use ENV variables for internal service endpoints.

Use runtime config in your application code:

typescript
// ✅ In your app code:
import config from './tsops.config'

const API_URL = config.url('api', 'service')  // http://api
const POSTGRES_URL = config.url('postgres', 'service')  // http://postgres
const REDIS_URL = config.url('redis', 'service')  // http://redis

// Full cluster DNS (cross-namespace safe):
const API_URL = config.url('api', 'cluster')  // http://api.prod.svc.cluster.local

// Public ingress:
const PUBLIC_URL = config.url('api', 'ingress')  // https://api.example.com

Benefits: Type-safe, namespace-aware, single source of truth.

Use ENV variables only for:

  • Secrets (passwords, API keys)
  • External services (outside the cluster)
  • Feature flags
  • Build-time values

secret()

typescript
secret(secretName: string): SecretRef  // Reference entire secret (envFrom)
secret(secretName: string, key: string): SecretRef  // Reference specific key

Simplified API for secret references.

Examples:

typescript
env: ({ secret }) => secret('api-secrets')  // All keys as env vars
env: ({ secret }) => ({
  JWT_SECRET: secret('api-secrets', 'JWT_SECRET')  // Specific key
})

configMap()

typescript
configMap(name: string): ConfigMapRef  // Reference entire configMap
configMap(name: string, key: string): ConfigMapRef  // Reference specific key

label()

typescript
label(key: string, value?: string): string

Generate resource labels following project conventions.

resource()

typescript
resource(kind: ResourceKind, name: string): string

Generate resource name following project conventions.

env()

typescript
env<T extends string>(key: string, fallback?: T): T

Get environment variable with optional fallback.

template()

typescript
template(str: string, vars: Record<string, string>): string

Simple template string helper.

View All Helpers →

Types

TsOpsConfig

Simplified shape of the main configuration type.

typescript
interface TsOpsConfig {
  project: string
  namespaces: Record<string, NamespaceVars>
  clusters: Record<string, {
    apiServer: string
    context: string
    namespaces: readonly string[]
  }>
  images: {
    registry: string
    tagStrategy: 'git-sha' | 'git-tag' | 'timestamp' | string
    repository?: string
    includeProjectInName?: boolean
  }
  secrets?: Record<string, SecretDefinition>
  configMaps?: Record<string, ConfigMapDefinition>
  apps: Record<string, AppDefinition>
}

NamespaceVars must be the same shape for every namespace and becomes part of the helper context.

AppDefinition

Application definition (runtime helpers use the same generics internally).

typescript
interface AppDefinition {
  image?: string
  build?: DockerfileBuild | Record<string, unknown>
  env?: Record<string, EnvValue> | ((ctx: AppContext) => Record<string, EnvValue> | SecretRef | ConfigMapRef)
  secrets?: Record<string, Record<string, string>> | ((ctx: AppContext) => Record<string, Record<string, string>>)
  configMaps?: Record<string, Record<string, string>> | ((ctx: AppContext) => Record<string, Record<string, string>>)
  network?: string | boolean | AppNetworkOptions | ((ctx: AppContext) => string | boolean | AppNetworkOptions)
  deploy?: 'all' | readonly string[] | { include?: readonly string[]; exclude?: readonly string[] }
  ports?: ServicePort[]
  podAnnotations?: Record<string, string>
  volumes?: Volume[]
  volumeMounts?: VolumeMount[]
  args?: string[]
}

The ingress field uses a simple object format:

typescript
ingress: ({ domain }) => ({ 
  domain: `api.${domain}`,
  protocol: 'https'  // optional, auto-detects if omitted
})

Protocol auto-detection:

  • *.localtest.me, localhost, *.localhttp (no certificate warnings)
  • Other domains → https (with TLS)
  • Explicit protocol overrides auto-detection for special cases

The protocol is automatically used by the url() runtime helper when called with type 'ingress'.

Runtime

env()

Get environment variable for an app.

typescript
import config from './tsops.config.js'

process.env.TSOPS_NAMESPACE = 'prod'

const nodeEnv = config.env('api', 'NODE_ENV')
const port = config.env('api', 'PORT')
console.log(nodeEnv, port)

dns()

Generate DNS name for different types of resources.

typescript
const clusterDns = config.dns('api', 'cluster')     // 'api.prod.svc.cluster.local'
const serviceDns = config.dns('api', 'service')     // 'api'
const ingressDns = config.dns('api', 'ingress')     // 'api.prod.example.com'

url()

Generate complete URL for different types of resources with automatic protocol resolution.

typescript
const clusterUrl = config.url('api', 'cluster')     // 'http://api.prod.svc.cluster.local'
const serviceUrl = config.url('api', 'service')     // 'http://api'
const ingressUrl = config.url('api', 'ingress')     // Protocol from ingress config
                                                     // 'http://api.dev.localtest.me' (dev)
                                                     // 'https://api.example.com' (prod)

The protocol for 'ingress' type is determined from the ingress configuration in your app definition. For 'cluster' and 'service' types, http is always used (internal communication).

The active namespace is determined by TSOPS_NAMESPACE; when unset, the first namespace in your config is used.

Next Steps

Released under the MIT License.