Three steps to
governed architecture
ArchRails enforces your CALM architecture on every pull request. Get from zero to your first automated review in under a day. Walk through the steps below in order — each one tells you exactly what to verify before moving on.
1
Get provisioned
Sign up and your tenant is ready in seconds.
Sign up with your work email. We mint a tenant ID + signing secret automatically — no operator in the loop. The 14-day Trial includes the full Team-tier toolkit on private repos.
2
Wire your repo
Add a workflow file so each PR triggers an ArchRails review.
# .github/workflows/archrails.yml name: ArchRails Architecture Check on: pull_request: types: [opened, synchronize, reopened, ready_for_review, closed] jobs: archrails-check: name: Architecture Governance runs-on: ubuntu-latest if: github.event.action != 'closed' || github.event.pull_request.merged == true permissions: contents: write pull-requests: write statuses: write steps: - name: Dispatch to ArchRails id: dispatch env: ARCHRAILS_TENANT_ID: ${{ secrets.ARCHRAILS_TENANT_ID }} ARCHRAILS_TOKEN: ${{ secrets.ARCHRAILS_TOKEN }} ARCHRAILS_URL: ${{ secrets.ARCHRAILS_WEBHOOK_URL }} ARCHRAILS_PARENT_TOKEN: ${{ secrets.ARCHRAILS_PARENT_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} PR_EVENT: ${{ toJson(github.event) }} RUN_ID: ${{ github.run_id }} RUN_ATTEMPT: ${{ github.run_attempt }} run: | : "${ARCHRAILS_TENANT_ID:?ARCHRAILS_TENANT_ID secret is required}" : "${ARCHRAILS_TOKEN:?ARCHRAILS_TOKEN secret is required}" : "${ARCHRAILS_URL:?ARCHRAILS_WEBHOOK_URL secret is required}" PAYLOAD=$(echo "$PR_EVENT" | jq \ --arg tenant_id "$ARCHRAILS_TENANT_ID" \ --arg parent_token "$ARCHRAILS_PARENT_TOKEN" \ --arg github_token "$GITHUB_TOKEN" \ '. + {archrails_tenant_id: $tenant_id, archrails_parent_token: $parent_token, github_token: $github_token}') RESP=$(curl -sS -o /tmp/ar_resp.json -w "%{http_code}" \ --max-time 30 \ -X POST "$ARCHRAILS_URL" \ -H "Content-Type: application/json" \ -H "X-GitHub-Event: pull_request" \ -H "X-GitHub-Delivery: ${RUN_ID}-${RUN_ATTEMPT}" \ -H "X-ArchRails-CI: github" \ -H "X-ArchRails-Token: $ARCHRAILS_TOKEN" \ -d "$PAYLOAD") echo "ArchRails response: $RESP" cat /tmp/ar_resp.json; echo if [[ "$RESP" == "204" ]]; then echo "ArchRails skipped this event — nothing to poll." exit 0 fi if [[ "$RESP" != "202" ]]; then echo "::error::ArchRails rejected the payload (HTTP $RESP)." exit 1 fi RUN_ID_OUT=$(jq -r '.run_id // empty' /tmp/ar_resp.json) if [[ -z "$RUN_ID_OUT" ]]; then echo "::error::ArchRails queued the run but did not return a run_id." exit 1 fi echo "run_id=$RUN_ID_OUT" >> "$GITHUB_OUTPUT" echo "ArchRails review queued as $RUN_ID_OUT." - name: Wait for ArchRails review if: steps.dispatch.outputs.run_id env: ARCHRAILS_TENANT_ID: ${{ secrets.ARCHRAILS_TENANT_ID }} ARCHRAILS_TOKEN: ${{ secrets.ARCHRAILS_TOKEN }} ARCHRAILS_URL: ${{ secrets.ARCHRAILS_WEBHOOK_URL }} RUN_ID_OUT: ${{ steps.dispatch.outputs.run_id }} timeout-minutes: 10 run: | POLL_URL="${ARCHRAILS_URL%/}/run/${RUN_ID_OUT}" for i in $(seq 1 60); do BODY=$(curl -sS --max-time 15 \ -H "X-ArchRails-Token: $ARCHRAILS_TOKEN" \ -H "X-ArchRails-Tenant-Id: $ARCHRAILS_TENANT_ID" \ "$POLL_URL") STATUS=$(echo "$BODY" | jq -r '.status // "unknown"') case "$STATUS" in completed) echo "ArchRails review completed."; exit 0 ;; failed) echo "::error::ArchRails review failed: $(echo "$BODY" | jq -r '.error // "unknown"')"; exit 1 ;; running|queued) : ;; *) echo "Unexpected status '$STATUS' — body: $BODY" ;; esac sleep 10 done echo "::error::Timed out waiting for ArchRails review ($RUN_ID_OUT)" exit 1
# .gitlab-ci.yml — append to your existing file archrails: stage: review image: alpine:latest only: [merge_requests] variables: ARCHRAILS_API: "https://ugq071csi5.execute-api.us-east-2.amazonaws.com" before_script: - apk add --no-cache curl jq script: - | RUN=$(curl -fsS -X POST "$ARCHRAILS_API/webhook/gitlab" \ -H "X-ArchRails-Token: $ARCHRAILS_TENANT_TOKEN" \ -H "X-ArchRails-Tenant-Id: $ARCHRAILS_TENANT_ID" \ -H "Content-Type: application/json" \ -d "{ \"object_kind\": \"merge_request\", \"project\": { \"id\": \"$CI_PROJECT_ID\", \"path_with_namespace\": \"$CI_PROJECT_PATH\" }, \"object_attributes\": { \"iid\": \"$CI_MERGE_REQUEST_IID\", \"action\": \"open\" } }") RUN_ID=$(echo "$RUN" | jq -r '.run_id') while :; do STATUS=$(curl -fsS "$ARCHRAILS_API/webhook/github/run/$RUN_ID" \ -H "X-ArchRails-Token: $ARCHRAILS_TENANT_TOKEN" \ -H "X-ArchRails-Tenant-Id: $ARCHRAILS_TENANT_ID" | jq -r '.status') case "$STATUS" in SUCCEEDED|FAILED|TIMED_OUT|ABORTED) break ;; *) sleep 10 ;; esac done [ "$STATUS" = "SUCCEEDED" ] || exit 1
Add to your repo as secrets
ARCHRAILS_TENANT_ID
Your tenant ID. Shown above — click Copy in the tenant panel.
ARCHRAILS_TOKEN
Signing secret. Click Reveal secret in the panel above — auto-hides after 30s.
ARCHRAILS_WEBHOOK_URL
The endpoint your CI POSTs to. Same value for every tenant on this environment — click Copy in the URL panel above.
ARCHRAILS_PARENT_TOKEN
Optional. Leave empty unless your tenant has multiple sub-organizations to bill separately.
3
Verify your setup
Read-only diagnostics — sign in first, then run the check.
Signed in
Your account is active and the dashboard authenticates you.
Not run
Admin access
You can manage repos and users on your tenant.
Not run
Repo connected
At least one repo has run the workflow and registered itself.
Not run
First PR review
ArchRails has commented on at least one pull request.
Not run