Skip to content
crafted signal

Getting Started

CLI Reference

Full reference for csctl, the CraftedSignal CLI. Covers commands for init, validate, push, pull, sync, diff, library management, and CI/CD integration.

Overview

csctl is the CraftedSignal CLI for managing detection rules as code. It connects your local YAML files to the CraftedSignal platform for validation, testing, deployment, and sync.


Installation

Download the latest binary from GitHub Releases :

# macOS / Linux
curl -fsSL https://github.com/CraftedSignal/cli/releases/latest/download/csctl-$(uname -s)-$(uname -m) -o csctl
chmod +x csctl && mv csctl /usr/local/bin/

Verify:

csctl version

Configuration

Config file

csctl looks for .csctl.yaml starting from the current directory and searching upward to the filesystem root.

# .csctl.yaml
url: https://app.craftedsignal.io
defaults:
  path: detections/
  platform: splunk

Initialize a project with a config file and example rule:

csctl init

Environment variables

VariableDescription
CSCTL_URLPlatform URL
CSCTL_TOKENAPI token (recommended over config file)
CSCTL_INSECURESkip TLS verification (true/false)
CSCTL_PATHDefault detections path
CSCTL_PLATFORMDefault platform (splunk, sentinel, crowdstrike, rapid7)

Global flags

These flags work with every command:

FlagDefaultDescription
-configPath to .csctl.yaml
-urlhttps://app.craftedsignal.ioPlatform URL
-insecurefalseSkip TLS certificate verification
-loginfoLog level: debug, info, warn, error
-pathdetections/Path to detections folder

Priority: flags > env vars > config file > defaults.


Commands

init

Create a new detections project with example rule and config file.

csctl init
csctl init -from-platform    # Bootstrap from existing platform rules
FlagDescription
-from-platformPull existing rules from platform instead of creating examples
-tokenAPI token

Creates:

detections/
├── endpoint/
│   └── example-detection.yaml
├── network/
├── cloud/
└── .csctl.yaml

auth

Verify API credentials and display authenticated user info.

csctl auth
csctl auth -token YOUR_TOKEN

Output:

Authenticated to https://app.craftedsignal.io
  Company:  Acme Corp
  API Key:  My API Key
  Scopes:   detection:read, detection:write

validate

Check local YAML files for syntax and schema errors. Runs entirely offline — no API token needed.

csctl validate
csctl validate /path/to/rules

Exit codes: 0 = valid, 1 = errors found.


diff

Show what would change between local files and the platform.

csctl diff
csctl diff -filter "brute*"
csctl diff -group network
FlagDescription
-tokenAPI token
-filterFilter rules by name or ID (supports * wildcard)
-groupFilter by group

Output symbols:

SymbolMeaning
+New rule (local only)
~Modified
!Conflict (both sides changed)

push

Upload local rules to the platform. Tests run automatically by default.

# Push all rules
csctl push -token YOUR_TOKEN

# Push with comment
csctl push -token YOUR_TOKEN -m "Add webshell detection"

# Push and deploy to SIEM
csctl push -token YOUR_TOKEN -deploy

# Preview without applying
csctl push -token YOUR_TOKEN -dry-run

# Push specific rules
csctl push -token YOUR_TOKEN -filter "brute*"
csctl push -token YOUR_TOKEN -group endpoint
FlagDefaultDescription
-tokenAPI token
-mVersion comment
-dry-runfalsePreview changes without applying
-deployfalseDeploy rules to SIEM after push
-testtrueRun tests before pushing
-filterFilter by name or ID (* wildcard)
-groupFilter by group
-atomictrueRoll back all changes if any rule fails
-force-syncfalseContinue even if validation or tests fail
-force-deployfalseDeploy even if tests fail (implies -deploy)

pull

Download rules from the platform to local YAML files.

csctl pull -token YOUR_TOKEN
csctl pull -token YOUR_TOKEN -group endpoint-threats
csctl pull -token YOUR_TOKEN -filter lateral
FlagDescription
-tokenAPI token
-groupPull specific group only
-filterFilter by name or ID (* wildcard)

sync

Bidirectional sync between local files and the platform. Fails on conflicts unless you specify a resolution strategy.

# Sync — fails if conflicts exist
csctl sync -token YOUR_TOKEN

# Keep local changes on conflict
csctl sync -token YOUR_TOKEN -resolve=local

# Keep platform changes on conflict
csctl sync -token YOUR_TOKEN -resolve=remote
FlagDefaultDescription
-tokenAPI token
-resolveConflict resolution: local or remote
-mVersion comment for pushed changes
-filterFilter by name or ID
-groupFilter by group
-atomictrueRoll back if any rule fails
-testtrueRun tests before syncing
-deployfalseDeploy after sync
-force-syncfalseContinue despite validation or test failures
-force-deployfalseDeploy despite failures

Exit codes: 0 = success, 1 = error, 2 = conflicts detected.


library

Manage detection rule library indexes.

Generate index

csctl library index generate -path ./queries -name "My Library"
FlagDefaultDescription
-path.Directory containing rule files
-outputlibrary.index.yamlOutput file
-nameLibraryRepository name
-urlRepository URL
-maintainerMaintainer name

Sign index

csctl library index sign -key signing.key -input library.index.yaml
FlagDefaultDescription
-keyRequired. Path to Ed25519 private key
-inputlibrary.index.yamlIndex file to sign
-outputSame as inputSigned output file
-key-idKey identifier

Verify signature

csctl library index verify -pubkey signing.pub -input library.index.yaml
FlagDefaultDescription
-pubkeyRequired. Path to Ed25519 public key
-inputlibrary.index.yamlIndex file to verify

Generate key pair

csctl library index keygen -output signing
# Creates: signing.key (private) and signing.pub (public)

version

csctl version
# csctl v1.2.3

Detection rule format

Rules are YAML files stored in your detections directory:

title: "Brute Force Login Attempts"
platform: splunk
enabled: true
kind: scheduled
severity: high
frequency: 5m
period: 15m
description: "Detects repeated failed login attempts from a single source."

query: |
  index=main sourcetype=auth action=failure
  | stats count by src_ip
  | where count > 10

tactics:
  - credential-access
techniques:
  - T1110.001

tags:
  - authentication
  - brute-force
groups:
  - endpoint

tests:
  positive:
    - name: "Multiple failed logins"
      data:
        - { src_ip: "10.0.0.1", action: "failure", sourcetype: "auth" }
        - { src_ip: "10.0.0.1", action: "failure", sourcetype: "auth" }
  negative:
    - name: "Successful login"
      data:
        - { src_ip: "10.0.0.1", action: "success", sourcetype: "auth" }
FieldRequiredDescription
titleYesRule name
platformYessplunk, sentinel, crowdstrike, or rapid7
enabledYesWhether the rule is active
idNoUUID assigned on first push — leave empty for new rules
kindNoscheduled (default), realtime, or correlation
queryNoDetection query in platform-native syntax
severityNolow, medium, high, critical
frequencyNoRun interval (e.g., 5m, 1h)
periodNoSearch window (e.g., 15m, 24h)
tacticsNoMITRE ATT&CK tactics
techniquesNoMITRE ATT&CK technique IDs
tagsNoCustom labels
groupsNoLogical groups for filtering
testsNoPositive and negative test cases

Lock file

csctl creates a .csctl.lock.json in your working directory to track sync state. This file maps rule IDs to local file paths, hashes, and platform versions. Commit it to version control.


Exit codes

CodeMeaning
0Success
1Error (validation failure, API error, test failure)
2Conflicts detected (sync only)

CI/CD example

# .github/workflows/detections.yml
name: Deploy detections
on:
  push:
    branches: [main]
    paths: ["detections/**"]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Validate
        run: csctl validate

      - name: Push and deploy
        run: csctl push -deploy -m "CI deploy ${{ github.sha }}"
        env:
          CSCTL_TOKEN: ${{ secrets.CSCTL_TOKEN }}
          CSCTL_URL: ${{ secrets.CSCTL_URL }}