# Limacharlie Iac

> Manage LimaCharlie Infrastructure as Code using ext-git-sync compatible repository structure. Initialize IaC repos, add/remove tenants, manage global and tenant-specific configurations (D&R rules, outputs, FIM, extensions, etc.), and coordinate with ext-git-sync for deployment. Supports importing existing rules from tenants and promoting tenant rules to global. Use when setting up multi-tenant config management, adding orgs to IaC, or managing detection rules across organizations via git.

- Skill: `refractionpoint/limacharlie-iac` (Agent Skill, multi-file: 14 files)
- Install (CLI): `npx skillmds@latest add refractionpoint/limacharlie-iac`
- Raw SKILL.md: https://api.skillmd.com/api/skills/refractionpoint/limacharlie-iac/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: refractionpoint (https://skillmd.com/u/refractionpoint)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/refractionpoint/limacharlie-iac

---


# LimaCharlie Infrastructure as Code Manager

Manage multi-tenant LimaCharlie configurations using git-based Infrastructure as Code, compatible with the `ext-git-sync` extension.

---

## LimaCharlie Integration

> **Prerequisites**: Run `/init-lc` to initialize LimaCharlie context.

### LimaCharlie CLI Access

All LimaCharlie operations use the `limacharlie` CLI directly:

```bash
limacharlie <noun> <verb> --oid <oid> --output yaml [flags]
```

For command help and discovery: `limacharlie <command> --ai-help`

### Critical Rules

| Rule | Wrong | Right |
|------|-------|-------|
| **CLI Access** | Call MCP tools or spawn api-executor | Use `Bash("limacharlie ...")` directly |
| **Output Format** | `--output json` | `--output yaml` (more token-efficient) |
| **Filter Output** | Pipe to jq/yq | Use `--filter JMESPATH` to select fields |
| **D&R Rules** | Write YAML manually | Use `limacharlie ai generate-*` + `limacharlie dr validate` |
| **OID** | Use org name | Use UUID (call `limacharlie org list` if needed) |

---

## Overview

This skill helps you manage LimaCharlie organizations using Infrastructure as Code:

```
┌─────────────────────────────────────────────────────────────────┐
│  IaC REPOSITORY (ext-git-sync compatible)                       │
│                                                                 │
│  org-manifest.yaml        ← Friendly name → OID mapping         │
│                                                                 │
│  hives/                   ← GLOBAL configs (all tenants)        │
│  ├── dr-general.yaml         D&R rules                          │
│  ├── fp.yaml                 False positives                    │
│  ├── outputs.yaml            Output destinations                │
│  ├── extensions.yaml         Extensions to enable               │
│  ├── integrity.yaml          FIM rules                          │
│  └── ...                                                        │
│                                                                 │
│  orgs/                    ← PER-TENANT configs                  │
│  ├── <oid-1>/                                                   │
│  │   ├── index.yaml          Includes global + custom           │
│  │   └── custom/             Tenant-specific overrides          │
│  │       ├── rules.yaml                                         │
│  │       └── fim.yaml                                           │
│  └── <oid-2>/                                                   │
│      └── index.yaml                                             │
│                                                                 │
│  exports/                 ← Auto-generated by ext-git-sync      │
│  └── orgs/...                                                   │
└─────────────────────────────────────────────────────────────────┘
```

## Key Concepts

### ext-git-sync Compatibility

This skill generates repositories compatible with LimaCharlie's `ext-git-sync` extension:
- Folder names under `orgs/` are OIDs (required by ext-git-sync)
- Each org has an `index.yaml` that includes global and custom configs
- Global configs in `hives/` are shared via relative path includes

### Friendly Name Mapping

Since OID folders are hard to read, the skill maintains `org-manifest.yaml`:

```yaml
version: 1
orgs:
  acme-corp:
    oid: 7e41e07b-c44c-43a3-b78d-41f34204789d
    description: "Acme Corporation - Production"
    added: "2025-11-30"

  globex:
    oid: a326700d-3cd7-49d1-ad08-20b396d8549d
    description: "Globex Industries"
    added: "2025-11-30"
```

You refer to orgs by friendly name; the skill translates to OIDs.

---

## Commands

### Initialize Repository

**"Set up a new IaC repo"** or **"Initialize LimaCharlie IaC at /path/to/repo"**

Creates a new ext-git-sync compatible repository:

```
<repo-path>/
├── org-manifest.yaml
├── hives/
│   ├── dr-general.yaml
│   ├── fp.yaml
│   ├── outputs.yaml
│   ├── extensions.yaml
│   ├── integrity.yaml
│   ├── artifact.yaml
│   ├── exfil.yaml
│   ├── resources.yaml
│   └── installation_keys.yaml
├── orgs/
│   └── .gitkeep
├── exports/
│   └── .gitkeep
├── README.md
└── .gitignore
```

**Workflow:**
1. Create directory structure
2. Initialize global config files with `version: 3` headers
3. Create empty `org-manifest.yaml`
4. Initialize git repository
5. Provide instructions for ext-git-sync setup

---

### Add Existing Tenant

**"Add tenant acme-corp"** or **"Add org Acme Corporation to IaC"**

Adds an existing LimaCharlie organization to the repository:

**Workflow:**
1. Look up organization by name using `limacharlie org list --output yaml`
2. Confirm with user if multiple matches
3. Create `orgs/<oid>/index.yaml` with global includes
4. Create `orgs/<oid>/custom/` directory for future customizations
5. Add entry to `org-manifest.yaml`
6. Optionally: Export current config from LC using ext-git-sync or `limacharlie sync pull`
7. Commit changes

**Example index.yaml generated:**
```yaml
version: 3
include:
  # Global configurations
  - ../../hives/extensions.yaml
  - ../../hives/dr-general.yaml
  - ../../hives/fp.yaml
  - ../../hives/outputs.yaml
  - ../../hives/integrity.yaml
  - ../../hives/artifact.yaml
  - ../../hives/exfil.yaml
  - ../../hives/resources.yaml
  - ../../hives/installation_keys.yaml
  # Custom configurations for this org (uncomment as needed)
  # - custom/rules.yaml
  # - custom/fim.yaml
  # - custom/outputs.yaml
```

---

### Create New Organization

**"Create new org called acme-corp"** or **"Create tenant Acme Corporation in US region"**

Creates a new organization in LimaCharlie AND adds it to the repository:

**Workflow:**
1. Use `create_org` to create organization in LC
2. Follow "Add Existing Tenant" workflow
3. Provide installation key information

---

### Remove Tenant from IaC

**"Remove acme-corp from IaC"** (does NOT delete the org in LC)

**Workflow:**
1. Look up OID from `org-manifest.yaml`
2. Remove `orgs/<oid>/` directory
3. Remove entry from `org-manifest.yaml`
4. Commit changes

---

## Rule Management

### Add Global Rule (New)

**"Add detection for encoded PowerShell to all tenants"**
**"Create global rule to detect mimikatz"**

Creates a NEW rule and adds it to global config:

**Workflow:**
1. Use AI generation (`limacharlie ai generate-detection`, `limacharlie ai generate-response`)
2. Validate with `limacharlie dr validate`
3. Append to `hives/dr-general.yaml`
4. Commit with descriptive message

---

### Import Rule from Tenant

**"Import rule encoded-powershell from acme-corp"**
**"Get rule mimikatz-detection from globex into IaC"**

Fetches an EXISTING rule from a LimaCharlie tenant and adds it to the IaC repo:

**Workflow:**
1. Look up tenant OID from `org-manifest.yaml`
2. Fetch rule using the CLI:
   ```bash
   limacharlie dr get --key encoded-powershell --oid <tenant-oid> --output yaml
   ```
3. Ask user: Add as **global** (all tenants) or **tenant-specific**?
4. If global: Add to `hives/dr-general.yaml`
5. If tenant-specific: Add to `orgs/<oid>/custom/rules.yaml`
6. Commit changes

---

### Promote Rule to Global

**"Promote rule encoded-powershell from acme-corp to global"**
**"Make rule X from globex apply to all tenants"**

Takes an existing rule from ONE tenant and makes it apply to ALL tenants:

**Workflow:**
1. Look up source tenant OID from `org-manifest.yaml`
2. Fetch rule using `limacharlie dr get --key <name> --oid <oid> --output yaml`
3. Add rule to `hives/dr-general.yaml`
4. Ask user: Remove from tenant's custom config? (if it was tenant-specific)
5. If yes: Remove from `orgs/<oid>/custom/rules.yaml`
6. Commit: "Promote rule [name] from [tenant] to global"

**Example:**
```
User: "Promote rule lateral-movement-psexec from acme-corp to global"

Skill:
1. Fetches rule from acme-corp (OID: 7e41e07b-...)
2. Adds to hives/dr-general.yaml:

   hives:
     dr-general:
       lateral-movement-psexec:
         data:
           detect: ...
           respond: ...
         usr_mtd:
           enabled: true

3. All tenants now get this rule via their index.yaml includes
```

---

### Copy Rule Between Tenants

**"Copy rule X from acme-corp to globex"**
**"Give globex the same custom-detection rule that acme-corp has"**

Copies a rule from one tenant to another (without making it global):

**Workflow:**
1. Look up source tenant OID
2. Fetch rule from source tenant
3. Look up destination tenant OID
4. Add to `orgs/<dest-oid>/custom/rules.yaml`
5. Update destination's `index.yaml` to include custom rules if needed
6. Commit changes

---

### Add Tenant-Specific Rule

**"Add custom detection only for acme-corp"**
**"Create rule for globex to detect their specific app"**

Creates a NEW rule for ONE tenant only:

**Workflow:**
1. Look up tenant OID from `org-manifest.yaml`
2. Use AI generation for rule
3. Validate rule
4. Create/update `orgs/<oid>/custom/rules.yaml`
5. Update `orgs/<oid>/index.yaml` to include custom rules
6. Commit changes

---

### List Rules

**"Show all global rules"**
**"What rules does acme-corp have?"**
**"List custom rules for globex"**

Displays rules from the IaC repo:

```
Global Rules (hives/dr-general.yaml)
════════════════════════════════════
- encoded-powershell-execution    (enabled)
- mimikatz-command-line           (enabled)
- lateral-movement-psexec         (enabled)

Tenant: acme-corp (7e41e07b-...)
Custom Rules (orgs/.../custom/rules.yaml)
─────────────────────────────────────────
- acme-specific-app-detection     (enabled)

Tenant: globex (a326700d-...)
Custom Rules: (none)
```

---

## Configuration Management

### Add Global Configuration

**"Add global output to send detections to Slack"**
**"Enable Zeek extension for all orgs"**
**"Add FIM rule to watch /etc/passwd globally"**

Adds configuration that applies to ALL tenants:

**Supported global config types:**

| Type | File | Command Example |
|------|------|-----------------|
| D&R Rules | `hives/dr-general.yaml` | "Add detection for X" |
| False Positives | `hives/fp.yaml` | "Add FP rule for Y" |
| Outputs | `hives/outputs.yaml` | "Add Slack output" |
| Extensions | `hives/extensions.yaml` | "Enable Zeek extension" |
| FIM | `hives/integrity.yaml` | "Add FIM for /etc/passwd" |
| Artifact Collection | `hives/artifact.yaml` | "Collect auth.log" |
| Exfil Watch | `hives/exfil.yaml` | "Watch for large uploads" |
| Resources | `hives/resources.yaml` | "Add payload X" |
| Installation Keys | `hives/installation_keys.yaml` | "Add Windows install key" |

---

### Add Tenant-Specific Configuration

**"Add custom FIM for acme-corp to watch /opt/app"**
**"acme-corp needs a custom Slack output"**

Adds configuration specific to ONE tenant:

**Workflow:**
1. Look up OID from `org-manifest.yaml`
2. Determine config type
3. Create/update appropriate file in `orgs/<oid>/custom/`
4. Update `orgs/<oid>/index.yaml` to include it
5. Commit changes

---

### Import All Rules from Tenant

**"Import all rules from acme-corp"**
**"Bootstrap IaC from globex's current config"**

Imports ALL D&R rules from a tenant into the IaC repo:

**Workflow:**
1. Look up tenant OID
2. Fetch all rules using `limacharlie dr list --oid <oid> --output yaml` and `limacharlie dr get --key <name> --oid <oid> --output yaml`
3. Ask user: Add as global or tenant-specific?
4. Add rules to appropriate location
5. Commit: "Import N rules from [tenant]"

---

## Repository Operations

### List Tenants

**"Show tenants in IaC"** or **"List orgs"**

```
IaC Repository Tenants
══════════════════════

Friendly Name    OID                                    Custom Configs
─────────────────────────────────────────────────────────────────────
acme-corp        7e41e07b-c44c-43a3-b78d-41f34204789d   rules, fim
globex           a326700d-3cd7-49d1-ad08-20b396d8549d   (none)
initech          cb639126-e0bc-4563-a577-2e559c0610b2   outputs

Total: 3 tenants
```

---

### Show Repository Structure

**"Show IaC structure"** or **"What's in the repo?"**

Displays the current repository layout with file summaries.

---

### Validate Repository

**"Validate IaC repo"** or **"Check for errors"**

Validates the repository structure and configurations:

**Checks:**
- All `index.yaml` files have valid includes
- Include paths resolve to existing files
- YAML syntax is valid
- D&R rules pass validation
- No orphaned org folders (not in manifest)
- No missing org folders (in manifest but no folder)

---

### Sync from LimaCharlie

**"Sync acme-corp from LC"** or **"Pull current config for globex"**

Exports current configuration from LimaCharlie into the repository:

**Workflow:**
1. Look up OID from manifest
2. Use ext-git-sync export OR `limacharlie sync pull`
3. Place exported config in `exports/orgs/<oid>/`
4. Optionally: Merge into main org folder
5. Show diff if merging

---

### Deploy (Local)

**"Deploy acme-corp"** or **"Push configs to LC"**

Deploys configuration to LimaCharlie using the CLI:

```bash
limacharlie sync push \
  --config-file ./orgs/<oid>/index.yaml \
  --oid <oid> \
  --force \
  --hive-dr-general \
  --hive-fp \
  --outputs \
  --integrity \
  --artifact \
  --exfil \
  --resources \
  --extensions \
  --installation-keys
```

**Note:** For production, recommend using ext-git-sync's recurring sync.

---

## D&R Rule Generation

**CRITICAL: Never write D&R YAML manually. Always use AI generation.**

When creating NEW detection rules:

```bash
# 1. Generate detection component from natural language
limacharlie ai generate-detection --description "..." --oid <oid> --output yaml

# 2. Generate response component from natural language
limacharlie ai generate-response --description "..." --oid <oid> --output yaml

# 3. Write to temp files and validate before adding to repo
cat > /tmp/detect.yaml << 'EOF'
<detection_yaml>
EOF
cat > /tmp/respond.yaml << 'EOF'
<response_yaml>
EOF
limacharlie dr validate --detect /tmp/detect.yaml --respond /tmp/respond.yaml --oid <oid>
```

When IMPORTING existing rules from LC, fetch them via API - no generation needed.

---

## ext-git-sync Setup Guide

After initializing the repo and adding tenants, each org needs ext-git-sync configured:

### Per-Organization Setup

1. **Subscribe to ext-git-sync extension** in each org:
   ```bash
   limacharlie extension subscribe --name ext-git-sync --oid <oid> --output yaml
   ```

2. **Create SSH deploy key:**
   ```bash
   ssh-keygen -t ed25519 -C "lc-gitsync-<org-name>" -f ~/.ssh/lc-gitsync
   ```

3. **Add public key to GitHub** (Settings → Deploy keys → Allow write access)

4. **Store private key in LC Secret Manager** for each org:
   ```bash
   limacharlie secret set --key git-sync-ssh-key --input-file /tmp/ssh-key.txt --oid <oid>
   ```

5. **Configure ext-git-sync** using the exact config schema below

### ext-git-sync Config Schema

**CRITICAL: Use these exact field names when configuring ext-git-sync:**

```yaml
# Required fields
repo_url: "git@github.com:your-org/your-repo.git"    # NOT "repository"
branch: "main"
conf_root: "orgs/<oid>/index.yaml"                   # Path to org's config entry point

# SSH authentication (recommended)
ssh_key_source: "secret"                              # Use LC Secret Manager
ssh_key_secret_name: "git-sync-ssh-key"              # Name of secret containing private key

# Alternative: inline SSH key (not recommended for production)
# ssh_key_source: "inline"
# ssh_key: "<private_key_content>"
```

**CLI Example:**
```bash
cat > /tmp/ext-git-sync-config.yaml << 'EOF'
repo_url: "git@github.com:your-org/your-repo.git"
branch: "main"
conf_root: "orgs/<oid>/index.yaml"
ssh_key_source: "secret"
ssh_key_secret_name: "git-sync-ssh-key"
EOF
limacharlie extension config-set --name ext-git-sync --input-file /tmp/ext-git-sync-config.yaml --oid <org-oid>
```

### Shared SSH Key Option

For MSSP scenarios, you can use ONE deploy key across all orgs:
1. Create single SSH key
2. Add to GitHub repo
3. Store same private key in each org's Secret Manager (same secret name)
4. Configure ext-git-sync in each org pointing to same repo

### Verify ext-git-sync Setup

After configuration, verify the setup is working:

1. **Check extension subscription:**
   ```bash
   limacharlie extension list --oid <oid> --output yaml
   # Should show ext-git-sync in the list
   ```

2. **Verify secret exists:**
   ```bash
   limacharlie secret list --oid <oid> --output yaml
   # Should include "git-sync-ssh-key"
   ```

3. **Check extension config:**
   ```bash
   limacharlie extension config-get --name ext-git-sync --oid <oid> --output yaml
   # Verify repo_url, branch, conf_root are correct
   ```

4. **Check for org errors:**
   ```bash
   limacharlie org errors --oid <oid> --output yaml
   # Look for ext-git-sync errors (SSH auth failures, repo access issues)
   ```

5. **Trigger manual sync (optional):**
   - In LC UI: Extensions → ext-git-sync → "Sync Now"
   - Check org errors afterward for any issues

---

## Repository Layout Reference

```
my-lc-iac/
├── org-manifest.yaml              # Friendly name → OID mapping
│
├── hives/                         # Global configurations
│   ├── dr-general.yaml            # Detection rules
│   ├── fp.yaml                    # False positive rules
│   ├── outputs.yaml               # Output destinations
│   ├── extensions.yaml            # Extensions to enable
│   ├── integrity.yaml             # FIM rules
│   ├── artifact.yaml              # Artifact collection
│   ├── exfil.yaml                 # Exfil monitoring
│   ├── resources.yaml             # Resources/payloads
│   └── installation_keys.yaml     # Sensor install keys
│
├── orgs/                          # Per-tenant configurations
│   ├── 7e41e07b-...-789d/         # acme-corp (OID)
│   │   ├── index.yaml             # Includes global + custom
│   │   └── custom/                # Tenant-specific
│   │       ├── rules.yaml
│   │       └── fim.yaml
│   │
│   └── a326700d-...-549d/         # globex (OID)
│       └── index.yaml
│
├── exports/                       # ext-git-sync exports land here
│   └── orgs/
│       └── ...
│
├── README.md
└── .gitignore
```

---

## YAML Structure Reference

### hives/dr-general.yaml
```yaml
version: 3
hives:
  dr-general:
    rule-name-here:
      data:
        detect:
          event: NEW_PROCESS
          op: contains
          path: event/COMMAND_LINE
          value: "-enc"
        respond:
          - action: report
            name: encoded-powershell
      usr_mtd:
        enabled: true
        expiry: 0
        tags: []
```

### hives/extensions.yaml
```yaml
version: 3
extensions:
  - ext-infrastructure
  - ext-velociraptor
  - ext-reliable-tasking
```

### hives/outputs.yaml
```yaml
version: 3
outputs:
  slack-alerts:
    for: detect
    module: slack
    slack_api_token: hive://secret/slack-token
    slack_channel: "#security-alerts"
```

### hives/integrity.yaml (FIM)
```yaml
version: 3
integrity:
  ssh-keys:
    patterns:
      - /root/.ssh/authorized_keys
      - /home/*/.ssh/authorized_keys
    platforms:
      - linux
    tags: []
```

### hives/installation_keys.yaml
```yaml
version: 3
installation_keys:
  windows:
    desc: "Windows endpoints"
    tags:
      - windows
  linux:
    desc: "Linux servers"
    tags:
      - linux
```

### org-manifest.yaml
```yaml
version: 1
orgs:
  acme-corp:
    oid: 7e41e07b-c44c-43a3-b78d-41f34204789d
    description: "Acme Corporation - Production"
    added: "2025-11-30"

  globex:
    oid: a326700d-3cd7-49d1-ad08-20b396d8549d
    description: "Globex Industries"
    added: "2025-11-30"
```

---

## Best Practices

### 1. Git Workflow
- Use branches for changes
- Review via PR before merging to main
- Let ext-git-sync handle deployment from main branch

### 2. Rule Naming
Use consistent naming: `[category]-[description]`
- `encoded-powershell-execution`
- `mimikatz-command-line`
- `lateral-movement-psexec`

### 3. Tenant Customization
- Keep global configs as defaults
- Only add tenant-specific configs when truly needed
- Document why custom configs exist

### 4. Secrets
- Never commit plaintext secrets
- Use LC Secret Manager: `hive://secret/secret-name`
- Reference secrets in configs, don't embed values

### 5. Testing Changes
- Use `limacharlie sync push --dry-run` before deploying
- Test on one org before rolling out globally
- Use `detection-engineering` skill to test rules

### 6. Importing vs Creating
- **Import** when rule already exists and works well in LC
- **Create** when building new detection logic
- **Promote** when a tenant-specific rule should be global

---

## Troubleshooting

### General Issues

| Issue | Solution |
|-------|----------|
| Include path not found | Check relative path from index.yaml location |
| YAML syntax error | Validate with `python -c "import yaml; yaml.safe_load(open('file.yaml'))"` |
| Org not in manifest | Run "add tenant" command |
| Rule not appearing | Check `enabled: true` in usr_mtd |
| Rule exists in LC but not IaC | Use "import rule" command |

### ext-git-sync Specific Issues

| Issue | Cause | Solution |
|-------|-------|----------|
| "repo_url is required" | Wrong field name | Use `repo_url`, not `repository` |
| "ssh_key is required" | Secret doesn't exist or wrong name | Verify secret exists with `limacharlie secret list --oid <oid> --output yaml` |
| "conf_root not found" | Wrong path in config | Use full path: `orgs/<oid>/index.yaml` |
| SSH auth failure | Deploy key not added or wrong key | Verify public key is in GitHub deploy keys |
| "Host key verification failed" | First connection to GitHub | Add GitHub to known_hosts or use `ssh -o StrictHostKeyChecking=no` |
| Sync runs but no changes | Branch mismatch | Verify `branch` field matches your repo's default branch |
| Extension not in list | Not subscribed | Run `limacharlie extension subscribe --name ext-git-sync --oid <oid>` |

### Debugging ext-git-sync

1. **Check org errors first:**
   ```bash
   limacharlie org errors --oid <oid> --output yaml
   ```
   This shows recent errors from ext-git-sync including SSH failures and config issues.

2. **Verify the complete config:**
   ```bash
   limacharlie extension config-get --name ext-git-sync --oid <oid> --output yaml
   ```
   Ensure all required fields are present: `repo_url`, `branch`, `conf_root`, `ssh_key_source`, `ssh_key_secret_name`

3. **Test SSH key locally:**
   ```bash
   ssh -i ~/.ssh/your-key -T git@github.com
   ```
   Should return: "Hi username! You've successfully authenticated..."

4. **Verify GitHub deploy key permissions:**
   - Must have "Allow write access" checked if using bidirectional sync
   - Key must be added to the specific repository, not account-level

---

## Command Quick Reference

| Command | Example |
|---------|---------|
| Initialize repo | "Set up IaC repo at ~/lc-config" |
| Add tenant | "Add tenant acme-corp" |
| Create tenant | "Create new org called acme-corp" |
| Add global rule | "Add detection for encoded PowerShell" |
| Import rule | "Import rule X from acme-corp" |
| Promote rule | "Promote rule X from acme-corp to global" |
| Copy rule | "Copy rule X from acme-corp to globex" |
| Add tenant rule | "Add custom rule for acme-corp only" |
| List rules | "Show global rules" |
| List tenants | "Show tenants in IaC" |
| Validate | "Validate IaC repo" |
| Deploy | "Deploy acme-corp" |
| Sync | "Sync acme-corp from LC" |

---

## Related Skills

| Skill | Use Case |
|-------|----------|
| `detection-engineering` | Test and refine D&R rules before adding to IaC |
| `lookup-lc-doc` | Reference D&R syntax and operators |
| `reporting` | Generate reports across managed orgs |

