Skip to content

Commit f4c237a

Browse files
committed
feat(calm-suite): adopt canonical nested relationship-type in CALMGuard
Bring CALMGuard onto the canonical nested relationship-type form for cross-tool interop with CalmStudio, updating the extractor/normalizer/types, examples, and tests. Signed-off-by: Eddie Knight <knight@linux.com>
1 parent 0a4f929 commit f4c237a

22 files changed

Lines changed: 1486 additions & 363 deletions

calm-suite/calm-guard/AGENTS.md

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,23 @@ Source: `https://github.com/finos/architecture-as-code` (CALM release 1.2)
3131
### Core Entities
3232

3333
**Nodes** (required: unique-id, node-type, name, description):
34-
- `node-type` enum: `actor`, `ecosystem`, `system`, `service`, `database`, `network`, `ldap`, `webclient`, `data-asset`
34+
- `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.
3535
- Optional: `interfaces[]`, `controls{}`, `metadata`, `details`
3636

3737
**Relationships** (required: unique-id, relationship-type):
38-
- Types (mutually exclusive): `interacts`, `connects`, `deployed-in`, `composed-of`, `options`
39-
- `interacts`: `{ actor: string, nodes: string[] }`
40-
- `connects`: `{ source: { node, interfaces[] }, destination: { node, interfaces[] } }`
41-
- `deployed-in` / `composed-of`: `{ container: string, nodes: string[] }`
38+
- `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.
39+
- Canonical nested form:
40+
```json
41+
{ "unique-id": "r1", "relationship-type": { "connects": { "source": { "node": "a" }, "destination": { "node": "b" } } }, "protocol": "HTTPS" }
42+
```
43+
- Variants (exactly one present):
44+
- `connects`: `{ source: { node, interfaces?[] }, destination: { node, interfaces?[] } }`
45+
- `interacts`: `{ actor: string, nodes: string[] }`
46+
- `deployed-in` / `composed-of`: `{ container: string, nodes: string[] }`
47+
- `options`: loosely typed (CALMGuard treats as `unknown`)
4248
- `protocol` enum: `HTTP`, `HTTPS`, `FTP`, `SFTP`, `JDBC`, `WebSocket`, `SocketIO`, `LDAP`, `AMQP`, `TLS`, `mTLS`, `TCP`
43-
- Optional: `controls{}`, `metadata`, `description`
49+
- Optional (siblings): `controls{}`, `metadata`, `description`
50+
- 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.
4451

4552
**Controls** (pattern-keyed object `^[a-zA-Z0-9-]+$`):
4653
- Each control: `{ description (required), requirements: [{ requirement-url (required), config-url | config }] }`

calm-suite/calm-guard/examples/payment-gateway.calm.json

Lines changed: 61 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -163,26 +163,28 @@
163163
"relationships": [
164164
{
165165
"unique-id": "customer-to-checkout",
166-
"relationship-type": "interacts",
167-
"description": "Customer enters payment information on checkout page",
168-
"interacts": {
169-
"actor": "customer",
170-
"nodes": ["checkout-page"]
171-
}
166+
"relationship-type": {
167+
"interacts": {
168+
"actor": "customer",
169+
"nodes": ["checkout-page"]
170+
}
171+
},
172+
"description": "Customer enters payment information on checkout page"
172173
},
173174
{
174175
"unique-id": "checkout-to-payment-api",
175-
"relationship-type": "connects",
176-
"description": "Checkout page submits encrypted payment data to payment API",
177-
"protocol": "HTTPS",
178-
"connects": {
179-
"source": {
180-
"node": "checkout-page"
181-
},
182-
"destination": {
183-
"node": "payment-api"
176+
"relationship-type": {
177+
"connects": {
178+
"source": {
179+
"node": "checkout-page"
180+
},
181+
"destination": {
182+
"node": "payment-api"
183+
}
184184
}
185185
},
186+
"description": "Checkout page submits encrypted payment data to payment API",
187+
"protocol": "HTTPS",
186188
"controls": {
187189
"tls-enforcement": {
188190
"description": "Enforce TLS 1.2 minimum with strong cipher suites",
@@ -196,59 +198,63 @@
196198
},
197199
{
198200
"unique-id": "payment-api-to-tokenization",
199-
"relationship-type": "connects",
200-
"description": "Payment API requests card tokenization",
201-
"protocol": "mTLS",
202-
"connects": {
203-
"source": {
204-
"node": "payment-api"
205-
},
206-
"destination": {
207-
"node": "tokenization-service"
201+
"relationship-type": {
202+
"connects": {
203+
"source": {
204+
"node": "payment-api"
205+
},
206+
"destination": {
207+
"node": "tokenization-service"
208+
}
208209
}
209-
}
210+
},
211+
"description": "Payment API requests card tokenization",
212+
"protocol": "mTLS"
210213
},
211214
{
212215
"unique-id": "payment-api-to-fraud",
213-
"relationship-type": "connects",
214-
"description": "Payment API checks transaction for fraud indicators",
215-
"protocol": "HTTPS",
216-
"connects": {
217-
"source": {
218-
"node": "payment-api"
219-
},
220-
"destination": {
221-
"node": "fraud-detection"
216+
"relationship-type": {
217+
"connects": {
218+
"source": {
219+
"node": "payment-api"
220+
},
221+
"destination": {
222+
"node": "fraud-detection"
223+
}
222224
}
223-
}
225+
},
226+
"description": "Payment API checks transaction for fraud indicators",
227+
"protocol": "HTTPS"
224228
},
225229
{
226230
"unique-id": "payment-api-to-db",
227-
"relationship-type": "connects",
228-
"description": "Payment API persists transaction records",
229-
"protocol": "JDBC",
230-
"connects": {
231-
"source": {
232-
"node": "payment-api"
233-
},
234-
"destination": {
235-
"node": "transaction-database"
231+
"relationship-type": {
232+
"connects": {
233+
"source": {
234+
"node": "payment-api"
235+
},
236+
"destination": {
237+
"node": "transaction-database"
238+
}
236239
}
237-
}
240+
},
241+
"description": "Payment API persists transaction records",
242+
"protocol": "JDBC"
238243
},
239244
{
240245
"unique-id": "tokenization-to-card-network",
241-
"relationship-type": "connects",
242-
"description": "Tokenization service forwards authorization request to card network",
243-
"protocol": "mTLS",
244-
"connects": {
245-
"source": {
246-
"node": "tokenization-service"
247-
},
248-
"destination": {
249-
"node": "card-network"
246+
"relationship-type": {
247+
"connects": {
248+
"source": {
249+
"node": "tokenization-service"
250+
},
251+
"destination": {
252+
"node": "card-network"
253+
}
250254
}
251-
}
255+
},
256+
"description": "Tokenization service forwards authorization request to card network",
257+
"protocol": "mTLS"
252258
}
253259
],
254260
"flows": [

calm-suite/calm-guard/examples/trading-platform.calm.json

Lines changed: 96 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -154,124 +154,133 @@
154154
"relationships": [
155155
{
156156
"unique-id": "trader-to-dashboard",
157-
"relationship-type": "interacts",
158-
"description": "Trader accesses trading dashboard via web browser",
159-
"interacts": {
160-
"actor": "trader",
161-
"nodes": ["trading-dashboard"]
162-
}
157+
"relationship-type": {
158+
"interacts": {
159+
"actor": "trader",
160+
"nodes": ["trading-dashboard"]
161+
}
162+
},
163+
"description": "Trader accesses trading dashboard via web browser"
163164
},
164165
{
165166
"unique-id": "dashboard-to-order-mgmt",
166-
"relationship-type": "connects",
167-
"description": "Trading dashboard submits orders to order management service",
168-
"protocol": "HTTPS",
169-
"connects": {
170-
"source": {
171-
"node": "trading-dashboard"
172-
},
173-
"destination": {
174-
"node": "order-mgmt-service"
167+
"relationship-type": {
168+
"connects": {
169+
"source": {
170+
"node": "trading-dashboard"
171+
},
172+
"destination": {
173+
"node": "order-mgmt-service"
174+
}
175175
}
176-
}
176+
},
177+
"description": "Trading dashboard submits orders to order management service",
178+
"protocol": "HTTPS"
177179
},
178180
{
179181
"unique-id": "order-mgmt-to-risk",
180-
"relationship-type": "connects",
181-
"description": "Order management service validates orders with risk engine",
182-
"protocol": "HTTPS",
183-
"connects": {
184-
"source": {
185-
"node": "order-mgmt-service"
186-
},
187-
"destination": {
188-
"node": "risk-engine"
182+
"relationship-type": {
183+
"connects": {
184+
"source": {
185+
"node": "order-mgmt-service"
186+
},
187+
"destination": {
188+
"node": "risk-engine"
189+
}
189190
}
190-
}
191+
},
192+
"description": "Order management service validates orders with risk engine",
193+
"protocol": "HTTPS"
191194
},
192195
{
193196
"unique-id": "order-mgmt-to-fix",
194-
"relationship-type": "connects",
195-
"description": "Order management service routes validated orders to FIX gateway",
196-
"protocol": "TCP",
197-
"connects": {
198-
"source": {
199-
"node": "order-mgmt-service"
200-
},
201-
"destination": {
202-
"node": "fix-gateway"
197+
"relationship-type": {
198+
"connects": {
199+
"source": {
200+
"node": "order-mgmt-service"
201+
},
202+
"destination": {
203+
"node": "fix-gateway"
204+
}
203205
}
204-
}
206+
},
207+
"description": "Order management service routes validated orders to FIX gateway",
208+
"protocol": "TCP"
205209
},
206210
{
207211
"unique-id": "order-mgmt-to-db",
208-
"relationship-type": "connects",
209-
"description": "Order management service persists orders to database",
210-
"protocol": "JDBC",
211-
"connects": {
212-
"source": {
213-
"node": "order-mgmt-service"
214-
},
215-
"destination": {
216-
"node": "order-database"
212+
"relationship-type": {
213+
"connects": {
214+
"source": {
215+
"node": "order-mgmt-service"
216+
},
217+
"destination": {
218+
"node": "order-database"
219+
}
217220
}
218-
}
221+
},
222+
"description": "Order management service persists orders to database",
223+
"protocol": "JDBC"
219224
},
220225
{
221226
"unique-id": "market-data-to-cache",
222-
"relationship-type": "connects",
223-
"description": "Market data service updates real-time price cache",
224-
"protocol": "TCP",
225-
"connects": {
226-
"source": {
227-
"node": "market-data-service"
228-
},
229-
"destination": {
230-
"node": "market-data-cache"
227+
"relationship-type": {
228+
"connects": {
229+
"source": {
230+
"node": "market-data-service"
231+
},
232+
"destination": {
233+
"node": "market-data-cache"
234+
}
231235
}
232-
}
236+
},
237+
"description": "Market data service updates real-time price cache",
238+
"protocol": "TCP"
233239
},
234240
{
235241
"unique-id": "fix-to-exchange",
236-
"relationship-type": "connects",
237-
"description": "FIX gateway connects to exchange for order execution",
238-
"protocol": "mTLS",
239-
"connects": {
240-
"source": {
241-
"node": "fix-gateway"
242-
},
243-
"destination": {
244-
"node": "exchange-gateway"
242+
"relationship-type": {
243+
"connects": {
244+
"source": {
245+
"node": "fix-gateway"
246+
},
247+
"destination": {
248+
"node": "exchange-gateway"
249+
}
245250
}
246-
}
251+
},
252+
"description": "FIX gateway connects to exchange for order execution",
253+
"protocol": "mTLS"
247254
},
248255
{
249256
"unique-id": "dashboard-to-market-data",
250-
"relationship-type": "connects",
251-
"description": "Trading dashboard subscribes to real-time market data",
252-
"protocol": "WebSocket",
253-
"connects": {
254-
"source": {
255-
"node": "trading-dashboard"
256-
},
257-
"destination": {
258-
"node": "market-data-service"
257+
"relationship-type": {
258+
"connects": {
259+
"source": {
260+
"node": "trading-dashboard"
261+
},
262+
"destination": {
263+
"node": "market-data-service"
264+
}
259265
}
260-
}
266+
},
267+
"description": "Trading dashboard subscribes to real-time market data",
268+
"protocol": "WebSocket"
261269
},
262270
{
263271
"unique-id": "trading-services-network",
264-
"relationship-type": "deployed-in",
265-
"description": "Core trading services deployed in low-latency network",
266-
"deployed-in": {
267-
"container": "trading-network",
268-
"nodes": [
269-
"order-mgmt-service",
270-
"fix-gateway",
271-
"risk-engine",
272-
"market-data-service"
273-
]
274-
}
272+
"relationship-type": {
273+
"deployed-in": {
274+
"container": "trading-network",
275+
"nodes": [
276+
"order-mgmt-service",
277+
"fix-gateway",
278+
"risk-engine",
279+
"market-data-service"
280+
]
281+
}
282+
},
283+
"description": "Core trading services deployed in low-latency network"
275284
}
276285
],
277286
"flows": [

0 commit comments

Comments
 (0)