OrkStageOrkStage

Importing infrastructure code

How the IaC import API and scan engine work. Supported sources, mapping rules, the ScanReport v1 format and the calls that create a draft template.

An import turns infrastructure code into a draft template that you review. One engine, orkstage-scan (written in Rust), does all detection and mapping. The ork CLI runs it natively, and the platform runs it compiled to WebAssembly (@orkstage/scan).

PathStatus
POST /imports { report }: send a ScanReport produced by ork import or ork scanAvailable
POST /imports { files }: upload files and let the server scan themAvailable. The API ships with the scan engine (@orkstage/scan, WebAssembly). A build without it answers 501 SCANNER_UNAVAILABLE.
ork import or ork scan on your machinePreview

Supported sources

Source kindWhat the engine reads
terraform.tf and .tf.json files; variables, locals, local modules, resource references
cloudformation, sam, cdkTemplates in YAML or JSON, including tags in short form; synthesized CDK output
kubernetes, helmDeployments, Services, Ingresses, StatefulSets; Chart.yaml
composeServices, ports, images and dependencies in docker-compose.yml or compose.yaml
dockerfileBase images and exposed ports
packagepackage.json, go.mod, requirements.txt and similar files, as hints (for example a Postgres driver)
pulumi, serverless, bicep, armDetected and reported

Detected resources map to the nine OrkStage types: network, compute.container-service, container-registry, database.postgresql, cache.redis, queue, object-storage, ingress and dns. Anything else appears in the unmapped list with a reason. Nothing is dropped silently.

Mapping rules in short

  • When several sources show the same component, it becomes one resource that keeps every source. For example, a Compose postgres service, pg in package.json and an aws_db_instance become one database.postgresql on AWS.
  • Evidence strength, from strongest: IaC, then Kubernetes, then Compose, Dockerfiles and package hints.
  • References become connections, for example a task definition that reads aws_db_instance.main.address.
  • The engine adds a network when services or data stores need one. It adds an ingress and DNS when a public HTTP service runs on a cloud provider.
  • Placement slots are chosen per provider: primary for the compute provider, and slots such as data, storage and messaging for the others. The engine also tags the template with its size (starter, growth or scale) and its contents.
  • Each resource has a confidence of high, medium or low.

API flow

Create the import

POST /api/v1/imports
Authorization: Bearer ork_live_…          # scope imports:write
Content-Type: application/json

{ "tenantId": "8a1c…", "report": { "version": 1, "generator": "orkstage-scan/0.1.0", … } }

To let the server scan the files instead, send them as text:

{ "tenantId": "8a1c…", "files": [ { "path": "infra/main.tf", "content": "resource \"aws_db_instance\" \"main\" { … }" } ] }

Send either files (text only, at most 2000 files and 5 MB in total) or report, never both. The response is an ImportDto:

interface ImportDto {
  id: string;
  tenantId: string | null;
  createdAt: string;
  sourceKinds: string[];        // e.g. ["terraform", "compose"]
  report: ScanReport;
  validation: { valid: boolean; issues: { stage: string; severity: string; code: string; message: string; path?: string }[] };
  templateId: string | null;    // set once a template was created from it
}

A report that is not a ScanReport version 1 gets 422 INVALID_REPORT.

Review

Call GET /api/v1/imports/:id, or open https://<console>/templates/import?importId=<id>. Check report.resources (with sources and confidence), report.unmapped, report.connections, report.warnings and validation.issues.

Create the draft template

POST /api/v1/imports/:id/template          # scope templates:write
Content-Type: application/json

{ "name": "shop", "displayName": "Shop" }

The response is { templateId, name, version: "0.1.0", status: "DRAFT" }. The API sets the name, display name and version in the YAML document itself, so the formatting and comments of the generated template survive. Templates are shared by all tenants, so this step needs a platform role: with an API key, an organization key with the PlatformAdmin role.

Possible errors: 409 ALREADY_CONVERTED (an import produces one template at most), 409 TEMPLATE_EXISTS, and 422 TEMPLATE_INVALID with the issues.

Then edit the draft in the builder, validate it and publish it. Both the import and the template creation are audited.

ScanReport (version 1)

interface ScanReport {
  version: 1;
  generator: string;                 // "orkstage-scan/0.1.0"
  detectors: { kind: SourceKind; files: number }[];
  resources: DetectedResource[];     // mapped to OrkStage logical types
  unmapped: { source: SourceRef; type: string; reason: string }[];
  connections: { from: string; to: string; protocol: string; port?: number; evidence: string }[];
  template: string;                  // generated platform/v1 EnvironmentTemplate YAML
  warnings: string[];
}
type SourceKind = 'terraform' | 'cloudformation' | 'sam' | 'cdk' | 'serverless' | 'pulumi'
  | 'kubernetes' | 'helm' | 'compose' | 'dockerfile' | 'package' | 'bicep' | 'arm';
interface SourceRef { kind: SourceKind; file: string; line?: number; address: string } // e.g. aws_db_instance.main
interface DetectedResource {
  id: string;                        // template resource id, e.g. "database"
  type: 'network' | 'compute.container-service' | 'container-registry' | 'database.postgresql'
      | 'cache.redis' | 'queue' | 'object-storage' | 'ingress' | 'dns';
  provider: 'aws' | 'azure' | 'gcp' | 'oci' | null;   // null = provider-neutral evidence
  implementation?: string;           // capability id when evident, e.g. "ecs-fargate", "rds-postgres"
  properties: Record<string, unknown>; // mapped template fields (replicas, cpu, memoryMiB, port, …)
  sources: SourceRef[];
  confidence: 'high' | 'medium' | 'low';
}

Using the engine in Node.js

The WebAssembly build is packaged as @orkstage/scan. You build it from the cli/ folder with scripts/build-wasm.sh. It is not published to a registry yet.

const { scanFiles, version } = require('@orkstage/scan');
const report = scanFiles([{ path: 'main.tf', content }], { templateName: 'shop' });

scanFiles(files, options?) is synchronous and does no I/O. The options are templateName, displayName, templateVersion and maxFileBytes.

On this page