Skip to content
Closed
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
11 changes: 11 additions & 0 deletions .github/workflows/build-calm-guard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ jobs:
- name: Install dependencies
run: npm ci

# @finos/calm-models is a typed dependency of CALMGuard's conformance
# check (src/lib/calm/conformance.ts). It ships from dist/ and has no
# prepare script, so it must be built before typecheck/lint resolve it.
- name: Build @finos/calm-models
run: npm run build --workspace=@finos/calm-models

- name: Lint
run: npm run lint --workspace=calmguard

Expand Down Expand Up @@ -88,5 +94,10 @@ jobs:
- name: Install dependencies
run: npm ci

# next build type-checks, and conformance.ts imports @finos/calm-models
# types — build it first (ships from dist/, no prepare script).
- name: Build @finos/calm-models
run: npm run build --workspace=@finos/calm-models

- name: Build
run: npm run build --workspace=calmguard
56 changes: 56 additions & 0 deletions calm-suite/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# calm-suite dev launcher
#
# The calm-suite products are root npm workspaces, so every target runs from the
# repo root (one level up). Requires Node 22 (`nvm use` at the repo root).
#
# Usage: `make -C calm-suite <target>` (or `cd calm-suite && make <target>`)

ROOT := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))/..)
NPM := cd $(ROOT) && npm

# Built entrypoint for the CalmStudio MCP server (bin: calmstudio-mcp)
MCP_BIN := $(ROOT)/calm-suite/calm-studio/packages/mcp-server/dist/index.js

.DEFAULT_GOAL := help
.PHONY: help install guard guard-docs studio studio-desktop studio-deps build-guard build-studio test mcp-build mcp claude-mcp

help: ## List available targets
@grep -hE '^[a-z0-9-]+:.*?## ' $(MAKEFILE_LIST) \
| awk 'BEGIN{FS=":.*?## "}{printf " \033[36m%-15s\033[0m %s\n", $$1, $$2}'

install: ## Install all workspaces from the lockfile (npm ci, repo root)
$(NPM) ci

guard: ## Run CALMGuard (Next.js) dev server
$(NPM) run dev --workspace=calmguard

guard-docs: ## Run CALMGuard docs (Docusaurus) dev server
$(NPM) run docs:dev --workspace=calmguard

studio-deps: ## Build CALM Studio workspace deps that ship from dist (calm-core)
$(NPM) run build --workspace=@calmstudio/calm-core

studio: studio-deps ## Run CALM Studio (SvelteKit) web dev server
$(NPM) run dev --workspace=@calmstudio/studio

studio-desktop: studio-deps ## Run CALM Studio desktop shell (Tauri)
$(NPM) run tauri --workspace=@calmstudio/studio dev

build-guard: ## Production build of CALMGuard
$(NPM) run build --workspace=calmguard

build-studio: ## Production build of CALM Studio
$(NPM) run build --workspace=@calmstudio/studio

mcp-build: studio-deps ## Build the CalmStudio MCP server (and its calm-core dep)
$(NPM) run build --workspace=@calmstudio/mcp

mcp: mcp-build ## Build and run the CalmStudio MCP server (stdio transport)
node $(MCP_BIN)

claude-mcp: mcp-build ## Register the CalmStudio MCP server with Claude Code
claude mcp add --transport stdio calmstudio -- node $(MCP_BIN)

test: ## Run tests for both calm-suite products
$(NPM) run test:run --workspace=calmguard
$(NPM) run test --workspace=@calmstudio/studio
19 changes: 13 additions & 6 deletions calm-suite/calm-guard/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,23 @@ Source: `https://github.com/finos/architecture-as-code` (CALM release 1.2)
### Core Entities

**Nodes** (required: unique-id, node-type, name, description):
- `node-type` enum: `actor`, `ecosystem`, `system`, `service`, `database`, `network`, `ldap`, `webclient`, `data-asset`
- `node-type`: any string. The 9 well-known values (`actor`, `ecosystem`, `system`, `service`, `database`, `network`, `ldap`, `webclient`, `data-asset`) get first-class UI, but canonical CALM allows any string (e.g. extension types like `aws:lambda`), so CALMGuard accepts any string for interoperability.
- Optional: `interfaces[]`, `controls{}`, `metadata`, `details`

**Relationships** (required: unique-id, relationship-type):
- Types (mutually exclusive): `interacts`, `connects`, `deployed-in`, `composed-of`, `options`
- `interacts`: `{ actor: string, nodes: string[] }`
- `connects`: `{ source: { node, interfaces[] }, destination: { node, interfaces[] } }`
- `deployed-in` / `composed-of`: `{ container: string, nodes: string[] }`
- `relationship-type` is a **nested object** keyed by exactly one variant — NOT a string discriminant. The variant payload lives inside it; `protocol`/`controls`/`metadata`/`description` are siblings of `relationship-type` at the relationship level.
- Canonical nested form:
```json
{ "unique-id": "r1", "relationship-type": { "connects": { "source": { "node": "a" }, "destination": { "node": "b" } } }, "protocol": "HTTPS" }
```
- Variants (exactly one present):
- `connects`: `{ source: { node, interfaces?[] }, destination: { node, interfaces?[] } }`
- `interacts`: `{ actor: string, nodes: string[] }`
- `deployed-in` / `composed-of`: `{ container: string, nodes: string[] }`
- `options`: loosely typed (CALMGuard treats as `unknown`)
- `protocol` enum: `HTTP`, `HTTPS`, `FTP`, `SFTP`, `JDBC`, `WebSocket`, `SocketIO`, `LDAP`, `AMQP`, `TLS`, `mTLS`, `TCP`
- Optional: `controls{}`, `metadata`, `description`
- Optional (siblings): `controls{}`, `metadata`, `description`
- NOTE: the legacy flat form (`"relationship-type": "connects"` + sibling `connects`) is **no longer accepted** — use the nested form so documents round-trip with the CLI, Hub, Visualizer, and Studio.

**Controls** (pattern-keyed object `^[a-zA-Z0-9-]+$`):
- Each control: `{ description (required), requirements: [{ requirement-url (required), config-url | config }] }`
Expand Down
116 changes: 61 additions & 55 deletions calm-suite/calm-guard/examples/payment-gateway.calm.json
Original file line number Diff line number Diff line change
Expand Up @@ -163,26 +163,28 @@
"relationships": [
{
"unique-id": "customer-to-checkout",
"relationship-type": "interacts",
"description": "Customer enters payment information on checkout page",
"interacts": {
"actor": "customer",
"nodes": ["checkout-page"]
}
"relationship-type": {
"interacts": {
"actor": "customer",
"nodes": ["checkout-page"]
}
},
"description": "Customer enters payment information on checkout page"
},
{
"unique-id": "checkout-to-payment-api",
"relationship-type": "connects",
"description": "Checkout page submits encrypted payment data to payment API",
"protocol": "HTTPS",
"connects": {
"source": {
"node": "checkout-page"
},
"destination": {
"node": "payment-api"
"relationship-type": {
"connects": {
"source": {
"node": "checkout-page"
},
"destination": {
"node": "payment-api"
}
}
},
"description": "Checkout page submits encrypted payment data to payment API",
"protocol": "HTTPS",
"controls": {
"tls-enforcement": {
"description": "Enforce TLS 1.2 minimum with strong cipher suites",
Expand All @@ -196,59 +198,63 @@
},
{
"unique-id": "payment-api-to-tokenization",
"relationship-type": "connects",
"description": "Payment API requests card tokenization",
"protocol": "mTLS",
"connects": {
"source": {
"node": "payment-api"
},
"destination": {
"node": "tokenization-service"
"relationship-type": {
"connects": {
"source": {
"node": "payment-api"
},
"destination": {
"node": "tokenization-service"
}
}
}
},
"description": "Payment API requests card tokenization",
"protocol": "mTLS"
},
{
"unique-id": "payment-api-to-fraud",
"relationship-type": "connects",
"description": "Payment API checks transaction for fraud indicators",
"protocol": "HTTPS",
"connects": {
"source": {
"node": "payment-api"
},
"destination": {
"node": "fraud-detection"
"relationship-type": {
"connects": {
"source": {
"node": "payment-api"
},
"destination": {
"node": "fraud-detection"
}
}
}
},
"description": "Payment API checks transaction for fraud indicators",
"protocol": "HTTPS"
},
{
"unique-id": "payment-api-to-db",
"relationship-type": "connects",
"description": "Payment API persists transaction records",
"protocol": "JDBC",
"connects": {
"source": {
"node": "payment-api"
},
"destination": {
"node": "transaction-database"
"relationship-type": {
"connects": {
"source": {
"node": "payment-api"
},
"destination": {
"node": "transaction-database"
}
}
}
},
"description": "Payment API persists transaction records",
"protocol": "JDBC"
},
{
"unique-id": "tokenization-to-card-network",
"relationship-type": "connects",
"description": "Tokenization service forwards authorization request to card network",
"protocol": "mTLS",
"connects": {
"source": {
"node": "tokenization-service"
},
"destination": {
"node": "card-network"
"relationship-type": {
"connects": {
"source": {
"node": "tokenization-service"
},
"destination": {
"node": "card-network"
}
}
}
},
"description": "Tokenization service forwards authorization request to card network",
"protocol": "mTLS"
}
],
"flows": [
Expand Down
Loading
Loading