Skip to content

feat: armorcopilot-ms Express webhook service skeleton#2

Closed
Harihara04sudhan wants to merge 1 commit into
mainfrom
feat/armorcopilot-ms-skeleton
Closed

feat: armorcopilot-ms Express webhook service skeleton#2
Harihara04sudhan wants to merge 1 commit into
mainfrom
feat/armorcopilot-ms-skeleton

Conversation

@Harihara04sudhan

Copy link
Copy Markdown
Contributor

Summary

Scaffolds `@armoriq/armorcopilot-ms`, the standalone Cloud Run service that fronts Microsoft Copilot Studio's external security webhook (`/analyze-tool-execution`). This is the ArmorCopilot product itself — mirrors how ArmorClaude and ArmorCodex live in their own repos and call into the ArmorIQ backend via `@armoriq/sdk`.

Architecture

```
Microsoft Copilot Studio
→ HTTPS POST /analyze-tool-execution/:tenantId
@armoriq/armorcopilot-ms (this service, Cloud Run)
→ verifyCopilotStudioSignature() from @armoriq/sdk (HMAC verify, no backend call)
→ translateCopilotStudioPayload() from @armoriq/sdk (MS → ArmorIQ tool-call shape)
→ axios POST /iap/sdk/enforce on ArmorIQ backend (single backend call, p95 target <400ms)
ArmorIQ backend (conmap-auto, unchanged)
← {allowed, action, reason}
← {action: allow|block} back to MS
```

Why standalone (not in conmap-auto)

Earlier PoC hosted this directly in `conmap-auto`. Reverted because:

  • ArmorClaude and ArmorCodex are separate products with their own deploys — ArmorCopilot should mirror that pattern.
  • Webhook code should use `@armoriq/sdk` rather than calling backend internals directly. Same enforcement contract across all 3 products.
  • conmap-auto changes from the earlier PoC are reverted in armoriq/conmap-auto#TBD (only the husky/lint-staged fix is kept).

Endpoints

```
GET /health liveness probe
POST /analyze-tool-execution/:tenantId MS webhook receiver (HMAC-signed)
```

Files

```
packages/armorcopilot-ms/
├── package.json Express + @armoriq/sdk@^0.4.0 + axios
├── tsconfig.json ES2022, CommonJS, strict
├── Dockerfile Two-stage Node 20 alpine for Cloud Run
├── .env.example ARMORIQ_API_KEY + ARMORIQ_BACKEND_ENDPOINT + COPILOT_STUDIO_DEFAULT_SECRET
├── README.md Local dev + Cloud Run deploy walkthrough
└── src/
├── index.ts Express bootstrap, route handlers
└── enforce.ts Thin axios wrapper → /iap/sdk/enforce
```

Also updates `SESSION_CONTEXT.md` to reflect the final architecture after the in-conmap-auto pivot.

Test plan

  • Publish `@armoriq/sdk@0.4.0` (or `@armoriq/sdk-dev@0.4.0`) — see armoriq/armoriq-sdk-customer-ts#TBD
  • `npm install && npm run build` clean
  • Local smoke: `npm run dev` + signed POST → `{"action":"allow"}`
  • Deploy to Cloud Run staging
  • Register the staging URL in a test Copilot Studio tenant + observe a real MS-sent webhook

Known follow-ups

  • `src/enforce.ts` wraps a manual axios POST to `/iap/sdk/enforce` with a synthetic intent token. Proper fix: add `client.enforceOnce(tool, args)` to the SDK (no plan required) — see armoriq/armoriq-sdk-customer-ts#TBD.
  • Multi-tenant secrets via env vars for v1 (`COPILOT_STUDIO_TENANT__SECRET`). Promote to Google Secret Manager when there's a third customer.

Refs #1

🤖 Generated with Claude Code

… Copilot Studio

Scaffolds @armoriq/armorcopilot-ms, the standalone Cloud Run service
that fronts Microsoft Copilot Studio's external security webhook
(/analyze-tool-execution). Mirrors the ArmorClaude/ArmorCodex pattern:
own repo, own deploy, talks to ArmorIQ backend through @armoriq/sdk.

Surface:
  GET  /health                                  liveness probe
  POST /analyze-tool-execution/:tenantId        MS webhook receiver

Flow:
  1. verifyCopilotStudioSignature() from @armoriq/sdk verifies HMAC
     against per-tenant secret (env-var resolution today: per-tenant
     COPILOT_STUDIO_TENANT_<id>_SECRET or COPILOT_STUDIO_DEFAULT_SECRET)
  2. translateCopilotStudioPayload() from @armoriq/sdk reshapes the
     MS payload into ArmorIQ's tool-call shape
  3. enforceToolViaBackend() (src/enforce.ts) posts to /iap/sdk/enforce
     with the customer's API key. Single backend call, p95 target <400ms
  4. toCopilotStudioDecision() returns {action: allow|block} to MS

Targets @armoriq/sdk@^0.4.0 (must be published before npm install resolves).

Files:
  package.json + tsconfig.json + Dockerfile (Node 20 alpine, two-stage)
  .env.example with ARMORIQ_API_KEY + ARMORIQ_BACKEND_ENDPOINT +
    COPILOT_STUDIO_DEFAULT_SECRET
  README.md with local-dev + Cloud Run deploy steps
  src/index.ts (Express bootstrap)
  src/enforce.ts (thin axios wrapper, TODO: replace once SDK exposes
    client.enforceOnce() for plan-less enforce)

Also updates SESSION_CONTEXT.md to reflect the final architecture after
reverting the earlier in-conmap-auto approach.

Refs #1

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 22, 2026 08:12

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Scaffolds a new standalone Node/Express service (@armoriq/armorcopilot-ms) intended to receive Microsoft Copilot Studio’s /analyze-tool-execution/:tenantId webhook, verify HMAC signatures via @armoriq/sdk, translate the payload, and call ArmorIQ backend enforcement (/iap/sdk/enforce) before returning an allow/block decision.

Changes:

  • Adds packages/armorcopilot-ms with Express server (/health, /analyze-tool-execution/:tenantId) and an axios-based enforcement bridge.
  • Adds containerization + local dev artifacts (Dockerfile, .env.example, README, TS config).
  • Adds a SESSION_CONTEXT.md architecture/session reference doc.

Reviewed changes

Copilot reviewed 8 out of 9 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
SESSION_CONTEXT.md Adds a session/architecture reference document for ArmorCopilot.
packages/armorcopilot-ms/tsconfig.json TypeScript compiler configuration for the new service.
packages/armorcopilot-ms/src/index.ts Express bootstrap + webhook handler with HMAC verification and decision response.
packages/armorcopilot-ms/src/enforce.ts Axios wrapper that forwards tool calls to ArmorIQ backend enforcement.
packages/armorcopilot-ms/README.md Local dev + Cloud Run deploy instructions and architecture notes.
packages/armorcopilot-ms/package.json Declares dependencies/scripts for the new service.
packages/armorcopilot-ms/package-lock.json Lockfile for the new service’s dependencies.
packages/armorcopilot-ms/Dockerfile Two-stage build container for Cloud Run deployment.
packages/armorcopilot-ms/.env.example Environment variable template for local/dev deployments.
Files not reviewed (1)
  • packages/armorcopilot-ms/package-lock.json: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread SESSION_CONTEXT.md
Comment on lines +106 to +109
| `armoriq/armorCopilot` (this repo) — `feat/armorcopilot-ms-skeleton` | `packages/armorcopilot-ms/` Express service (5 source files + Dockerfile + README + .env.example) |
| `armoriq/armoriq-sdk-customer-ts` — `feat/microsoft-copilot-integration` (off `dev`) | New `src/integrations/microsoft_copilot.ts` (HMAC verify + payload translator + decision mapper) + index exports + version bump 0.3.3 → 0.4.0 |
| `armoriq/conmap-auto` — `feat/copilot-studio-webhook-poc` | Reset to ONLY commit `361d90b` (lint-staged setup for husky — useful repo improvement). All previous webhook/tenant/migration code reverted. Staging DB rolled back (`copilot_studio_tenants` table dropped). |

Comment thread SESSION_CONTEXT.md
Comment on lines +11 to +15
| Product | Harness | Repo | Status |
|---|---|---|---|
| **ArmorClaude** | Anthropic Claude Code | `/Users/hariharasudhan/Armoriq/armorClaude` | Live in marketplace |
| **ArmorCodex** | OpenAI Codex | `/Users/hariharasudhan/Armoriq/armorCodex` | Live + listed on `hashgraph-online/awesome-codex-plugins` |
| **ArmorCopilot** (NEW) | Microsoft Copilot Studio (later: GitHub Copilot CLI) | `/Users/hariharasudhan/Armoriq/armorCopilot` | PoC stub bootstrapped 2026-05-21 |
} from '@armoriq/sdk';
import { enforceToolViaBackend } from './enforce';

const PORT = Number(process.env.PORT ?? 8080);
Comment on lines +48 to +62
const resp = await axios.post(`${ea.backend}/iap/sdk/enforce`, payload, {
headers: {
'X-API-Key': ea.apiKey,
'Content-Type': 'application/json',
},
timeout: 800,
validateStatus: (s) => s >= 200 && s < 500,
});

const data = (resp.data ?? {}) as Record<string, unknown>;
const allowed = data.allowed !== false;
const action =
(data.enforcementAction as 'allow' | 'block' | 'hold' | undefined) ??
(data.action as 'allow' | 'block' | 'hold' | undefined) ??
(allowed ? 'allow' : 'block');
Comment on lines +7 to +25
"": {
"name": "@armoriq/armorcopilot-ms",
"version": "0.1.0",
"dependencies": {
"@armoriq/sdk": "file:../../../armoriq-sdk-customer-ts",
"axios": "^1.7.0",
"express": "^4.21.0"
},
"devDependencies": {
"@types/express": "^4.17.21",
"@types/node": "^20.0.0",
"tsx": "^4.19.0",
"typescript": "^5.6.0"
}
},
"../../../armoriq-sdk-customer-ts": {
"name": "@armoriq/sdk",
"version": "0.3.3",
"license": "MIT",
Comment on lines +4 to +7
RUN npm install --omit=dev --no-package-lock
COPY tsconfig.json ./
COPY src ./src
RUN npm install --no-save typescript@5.6.0
Comment on lines +3 to +14
COPY package.json ./
RUN npm install --omit=dev --no-package-lock
COPY tsconfig.json ./
COPY src ./src
RUN npm install --no-save typescript@5.6.0
RUN npx tsc -p tsconfig.json

FROM node:20-alpine
WORKDIR /app
ENV NODE_ENV=production
COPY package.json ./
RUN npm install --omit=dev --no-package-lock
@Harihara04sudhan

Copy link
Copy Markdown
Contributor Author

Parking this PR — pivoting to GitHub Copilot CLI first per #1 architecture rethink. Branch feat/armorcopilot-ms-skeleton is preserved on remote. The Microsoft Copilot Studio integration loses our intent-capture wedge (Microsoft only calls us AFTER the LLM picks a tool; we can't sit pre-action). GitHub Copilot CLI runs locally + supports preToolUse/postToolUse hooks — same model as ArmorClaude/ArmorCodex, full intent capture preserved.

Revisit this PR if/when we want a separate ArmorCopilot-MS product as Phase 2 (governance-only SKU). Until then, the work stays on the branch and stays unmerged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants