Now accepting design partners — Platform teams & regulated enterprises. Contact us to get provisioned →

Getting Started

Contact us to get provisioned, commit your CALM document and config mapping, and ArchRails will enforce your architecture on every PR or MR automatically.

GitHub App GitLab CI + Webhook FINOS CALM 1.2 Repo-owned config Contact to provision
1

Connect your platform

Contact us to get provisioned — we'll set up your organization, provide credentials, and connect your GitHub or GitLab integration. Choose your platform below for setup details.

We install the ArchRails GitHub App into your organization during provisioning. It listens for pull request events and posts CALM-backed review comments — no CI changes needed on your side.

Permissions

The app requests read access to pull request diffs and write access to post review comments. Your source code is never copied or stored.

GitLab integration uses a CI pipeline job that fires on every merge request event and calls the ArchRails webhook. No marketplace install needed — just add the job and set three variables.

Before you start

You'll receive your ARCHRAILS_ENDPOINT, GITLAB_WEBHOOK_SECRET, and ARCHRAILS_TENANT_ID during provisioning. Contact us to get started.

Add this job to your .gitlab-ci.yml

.gitlab-ci.yml
Add to your repo
stages:
  - review

archrails_review:
  stage: review
  image: alpine:3.20

  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'

  before_script:
    - apk add --no-cache curl

  script:
    - echo "Triggering ArchRails review..."
    - |
      set -euo pipefail

      : "${ARCHRAILS_ENDPOINT:?ARCHRAILS_ENDPOINT is required}"
      : "${GITLAB_WEBHOOK_SECRET:?GITLAB_WEBHOOK_SECRET is required}"
      : "${ARCHRAILS_TENANT_ID:?ARCHRAILS_TENANT_ID is required}"

      delivery_id="${CI_PIPELINE_ID}-${CI_JOB_ID}-${CI_COMMIT_SHA}"

      curl --silent --show-error --fail-with-body -X POST "${ARCHRAILS_ENDPOINT}" \
        -H "X-Gitlab-Token: ${GITLAB_WEBHOOK_SECRET}" \
        -H "X-Gitlab-Event: Merge Request Hook" \
        -H "X-Gitlab-Delivery: ${delivery_id}" \
        -H "Content-Type: application/json" \
        -d "{
          \"object_kind\": \"merge_request\",
          \"archrails_tenant_id\": \"${ARCHRAILS_TENANT_ID}\",
          \"project\": {
            \"id\": ${CI_PROJECT_ID},
            \"path_with_namespace\": \"${CI_PROJECT_PATH}\",
            \"web_url\": \"${CI_PROJECT_URL}\"
          },
          \"object_attributes\": {
            \"iid\": ${CI_MERGE_REQUEST_IID},
            \"action\": \"update\",
            \"last_commit\": { \"id\": \"${CI_COMMIT_SHA}\" },
            \"url\": \"${CI_PROJECT_URL}/-/merge_requests/${CI_MERGE_REQUEST_IID}\"
          }
        }"

Set these CI/CD variables in your project settings

Settings → CI/CD → Variables
Variable Where to get it
ARCHRAILS_ENDPOINT Provided by the ArchRails team on provisioning
GITLAB_WEBHOOK_SECRET Shared secret provided by the ArchRails team on provisioning
ARCHRAILS_TENANT_ID Your organization identifier, provided on provisioning
2

Commit your CALM document and config

Add two files to your repo: a calm.json describing your architecture graph, and a .archrails/config.yaml that maps your source paths to CALM node IDs. Together these become the enforceable source of truth ArchRails verifies every PR against.

File layout

CALM document: docs/architecture/calm.json
Config mapping: .archrails/config.yaml

.archrails/config.yaml
Repo-owned
version: 1

default_architecture: layered-services

governance:
  provider: calm
  file: docs/architecture/calm.json   # path to your CALM document

mapping:
  - path: src/EcommercePlatform.java
    node: system-ecommerce-platform

  - path: src/EcommercePlatform.java#ApiGateway
    node: service-api-gateway

  - path: src/EcommercePlatform.java#OrderService
    node: service-order

  - path: src/EcommercePlatform.java#InventoryService
    node: service-inventory

  - path: src/EcommercePlatform.java#PaymentService
    node: service-payment

  - path: src/EcommercePlatform.java#OrderDatabase
    node: db-orders

  - path: src/EcommercePlatform.java#InventoryDatabase
    node: db-inventory

review:
  on: pull_request

integrations:
  github:
    app: archrails
docs/architecture/calm.json
FINOS CALM 1.2
{
  "$schema": "https://calm.finos.org/release/1.2/meta/calm.json",
  "metadata": {
    "owner": "platform-team@example.com",
    "version": "1.0.0",
    "description": "E-commerce order processing platform"
  },
  "nodes": [
    { "unique-id": "service-api-gateway",  "node-type": "service",  "name": "API Gateway",
      "interfaces": [{ "unique-id": "iface-api-http", "protocol": "HTTPS", "port": 443 }] },
    { "unique-id": "service-order",        "node-type": "service",  "name": "Order Service",
      "interfaces": [{ "unique-id": "iface-order-rest", "protocol": "HTTPS", "port": 8080 }] },
    { "unique-id": "service-payment",      "node-type": "service",  "name": "Payment Service",
      "interfaces": [{ "unique-id": "iface-payment-rest", "protocol": "HTTPS", "port": 8080 }] },
    { "unique-id": "db-orders",            "node-type": "database", "name": "Order Database",
      "interfaces": [{ "unique-id": "iface-orders-sql", "protocol": "JDBC", "port": 5432 }] }
  ],
  "relationships": [
    {
      "unique-id": "rel-connects-api-order",
      "relationship-type": {
        "connects": {
          "source":      { "node": "service-api-gateway", "interfaces": ["iface-api-http"] },
          "destination": { "node": "service-order",       "interfaces": ["iface-order-rest"] }
        }
      }
    },
    {
      "unique-id": "rel-connects-order-payment",
      "relationship-type": {
        "connects": {
          "source":      { "node": "service-order",   "interfaces": ["iface-order-rest"] },
          "destination": { "node": "service-payment", "interfaces": ["iface-payment-rest"] }
        }
      }
    },
    {
      "unique-id": "rel-connects-order-db",
      "relationship-type": {
        "connects": {
          "source":      { "node": "service-order", "interfaces": ["iface-order-rest"] },
          "destination": { "node": "db-orders",     "interfaces": ["iface-orders-sql"] }
        }
      }
    }
  ]
}
How ArchRails uses these files
  • CALM nodes and relationships are the structural backbone — allowed connections, interface protocols, and node boundaries are graph-checked deterministically.
  • Path → node mapping scopes every review to the exact CALM nodes touched by the PR diff. Only relevant nodes are checked.
  • Every PR comment cites the CALM node ID or relationship that triggered it — fully traceable, no generic guesses.
3

CALM nodes are resolved per PR

ArchRails uses the mapping section in your config to determine which CALM nodes are touched by each PR diff. Only the relevant nodes and their relationships are checked — changes to services/payment/ won't trigger Order Service rules.

.archrails/config.yaml — monorepo scoping
mapping:
  # Order service module → CALM node
  - path: services/order/src
    node: service-order
  - path: services/order/src/db
    node: db-orders

  # Inventory service module → CALM node
  - path: services/inventory/src
    node: service-inventory

  # Payment service module → CALM node
  - path: services/payment/src
    node: service-payment

  # API Gateway module → CALM node
  - path: services/api-gateway/src
    node: service-api-gateway

scopes:
  - name: "Order + Inventory"
    match:
      any_prefix:
        - "services/order/"
        - "services/inventory/"

  - name: "Payment"
    match:
      any_prefix: ["services/payment/"]

  - name: "Gateway"
    match:
      any_prefix: ["services/api-gateway/"]
How node resolution works

For each PR, ArchRails computes the set of changed file paths, resolves them to CALM node IDs via the mapping table, then verifies only the relationships and interface contracts involving those nodes. No other nodes or constraints are evaluated.

4

Get CALM-backed PR feedback

On every PR or MR, ArchRails posts a review comment citing the exact CALM node ID, relationship, or interface contract that was violated — with a plain-English explanation of why. No generic guesses.

Example PR comment from ArchRails
🏗️ CALM Architecture Review

⚠️  Relationship violation: OrderService calls PaymentService directly.
    CALM node rel-connects-order-payment requires routing via service-api-gateway.
    → calm.json · rel-connects-order-payment

⚠️  Interface breach: OrderDatabase accessed via HTTP.
    Node db-orders declares JDBC:5432 only.
    → calm.json · db-orders · iface-orders-sql

✅  Node boundary respected: InventoryService changes are scoped to
    service-inventory — no cross-node leakage detected.

Sources: .archrails/config.yaml · calm.json · service-order · db-orders
What to do next
  • Open a real PR and verify the first CALM review comment cites the correct node IDs
  • Expand your calm.json with additional relationships and interface contracts as your system grows
  • Add more path mappings to config.yaml as new services or modules are introduced