Home
Softono
carina

carina

Open source Apache-2.0 Rust
52
Stars
2
Forks
332
Issues
1
Watchers
1 week
Last Commit

About carina

A strongly typed infrastructure management tool

Platforms

Web Self-hosted iOS Android

Languages

Java Rust

Carina

Carina

Carina represents the keel of Argo Navis: the structural backbone that quietly supports everything above it.

[!CAUTION] This is an experimental project. The DSL syntax, APIs, and features are subject to change without notice.

A strongly typed infrastructure management tool written in Rust.

Features

  • Custom DSL: Simple, expressive syntax for defining infrastructure
  • Effects as Values: Side effects are represented as data structures, not immediately executed
  • Strong Typing: Catch configuration errors at parse time with schema validation
  • Data Sources: Reference existing infrastructure without managing its lifecycle
  • Provider Architecture: Extensible provider system for multi-cloud support
  • Modules: Reusable infrastructure components with typed inputs/outputs
  • State Management: Remote state storage with locking (S3 backend)
  • LSP Support: Editor integration with completion, diagnostics, and syntax highlighting
  • Terraform-like Workflow: Familiar validate, plan, apply, destroy commands

Installation

cargo build --release

The binary will be available at target/release/carina.

Quick Start

1. Define your infrastructure

Create a .crn file:

# main.crn

provider aws {
  region = aws.Region.ap_northeast_1
}

let main_vpc = aws.vpc {
  name       = 'main-vpc'
  cidr_block = '10.0.0.0/16'
}

let web_sg = aws.security_group {
  name   = 'web-sg'
  vpc_id = main_vpc.id
}

aws.security_group.ingress_rule {
  name              = 'http'
  security_group_id = web_sg.id
  from_port         = 80
  to_port           = 80
  protocol          = 'tcp'
  cidr_blocks       = ['0.0.0.0/0']
}

2. Validate

Point carina at the directory containing your .crn files (all .crn files in that directory are merged):

$ carina validate .
Validating...
✓ 3 resources validated successfully.
  • vpc.main-vpc
  • security_group.web-sg
  • security_group.ingress_rule.http

3. Plan

$ carina plan .
Execution Plan:

  + vpc
      name: "main-vpc"
      cidr_block: "10.0.0.0/16"
        └─ + security_group
              name: "web-sg"
              vpc_id: main_vpc.id
              └─ + security_group.ingress_rule
                    name: "http"
                    security_group_id: web_sg.id

Plan: 3 to add, 0 to change, 0 to destroy.

4. Apply

$ carina apply .
Applying changes...

  ✓ Create vpc.main-vpc
  ✓ Create security_group.web-sg
  ✓ Create security_group.ingress_rule.http

Apply complete! 3 changes applied.

DSL Syntax

Provider Block

provider aws {
  region = aws.Region.ap_northeast_1
}

Resources

Anonymous resources - ID is derived from the name attribute:

aws.security_group.ingress_rule {
  name              = 'http'
  security_group_id = web_sg.id
  from_port         = 80
  to_port           = 80
  protocol          = 'tcp'
}

Named resources - Use let binding for referencing:

let web_sg = aws.security_group {
  name   = 'web-sg'
  vpc_id = main_vpc.id
}

Data Sources

Use the read keyword to reference existing infrastructure without managing its lifecycle. Data sources are read-only and cannot be created, modified, or deleted by Carina.

# Read an existing VPC (data source)
let existing_vpc = read aws.vpc {
  name = 'production-vpc'
}

# Create a new subnet that references the existing VPC
let app_subnet = aws.subnet {
  name              = 'app-subnet'
  vpc_id            = existing_vpc.id
  cidr_block        = '10.0.100.0/24'
  availability_zone = aws.AvailabilityZone.ZoneName.ap_northeast_1a
}

In plan output, read effects are displayed with the <= symbol to distinguish them from mutations.

Enum Values

Enum values support multiple formats. The shorthand forms are automatically resolved based on schema context:

# Full namespace format
instance_tenancy = aws.ec2.Vpc.InstanceTenancy.dedicated

# Type.value format
instance_tenancy = InstanceTenancy.dedicated

# Value-only format (shortest, recommended)
instance_tenancy = dedicated

Nested Objects (Struct Types)

Some resources support nested objects for inline configuration. Use repeated blocks for multiple items:

awscc.ec2.SecurityGroup {
  name              = 'web-sg'
  vpc_id            = vpc.vpc_id
  group_description = 'Web server security group'

  security_group_ingress {
    ip_protocol = 'tcp'
    from_port   = 80
    to_port     = 80
    cidr_ip     = '0.0.0.0/0'
  }

  security_group_ingress {
    ip_protocol = 'tcp'
    from_port   = 443
    to_port     = 443
    cidr_ip     = '0.0.0.0/0'
  }
}

Array syntax is also supported:

  security_group_ingress = [
    {
      ip_protocol = 'tcp'
      from_port   = 80
      to_port     = 80
      cidr_ip     = '0.0.0.0/0'
    }
  ]

Modules

Modules enable reusable infrastructure components with typed inputs and outputs.

Module definition (modules/web_tier/main.crn):

input {
  vpc: aws.vpc
  cidr_blocks: list(cidr)
  enable_https: bool = true
}

output {
  security_group: aws.security_group = web_sg.id
}

let web_sg = aws.security_group {
  name        = 'web-sg'
  vpc_id      = input.vpc
  description = 'Security group for web servers'
}

Using modules:

let web_tier = use { source = './modules/web_tier' }

let main_vpc = aws.vpc {
  name       = 'main-vpc'
  cidr_block = '10.0.0.0/16'
}

web_tier {
  vpc         = main_vpc.id
  cidr_blocks = ['10.0.1.0/24', '10.0.2.0/25']
}

Inspect module structure:

$ carina module info modules/web_tier
Module: web_tier

=== REQUIRES ===

  vpc: aws.vpc  (required)
  cidr_blocks: list(cidr)  (required)
  enable_https: bool = true

=== CREATES ===

  input { vpc: aws.vpc }
    └── web_sg: aws.security_group
       ├── http: aws.security_group.ingress_rule
       └── https: aws.security_group.ingress_rule

=== ATTRIBUTES ===

  security_group: aws.security_group

Architecture

Carina follows a functional architecture where side effects are treated as values:

DSL File (.crn)
     │
     ▼
┌─────────┐
│ Parser  │  Parse DSL into Resources
└────┬────┘
     │
     ▼
┌─────────┐
│ Differ  │  Compare desired vs current state
└────┬────┘
     │
     ▼
┌─────────┐
│  Plan   │  Collection of Effects (Create/Update/Delete)
└────┬────┘
     │
     ▼
┌──────────┐
│ Provider │  Execute Effects (AWS, GCP, etc.)
└──────────┘

Core Concepts

  • Resource: Desired state declared in DSL
  • State: Current state fetched from infrastructure
  • Effect: Represents a side effect (Create, Update, Delete, Read)
  • Plan: Collection of Effects to be executed
  • Provider: Abstraction for infrastructure operations

Project Structure

carina/
├── carina-cli/              # CLI application
├── carina-core/             # Core library (provider-agnostic)
│   ├── src/
│   │   ├── effect.rs        # Effect type definitions
│   │   ├── plan.rs          # Plan (collection of Effects)
│   │   ├── resource.rs      # Resource and State types
│   │   ├── provider.rs      # Provider trait
│   │   ├── differ/          # State comparison
│   │   ├── parser/          # DSL parser (pest-based)
│   │   ├── schema.rs        # Type validation (generic types only)
│   │   ├── module.rs        # Module signature and dependency graph
│   │   ├── module_resolver/ # Module import and expansion
│   │   └── formatter/       # Code formatter
│   └── ...
├── carina-plugin-host/      # WASM plugin host for provider plugins
├── carina-plugin-sdk/       # SDK for building WASM provider plugins
├── carina-provider-mock/    # Mock provider for testing
├── carina-provider-protocol/ # Protocol definitions for provider communication
├── carina-provider-resolver/ # Resolves and loads provider plugins
├── carina-state/            # State management
│   └── src/backends/        # State backends (S3, etc.)
├── carina-lsp/              # Language Server Protocol implementation
└── carina-tui/              # Terminal UI for plan display

AWS Provider

AWS providers are distributed as separate repositories under carina-rs and loaded as WASM plugins by the core runtime:

Configure valid AWS credentials via:

  • Environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)
  • AWS credentials file (~/.aws/credentials)
  • IAM roles (when running on AWS)

Using with aws-vault

aws-vault exec myprofile -- carina apply .

Commands

Format

Format .crn files:

# Format a single file
$ carina fmt example.crn

# Format all .crn files in current directory
$ carina fmt

# Format recursively
$ carina fmt -r

# Check formatting without modifying files
$ carina fmt --check

# Show diff of formatting changes
$ carina fmt --diff

Destroy

Remove all resources defined in a configuration:

$ carina destroy .
Destroy Plan:

  - security_group.ingress_rule.http
  - security_group.web-sg
  - vpc.main-vpc

Plan: 3 to destroy.

Do you really want to destroy all resources?
  This action cannot be undone. Type 'yes' to confirm.

  Enter a value: yes

Destroying resources...

  ✓ Delete security_group.ingress_rule.http
  ✓ Delete security_group.web-sg
  ✓ Delete vpc.main-vpc

Destroy complete! 3 resources destroyed.

Use --auto-approve to skip the confirmation prompt.

Module Info

Inspect module structure and dependencies:

$ carina module info modules/web_tier

State Management

Carina supports remote state storage for tracking infrastructure state across team members and CI/CD pipelines.

S3 Backend

Store state in an S3 bucket:

backend s3 {
  bucket      = 'my-carina-state'
  key         = 'infra/prod/carina.crnstate'
  region      = aws.Region.ap_northeast_1
  encrypt     = true
  auto_create = true  # Automatically create the bucket if it doesn't exist
}

provider aws {
  region = aws.Region.ap_northeast_1
}

aws.s3.Bucket {
  name = 'my-app-data'
}

The state file tracks:

  • Resource states and attributes
  • Serial number for change detection
  • Locking to prevent concurrent modifications

Development

Run tests

cargo test

Build

cargo build

License

MIT

Roadmap

  • [x] Resource dependencies and references
  • [x] Modules and reusability
  • [x] Destroy command
  • [x] State file management (S3 backend)
  • [x] Data sources (read existing infrastructure)
  • [ ] More AWS resources (EC2, IAM, Lambda, etc.)
  • [ ] GCP provider
  • [ ] Import existing resources