# Monitors, dashboards and detections as code

Publish detection rules, value lists and YARA rulesets from Git with validate, plan and apply, and describe monitors and dashboards in tracix.io/v1 bundles.

Tracix takes two kinds of content from Git. Detection rules, value lists and YARA rulesets live as plain files in a detection repository. Monitors, visualizations, dashboards and APM service groups are described in `tracix.io/v1` JSON bundles. Both are published the same way: validate, then plan, then apply the plan you reviewed.

> **What you can automate today**
>
> Detection content runs from any CI system with a scoped [API key](https://tracix.io/docs/api/authentication), or your workspace syncs the repository itself. Monitor and dashboard bundles can't be published with an API key yet: publishing one needs a workspace admin, so bundles have no unattended CI path today.

## How content managed as code behaves

* **Git owns the definition.** Each published object gets a stable ID, `code:<package>:<kind>:<uid>`, and is marked as managed as code.
* **The workspace won't edit it.** Editing or deleting a managed definition in the workspace is refused, for admins too. To change one for yourself, duplicate it: the copy is an ordinary object you own.
* **Operational controls stay yours.** You can still enable, disable and snooze a managed monitor and change where it notifies, and enable or disable a managed detection rule or YARA ruleset. These settings survive later publishes.
* **A package owns its objects.** Everything a repository or bundle publishes belongs to the package it names.

Integration packages from the catalog install their monitors and dashboards the same way, so those are managed as code too.

## Validate, plan and apply

| Step     | What it does                                                                                                                                         | Changes your workspace |
| -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- |
| Validate | Compiles every definition with your workspace's engine and runs the tests that come with them                                                        | No                     |
| Plan     | Validates, then compares with what the workspace runs: each object is a create, update, delete, unchanged or conflict, and the plan gets a plan hash | No                     |
| Apply    | Plans again and must reproduce the plan hash you reviewed, then publishes                                                                            | Yes                    |

* **Apply refuses a stale plan.** If anything changed since the review, apply answers `409` and you plan again.
* **Plans survive a merge.** The plan hash covers each object's action, definition and live version, not the commit, so a plan reviewed on a pull request still applies at the merge commit when the content is the same.
* **Apply isn't a transaction.** Each write is safe to repeat, but a failure part way leaves the earlier writes in place. Plan again and apply again.
* **Roll back from Git.** Revert the commit and apply it. For detection content, only the rules whose definition changes are written.

## Detection repositories

A detection repository holds plain files, and its Sigma rules stay readable by sigma-cli and pySigma.

```text title="Repository layout"
tracix-detections.yaml    the manifest: package, defaults, overrides
sigma/**/*.yml|yaml       Sigma rules, verbatim (up to 50 documents per file)
native/**/*.yml|yaml      native rules: sql, sequence, new_terms, cardinality, watchlist
lists/**/*.yml|yaml       value lists: name, description, items
yara/**/*.yar|yara        YARA rulesets, one per file
tests/**/*.yml|yaml       fixtures: events each rule must or must not match
```

Other files, such as a README or CI configuration, are ignored. A repository can keep its detections in a subfolder. It can hold up to 5,000 detection files and 50 MB of content, and publish up to 5,000 objects.

### The manifest

`tracix-detections.yaml` at the repository root names the package and tunes the rules. Unknown keys and values that would need converting fail validation, so a typo never becomes a silent default.

```yaml title="tracix-detections.yaml"
package: acme-soc

defaults:
  intervalSeconds: 60   # how often each rule's monitor runs
  windowMinutes: 5      # how far back each run looks
  rbacFloor: editor     # who sees the published rules: viewer | editor | admin
  license: custom       # for Sigma documents that declare no license

overrides:
  # One rule, by its Sigma id: tuning lives here, not in the Sigma file.
  "0dc16a63-818e-4162-95ea-d8f9e5740ef7":
    severity: high
    exceptions:
      - User: list:service-accounts
    suppress:
      byFields: [host.name]
      windowMinutes: 60

  # A glob over repository paths: new network rules start in shadow mode.
  "sigma/network/**":
    stage: test

prune: true
```

| Key          | What it sets                                                                                                                                                                                   |
| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `package`    | Required. A lowercase name of letters, digits, `_`, `.` and `-`, up to 128 characters                                                                                                          |
| `defaults`   | `stage`, `enabled`, `intervalSeconds` (15 to 86,400), `windowMinutes` (1 to 10,080), `rbacFloor` and `license` for every rule                                                                  |
| `overrides`  | Tuning per rule ID, uid, path or glob: stage, severity, risk score, threshold, exceptions, suppression, schedule and investigation guide. Glob keys apply first, in order, and exact keys last |
| `prune`      | `true` by default: a file removed from the repository plans a delete. With `false`, removing a file leaves the published object alone                                                          |
| `logsources` | Routes for Sigma log sources the built-in table doesn't know. In this release, validate checks the block and warns that it isn't applied yet                                                   |

### Stages

Every rule runs at a stage. A rule's stage comes from the manifest; without one, a Sigma rule's `status` decides: `experimental` and `test` start in `test`, `stable` in `production`, and `deprecated` or `unsupported` documents are errors.

| Stage        | Behavior                                                                                                       |
| ------------ | -------------------------------------------------------------------------------------------------------------- |
| `draft`      | Off, with no monitor                                                                                           |
| `test`       | Shadow mode: the rule's monitor runs and records alerts, but nobody is notified or paged and no case is opened |
| `production` | Alerts and notifies as usual                                                                                   |

Promote a rule with a pull request that changes its stage. A rule that doesn't compile is published disabled, with its reasons, never dropped.

### Fixtures

Files under `tests/` hold events a rule must or must not match. Validate and plan run every case against the compiled rule on one synthetic row: nothing is written and nothing alerts.

```yaml title="tests/proc_creation_whoami.yaml"
rule: 0dc16a63-818e-4162-95ea-d8f9e5740ef7
cases:
  - name: whoami run by a person
    event:
      Image: 'C:\Windows\System32\whoami.exe'
      User: alice
    expect: match

  - name: service account excluded by the exception
    event:
      Image: 'C:\Windows\System32\whoami.exe'
      User: svc_backup
    expect: no_match
    withExceptions: true
```

* Sigma and SQL rules run fixtures. Sequence, new terms, cardinality and watchlist rules need several events or history, so their cases report `unsupported` and the rules are checked by compiling them.
* A file that names a `ruleset` holds YARA fixtures: text or base64 samples and the exact set of rules that must match.
* A file holds up to 100 cases. A failing case makes validate and plan report `valid: false`, so the CLI exits with `1` before apply.

### Conflicts

A repository rule whose rule ID matches a rule created in the UI or installed from the Sigma pack plans a **conflict**. Apply refuses the whole plan until you confirm the replacement with `--replace`. The older rule is then disabled and linked to the new one, never taken over in place. Value lists can't be replaced: a list name that another list already uses blocks the plan.

In this release, a repository refuses Sigma correlation and filter documents. Write a correlation as a native `sequence` or `cardinality` rule, and a filter as manifest `exceptions`.

### Run the CLI

`tracix-detections` is a single Node.js 20 or later file with no dependencies. Your workspace serves the build that matches its API.

**1. Point it at your workspace**

Set your workspace's address and a CI key with `security.read`:

```bash
export TRACIX_URL=https://production-a7k3p2q9.app.tracix.io
export TRACIX_API_KEY=txk_...
```

**2. Download the CLI**

```bash
curl -fsS -H "X-API-Key: $TRACIX_API_KEY" \
  "$TRACIX_URL/api/security/detections/cli" -o tracix-detections.mjs
```

The response's `X-Tracix-Cli-Sha256` header carries the file's SHA-256, so CI can pin a reviewed build.

**3. Validate and plan**

```bash
node tracix-detections.mjs validate .
node tracix-detections.mjs plan . plan.json
```

The plan lists what apply would create, update, delete or replace, and is saved for review.

**4. Apply the reviewed plan**

With a key that carries `security.manage`:

```bash
node tracix-detections.mjs apply . plan.json
```

More than 200 writes run in the background, and the CLI follows the job to the end.

### CLI reference

| Command                     | What it does                                                          |
| --------------------------- | --------------------------------------------------------------------- |
| `validate <dir>`            | Compiles every file and runs the fixtures. Read-only                  |
| `plan <dir> <plan.json>`    | Validates, compares with the workspace and writes the plan for review |
| `apply <dir> <plan.json>`   | Applies a reviewed plan. The workspace plans again and must match     |
| `export <package> <outdir>` | Writes a package's published rules and lists as repository files      |

| Option                | What it does                                                                                                    |
| --------------------- | --------------------------------------------------------------------------------------------------------------- |
| `--replace`           | `plan`, `apply`: confirm replacing rules that share a rule ID with this repository                              |
| `--archive`           | Have the workspace fetch the commit from GitHub or GitLab instead of sending the files                          |
| `--revision <sha>`    | The commit the files were taken at. Defaults to the Git `HEAD` of the folder, with `-dirty` when it has changes |
| `--repo <owner/name>` | Archive mode: the repository                                                                                    |
| `--package <name>`    | Refuse to run unless the manifest publishes this package                                                        |
| `--no-wait`           | `apply`: return once a background apply has started                                                             |
| `--force`             | `export`: write into a folder that isn't empty                                                                  |
| `--json`              | Print the workspace's JSON instead of tables                                                                    |
| `--verbose`           | Also list unchanged objects and passing fixtures                                                                |

| Environment variable       | What it sets                                                                                           |
| -------------------------- | ------------------------------------------------------------------------------------------------------ |
| `TRACIX_URL`               | Your workspace's address. HTTPS, except for `localhost`                                                |
| `TRACIX_API_KEY`           | A CI key: `security.read` for validate, plan and export, `security.manage` for apply                   |
| `TRACIX_TOKEN`             | A bearer token, used instead of `TRACIX_API_KEY`. Set one or the other                                 |
| `TRACIX_REPO_CREDENTIAL`   | Archive mode: the ID of a GitHub or GitLab credential stored in your workspace                         |
| `TRACIX_REPO`              | Archive mode: `owner/name`, when `--repo` isn't given                                                  |
| `TRACIX_REVISION`          | The revision to publish, when `--revision` isn't given                                                 |
| `TRACIX_MAX_REQUEST_BYTES` | Request size before the CLI batches or switches to archive mode. Default 1,000,000, at most 10,485,760 |
| `TRACIX_TIMEOUT_MS`        | Timeout per request. Default 300,000                                                                   |
| `TRACIX_POLL_MS`           | How often `apply` checks a background job. Default 2,000                                               |
| `TRACIX_APPLY_WAIT_MS`     | How long `apply` follows a background job. Default 1,800,000                                           |

The CLI exits with `0` on success, `1` for compile errors, failing fixtures, unconfirmed conflicts, a blocked plan or a failed request, and `2` for usage or configuration errors. It sends the key only in a header to `TRACIX_URL`, refuses redirects and masks the key in everything it prints. A plan is bound to the workspace it was made against, and `apply` refuses a plan made for another `TRACIX_URL`.

### Sync a repository from your workspace

Your workspace can follow a repository without any CI. An admin connects it under **Security › Detection rules › Repositories** with **Connect repository**, or with **Keep in sync** when importing rules from a Git repository, using a GitHub or GitLab credential stored in the workspace.

| Setting                         | Options                                                                                                                                                                                                                                                                           |
| ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Repository, branch and folder   | Where the detections live                                                                                                                                                                                                                                                         |
| Mode                            | **Interval** checks the branch every 15 minutes by default (5 to 1,440). **Webhook** plans on every push, checked against a webhook signing secret, and still checks hourly in case a delivery was lost. **Manual** plans only when you select **Sync now** or **Sync to commit** |
| Apply clean plans automatically | Off by default. When on, a plan applies by itself only if it has no compile errors, failing fixtures or conflicts                                                                                                                                                                 |

Every other plan waits for an admin to review it, with a diff per rule, and apply or discard it. To roll back, **Sync to commit** pins the repository to an earlier commit and plans it.

### Export rules you wrote in the UI

**Security › Detection rules › Export**, or **Export to git** on one rule, turns rules authored in the UI into repository files: a download, or one pull request on a connected GitHub repository (GitLab repositories export as files). After the merge, the next plan shows each exported rule as a conflict that replaces its UI rule; confirm it to hand the rule to Git.

To get a published package back as files, run `node tracix-detections.mjs export <package> <outdir>`. Fixtures aren't stored in the workspace, so they aren't exported.

## Monitor and dashboard bundles

A bundle is one JSON file that describes monitors, visualizations, dashboards and APM service groups.

```json title="operations.json"
{
  "apiVersion": "tracix.io/v1",
  "package": "team-operations",
  "revision": "1.0.0",
  "resources": [
    {
      "kind": "monitor",
      "uid": "service-error-rate",
      "attributes": {
        "name": "Service error rate",
        "description": "Sustained server error ratio above 5 percent.",
        "templateId": "apm.error_rate",
        "enabled": false,
        "condition": "above",
        "thresholds": { "alert": 5, "warn": 2 },
        "windowMinutes": 15,
        "pendingForMinutes": 5,
        "noData": "last_known",
        "sharing": { "visibility": "team", "rbacFloor": "viewer" }
      },
      "tests": [
        { "name": "healthy", "value": 1, "expected": "ok" },
        { "name": "warning", "value": 3, "expected": "warn" },
        { "name": "alert", "value": 6, "expected": "alert" }
      ]
    }
  ]
}
```

| Field                    | Rules                                                                                                                  |
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------- |
| `apiVersion`             | `tracix.io/v1`                                                                                                         |
| `package`                | A lowercase name of letters, digits, `_`, `.` and `-`, up to 128 characters, starting with a letter or digit           |
| `revision`               | Any label up to 128 characters, such as a version or a commit                                                          |
| `resources`              | 1 to 500 objects                                                                                                       |
| `resources[].kind`       | `monitor`, `visualization`, `dashboard` or `service_group`                                                             |
| `resources[].uid`        | The object's ID within the package, in the same form as `package`                                                      |
| `resources[].attributes` | The object's definition, as the workspace stores it                                                                    |
| `resources[].references` | Data sources, visualizations or dashboards it depends on. `@<uid>` refers to an object of that kind in the same bundle |
| `resources[].tests`      | Monitors only: threshold fixtures                                                                                      |
| `resources[].state`      | `present` (the default) or `absent`                                                                                    |

Validation is strict: an unknown field fails, and a monitor value that would have to be converted or clamped fails instead of being adjusted. Detection content can't go in a bundle; it comes from a detection repository.

### Tests

A value-threshold monitor can carry `tests`: a `name`, a numeric `value`, an optional `currentLevel` and the `expected` level (`ok`, `warn` or `alert`). Validation runs the real threshold logic, including recovery. Tests don't replay queries, pending periods, missing data timing, notifications or anomaly learning, so check those with real telemetry in a staging workspace.

### Delete and restore

Leaving an object out of a bundle never deletes it. To delete one, keep it in the bundle with `"state": "absent"`, plan and apply: the plan shows `delete`. Set it back to `present` to restore it. Apply writes visualizations before the dashboards that use them and deletes dashboards first, so delete or update the dashboards that reference a visualization in the same change.

### Publish a bundle

> **Workspace admins only, for now**
>
> Your workspace validates, plans, applies and exports bundles only for a signed-in workspace admin. API keys can't publish monitor and dashboard bundles yet, so you can't apply them from unattended CI today.

An export captures each object's current definition and operational controls. It isn't a backup of your Git file: tests and comments aren't stored in the workspace.

## Next steps

- [Run as code in CI](https://tracix.io/docs/api/ci): Plan on pull requests, apply from main

- [Workspace API reference](https://tracix.io/docs/api/workspace-api): The detection content endpoints

- [Detection rules and shadow mode](https://tracix.io/docs/security/detections): Rules, stages and shadow mode in the workspace

- [Monitors and templates](https://tracix.io/docs/alerting/monitors): How monitors evaluate and alert

- [Metrics and dashboards](https://tracix.io/docs/observe/dashboards): Build and share dashboards
