Home
Softono

Carina

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

 About Carina

A strongly typed infrastructure management tool

Platforms

Web Self-hosted iOS Android

Languages

Java Rust

Need Help Installing Carina?

We provide expert installation service for this software. Our team will install, configure, and secure Carina on your server. plans start at just $30.

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

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