Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions cmd/ampel-plugin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ The plugin uses granular AMPEL policy files (one JSON file per control) stored i

Sample policy files are available in the [complytime-demos](https://github.com/complytime/complytime-demos) repository under `base_ansible_env/files/ampel-policies/`.

**Creating AMPEL Policies**: Use `complyctl-provider-ampel init --tool <agent>` to generate AI agent skill definitions for interactive policy creation. See [docs/MIGRATION.md](./docs/MIGRATION.md) for migration from manual `gemara2ampel` script.

### Generate

When the plugin receives the `generate` command from complyctl, it will:
Expand Down Expand Up @@ -196,6 +198,48 @@ The plugin is discovered automatically by complyctl — no manifest files or che

To use the plugin with `complyctl`, see the quick start [guide](../../docs/QUICK_START.md).

## Plugin Commands

The AMPEL plugin supports two command modes:

### Default Behavior (serve)

The plugin starts the gRPC server for runtime policy execution by default. When invoked by complyctl or run directly, it handles `Describe`, `Generate`, and `Scan` RPCs:

```bash
complyctl-provider-ampel
```

The plugin handles `Describe`, `Generate`, and `Scan` RPCs from complyctl.

### `init --tool <agent>` Command

Installs AI agent integration artifacts for creating AMPEL policies. Supported agents:

- `cursor`: Installs Cursor command to `.cursor/commands/ampel-create-policy.md` and skill to `.cursor/skills/ampel-create-policy/SKILL.md`
- `opencode`: Appends skill definition to `.cursorrules` in workspace root
- `claude-code`: Installs Claude Code tool definition to `.claude/ampel-create-policy-tool.md`

**Usage:**

```bash
# Install Cursor command and skill (/ampel-create-policy)
complyctl-provider-ampel init --tool cursor

# Install OpenCode skill definition
complyctl-provider-ampel init --tool opencode

# Install Claude Code tool definition
complyctl-provider-ampel init --tool claude-code
```

The `init` command:
- Creates necessary directories (`.cursor/`, `.claude/`) if they don't exist
- Writes agent-specific skill definitions to the correct locations
- Provides feedback on what was installed

After installation, AI coding assistants (OpenCode, Cursor, Claude Code) can help users create AMPEL policies from Gemara artifacts interactively.

### Using complytime-demos with a Fedora 43 VM

The [complytime-demos](https://github.com/complytime/complytime-demos) repository provides an automated way to set up a complete environment with complyctl, the ampel-plugin, and all required tools inside a Fedora 43 VM using Vagrant and Ansible.
Expand Down
63 changes: 63 additions & 0 deletions cmd/ampel-plugin/docs/MIGRATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Migration from gemara2ampel to init --tool

This document describes how to migrate from using the manual `gemara2ampel` script to using AI-assisted policy creation with the `init --tool` command.

## Overview

Previously, creating AMPEL policies from Gemara artifacts required:
1. Running the `gemara2ampel` script manually
2. Converting Gemara artifacts to AMPEL format
3. Manually editing and validating policies

With the new `init --tool` command, you can:
1. Generate AI agent skill definitions for your preferred tool (OpenCode, Cursor, Claude Code)
2. Use AI assistants to help create AMPEL policies interactively
3. Validate policies against the AMPEL schema automatically

## Migration Steps

### Step 1: Install Skill Definitions

Choose your AI coding assistant and install the appropriate skill definition:

```bash
# For OpenCode (installs to .cursorrules)
complyctl-provider-ampel init --tool opencode

# For Cursor (installs to .cursor/commands/ampel-create-policy.md and .cursor/skills/ampel-create-policy/SKILL.md)
complyctl-provider-ampel init --tool cursor

# For Claude Code (installs to .claude/ampel-policy-tool.json)
complyctl-provider-ampel init --tool claude-code
```

The `init` command automatically creates the necessary directories and installs the skill definitions in the correct locations.

### Step 3: Use AI Assistant to Create Policies
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Step 2?


Instead of running `gemara2ampel` manually, use your AI assistant:

- **OpenCode**: Ask it to create an AMPEL policy from a Gemara requirement
- **Cursor**: Use the `/ampel-create-policy` slash command
- **Claude Code**: Invoke the `create_ampel_policy` tool

The AI assistant will:
- Understand the AMPEL policy format
- Convert Gemara requirements to AMPEL policies
- Validate against the schema
- Provide guidance on policy structure

### Step 4: Validate Policies

Policies created with AI assistance should follow the AMPEL schema. The skill definitions include the complete JSON Schema for validation.

## Benefits

- **Interactive**: Get help from AI as you create policies
- **Validated**: Schema validation ensures correct structure
- **Documented**: Skill definitions include conversion guidelines
- **Consistent**: AI follows the same patterns every time

## gemara2ampel Script Status

The `gemara2ampel` script remains available in the [complytime-demos](https://github.com/complytime/complytime-demos) repository for batch conversion workflows. For interactive policy creation, prefer using `init --tool` with your AI coding assistant.
8 changes: 6 additions & 2 deletions cmd/ampel-plugin/docs/STRATEGY.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ During the `generate` phase, the plugin receives assessment configurations from

This granular approach is also aligned with how the [Gemara2Ampel](https://github.com/complytime/complytime-demos/tree/main/tools/gemara2ampel) tool works: Gemara Layer 3 policies map naturally to individual AMPEL policy files, and the workspace mode (`-w`) of Gemara2Ampel already produces one file per policy. When complyctl adopts Gemara as its policy source, this granular structure will allow the plugin to consume Gemara-generated policies without changes to the matching and merging logic.

**Note**: For interactive policy creation, use `complyctl-provider-ampel init --tool <agent>` to generate AI agent skill definitions. See [MIGRATION.md](./MIGRATION.md) for details.

## Multi-Target Scanning

The ampel-plugin introduces multi-target scanning to complyctl. Unlike the existing openscap-plugin, which scans the local system it runs on, the ampel-plugin scans remote repositories defined in the `complytime.yaml` configuration.
Expand All @@ -36,8 +38,10 @@ Together, these capabilities position complyctl as the layer that connects organ

## Next Actions

1. **Review and update the Gemara2Ampel tool to work with granular AMPEL policies**
The Gemara2Ampel converter currently supports workspace mode for generating individual policy files. It should be reviewed to ensure its output aligns with the granular policy format expected by the ampel-plugin (policy ID, meta.controls, tenets structure), so that Gemara-generated policies can be used directly as input to the plugin without manual adjustments.
1. **Use AI-assisted policy creation with `init --tool`**
For interactive policy creation, use `complyctl-provider-ampel init --tool <agent>` to generate AI agent skill definitions. This enables AI coding assistants (Cursor, OpenCode, Claude Code) to help create AMPEL policies from Gemara artifacts. See [MIGRATION.md](./MIGRATION.md) for migration guidance.

The Gemara2Ampel converter remains available in [complytime-demos](https://github.com/complytime/complytime-demos) for batch conversion workflows. Its workspace mode (`-w`) produces individual policy files that align with the granular policy format expected by the ampel-plugin (policy ID, meta.controls, tenets structure).

2. **Evolve the plugin API alongside complyctl**
As complyctl's Gemara integration matures, the plugin's `Generate` phase may need to handle additional policy metadata or parameters. The matching and merging logic in the `convert` package is isolated for this purpose. The `server.Generate` method should be updated as the API evolves.
Expand Down
19 changes: 19 additions & 0 deletions cmd/ampel-plugin/main.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,30 @@
package main

import (
_ "embed"

"github.com/complytime/complyctl/cmd/ampel-plugin/server"
"github.com/complytime/complyctl/pkg/plugin"
)

//go:embed templates/command.md
var commandTemplate string

//go:embed templates/skill.md
var skillTemplate string

func main() {
// Register init command handler
if plugin.RegisterInit(plugin.InitTemplates{
CommandTemplate: commandTemplate,
SkillTemplate: skillTemplate,
CommandName: "ampel-create-policy",
SkillName: "ampel-create-policy",
}) {
return
}

// Default behavior: serve the plugin
ampelPlugin := server.New()
plugin.Serve(ampelPlugin)
}
172 changes: 172 additions & 0 deletions cmd/ampel-plugin/templates/command.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
---
name: /ampel-create-policy
id: ampel-create-policy
category: Policy
description: Create an AMPEL policy from a Gemara requirement
---

Create an AMPEL policy from a Gemara requirement.

I'll help you convert a Gemara requirement to an AMPEL policy file with the correct structure, including policy metadata, control references, and verification tenets.

---

**Input**: The user should provide:
- Gemara requirement ID (e.g., 'ac-3')
- Compliance framework and control references (e.g., NIST-800-53 AC-3)
- Description of what the policy should verify

**Steps**

1. **Gather requirement information**
- Extract or ask for the Gemara requirement ID
- Identify compliance control references (framework, class, id)
- Understand what the policy should verify

2. **Create AMPEL policy structure**
- Policy id: Convert requirement ID to kebab-case (e.g., 'ac-3' → 'ac-3-access-control-enforcement')
- meta.description: Clear description of what the policy verifies
- meta.controls: Array of control references with framework, class, and id

3. **Define verification tenets**
- Create tenets array with verification checks
- Each tenet should have:
- id: Unique tenet identifier
- code: CEL expression that evaluates the attestation data
- predicates.types: Attestation predicate type URIs
- assessment.message: Success message
- error.message and error.guidance: Failure remediation

4. **Validate policy structure**
- Ensure all required fields are present: id, meta, tenets
- Verify meta has description and controls
- Check each tenet has id, code, and predicates fields

5. **Write policy file**
- Save as JSON file with kebab-case name matching policy id
- Place in appropriate AMPEL policy directory

**Output**

After creating the policy:
- Policy file location
- Summary of policy structure
- List of tenets created
- Next steps (e.g., test with ampel verify)

**AMPEL Policy Structure**

AMPEL policies are JSON files with the following structure:

```json
{
"id": "BP-1.01",
"meta": {
"description": "Validate branch protection settings require pull requests",
"controls": [
{ "framework": "repo-branch-protection", "class": "source-code", "id": "BP-1" }
]
},
"tenets": [
{
"id": "01",
"code": "has(predicates[0].data.values) ? predicates[0].data.values.exists(rule, rule.type == \"update\") : false",
"predicates": {
"types": ["http://github.com/carabiner-dev/snappy/specs/branch-rules.yaml"]
},
"assessment": {
"message": "Direct pushes are disabled in default branch. PR required."
},
"error": {
"message": "Direct pushes are enabled so PRs are not required.",
"guidance": "Create a branch ruleset protecting your default branch and enable \"Restrict updates\""
}
}
]
}
```

**Input Data Structure (Snappy Attestations)**

AMPEL policies evaluate in-toto attestations produced by snappy. The attestation structure is:

```json
{
"_type": "https://in-toto.io/Statement/v1",
"subject": [...],
"predicateType": "http://github.com/carabiner-dev/snappy/specs/branch-rules.yaml",
"predicate": {
"data": {
"values": [
{
"type": "update",
"parameters": {}
},
{
"type": "pull_request",
"parameters": {
"required_approving_review_count": 1,
"dismiss_stale_reviews_on_push": true,
"require_last_push_approval": true,
"require_code_owner_review": false
}
},
{
"type": "non_fast_forward",
"parameters": {}
}
]
}
}
}
```

**How Predicates Work**

1. Each tenet specifies `predicates.types` array with predicate type URIs (e.g., `http://github.com/carabiner-dev/snappy/specs/branch-rules.yaml`)
2. AMPEL matches attestations by predicate type URI
3. Matched predicates are available in the `predicates` array (indexed by match order)
4. Access attestation data via `predicates[0].data.values` (array of rule objects)

**CEL Expression Patterns**

The `code` field uses CEL (Common Expression Language) to evaluate attestation data:

- **Check if data exists**: `has(predicates[0].data.values)`
- **Find rule by type**: `predicates[0].data.values.exists(rule, rule.type == "update")`
- **Check rule parameters**: `predicates[0].data.values.exists(rule, rule.type == "pull_request" && rule.parameters.required_approving_review_count >= 1)`
- **Multiple conditions**: `predicates[0].data.values.exists(rule, rule.type == "pull_request" && rule.parameters.dismiss_stale_reviews_on_push == true)`

**Common Rule Types (GitHub Branch Protection)**

- `"update"`: Restricts direct pushes (requires PR)
- `"pull_request"`: Pull request requirements
- `parameters.required_approving_review_count`: Minimum approvals (integer)
- `parameters.dismiss_stale_reviews_on_push`: Dismiss stale reviews (boolean)
- `parameters.require_last_push_approval`: Require last push approval (boolean)
- `parameters.require_code_owner_review`: Require code owner review (boolean)
- `"non_fast_forward"`: Blocks force pushes

**Example CEL Expressions**

```cel
// Check if direct pushes are disabled
has(predicates[0].data.values) ? predicates[0].data.values.exists(rule, rule.type == "update") : false

// Check minimum approval count
has(predicates[0].data.values) ? predicates[0].data.values.exists(rule, rule.type == "pull_request" && rule.parameters.required_approving_review_count >= 1) : false

// Check multiple PR requirements
has(predicates[0].data.values) ? predicates[0].data.values.exists(rule, rule.type == "pull_request" && rule.parameters.dismiss_stale_reviews_on_push == true && rule.parameters.require_last_push_approval == true) : false

// Check force push blocking
has(predicates[0].data.values) ? predicates[0].data.values.exists(rule, rule.type == "non_fast_forward") : false
```

**Gemara to AMPEL Conversion Guidelines**

- Map Gemara requirement ID to AMPEL policy id (kebab-case)
- Convert Gemara control references to meta.controls array format
- Transform Gemara assessment requirements into AMPEL tenets
- Each tenet represents a specific verification check
- Use CEL expressions to evaluate snappy attestation data structure
Loading
Loading