Skip to content

Magic Proxy System ​

TypeKro uses two proxy systems to make resource references feel natural while generating CEL expressions under the hood.

Two Types of Proxies ​

TypeKro has two distinct proxy types:

Proxy TypeWhat It WrapsExample AccessGenerated CEL
Schema ProxyThe spec parameterspec.name${schema.spec.name}
Magic ProxyResources (Deployment, Service, etc.)deploy.status.readyReplicas${deploy.status.readyReplicas}

Both create KubernetesRef objects at runtime, but they reference different things:

  • Schema Proxy: References input values from your composition's spec
  • Magic Proxy: References live Kubernetes resource state

The Problem They Solve ​

In Kubernetes, resources reference each other's live state. A Deployment's environment variable might need a Service's cluster IP - but that IP doesn't exist until the Service is created.

TypeKro's proxies let you write natural TypeScript that becomes runtime-aware CEL expressions.

typescript
import { kubernetesComposition } from 'typekro';
import { Deployment, Service } from 'typekro/simple';
import { type } from 'arktype';

const composition = kubernetesComposition({
  name: 'webapp',
  apiVersion: 'example.com/v1',
  kind: 'WebApp',
  spec: type({ name: 'string', image: 'string' }),
  status: type({ dbHost: 'string' })
}, (spec) => {  // ← spec is a Schema Proxy
  const dbService = Service({
    id: 'dbSvc',
    name: 'db-service',
    selector: { app: 'db' },
    ports: [{ port: 5432 }]
  });  // ← dbService is wrapped with a Magic Proxy

  const app = Deployment({
    id: 'app',
    name: spec.name,                        // Schema Proxy → ${schema.spec.name}
    image: spec.image,                      // Schema Proxy → ${schema.spec.image}
    env: { DB_HOST: dbService.status.clusterIP }  // Magic Proxy → ${dbSvc.status.clusterIP}
  });

  return { dbHost: dbService.status.clusterIP };
});

Schema Proxy: Referencing Input Values ​

The spec parameter in your composition function is a Schema Proxy. Access its properties to create references to input values:

typescript
import { type } from 'arktype';
import { kubernetesComposition } from 'typekro';
import { Deployment } from 'typekro/simple';

const composition = kubernetesComposition({
  name: 'webapp',
  apiVersion: 'example.com/v1',
  kind: 'WebApp',
  spec: type({ name: 'string', image: 'string', replicas: 'number' }),
  status: type({ appName: 'string' })
}, (spec) => {
  const app = Deployment({
    id: 'app',
    name: spec.name,        // Becomes: ${schema.spec.name}
    image: spec.image,      // Becomes: ${schema.spec.image}
    replicas: spec.replicas // Becomes: ${schema.spec.replicas}
  });
  
  return {
    appName: spec.name      // Schema reference in status
  };
});

Magic Proxy: Referencing Resource State ​

Resources returned by factory functions are wrapped with Magic Proxies. Access their properties to create references to live Kubernetes state:

typescript
import { type } from 'arktype';
import { kubernetesComposition } from 'typekro';
import { Deployment, Service } from 'typekro/simple';

const composition = kubernetesComposition({
  name: 'webapp',
  apiVersion: 'example.com/v1',
  kind: 'WebApp',
  spec: type({ name: 'string' }),
  status: type({ ready: 'number', endpoint: 'string' })
}, (spec) => {
  const deployment = Deployment({ 
    id: 'deploy', 
    name: 'web-app', 
    image: 'nginx:latest' 
  });
  
  const service = Service({
    id: 'svc',
    name: 'web-service',
    selector: { app: 'web-app' },
    ports: [{ port: 80 }]
  });
  
  return {
    ready: deployment.status.readyReplicas,  // ${deploy.status.readyReplicas}
    endpoint: service.status.clusterIP       // ${svc.status.clusterIP}
  };
});

JavaScript Expressions Work! ​

TypeKro automatically converts JavaScript expressions to CEL:

typescript
// ✅ These all work - auto-converted to CEL
return {
  ready: app.status.readyReplicas > 0,
  allReady: app.status.readyReplicas >= spec.replicas,
  url: `https://${service.status.clusterIP}`,
  phase: app.status.readyReplicas > 0 ? 'running' : 'pending'
};

When to Use Explicit CEL ​

Only use Cel.expr() for advanced list operations:

typescript
import { Cel } from 'typekro';

// ❌ Array methods don't convert
length: deployment.spec.containers.length

// ✅ Use explicit CEL for list operations
containerCount: Cel.size(deployment.spec.template.spec.containers),
podNames: Cel.expr('pods.map(p, p.metadata.name)'),

Cross-Resource References ​

Resources can reference each other. The id field enables this (see Resource IDs for details):

typescript
import { type } from 'arktype';
import { kubernetesComposition } from 'typekro';
import { Deployment, Service } from 'typekro/simple';

const composition = kubernetesComposition({
  name: 'fullstack',
  apiVersion: 'example.com/v1',
  kind: 'FullStack',
  spec: type({ name: 'string', image: 'string' }),
  status: type({ databaseReady: 'boolean', appReady: 'boolean', ready: 'boolean' })
}, (spec) => {
  const postgres = Deployment({ 
    id: 'db',  // id enables references to this resource
    name: 'postgres', 
    image: 'postgres:15' 
  });
  
  const dbService = Service({
    id: 'dbSvc',
    name: 'postgres-service',
    selector: { app: 'postgres' },
    ports: [{ port: 5432 }]
  });
  
  // App references database service
  const app = Deployment({
    id: 'app',
    name: spec.name,
    image: spec.image,
    env: {
      DATABASE_HOST: dbService.status.clusterIP  // Cross-resource reference
    }
  });
  
  return {
    databaseReady: postgres.status.readyReplicas > 0,
    appReady: app.status.readyReplicas > 0,
    ready: postgres.status.readyReplicas > 0 && app.status.readyReplicas > 0
  };
});

Type Safety ​

The proxy maintains full TypeScript type safety:

typescript
const app = Deployment({ id: 'app', name: 'web-app', image: 'nginx:latest' });

// ✅ Proper types
app.metadata.name;              // string
app.spec.replicas;              // number  
app.status.readyReplicas;       // number

// ❌ Compile-time errors
app.spec.invalidField;          // Property doesn't exist

Debugging ​

Use toYaml() to see generated CEL expressions:

typescript
const yaml = composition.toYaml({ name: 'test', image: 'nginx' });
console.log(yaml);  // See actual CEL expressions

Next Steps ​

Released under the Apache 2.0 License.