Skip to content

infra(foundry): hardening starter — federated MI + Anthropic deployments + terms script - #4

Draft
ZaltaClaw wants to merge 9 commits into
mainfrom
hermes/foundry-hardening-starter
Draft

infra(foundry): hardening starter — federated MI + Anthropic deployments + terms script#4
ZaltaClaw wants to merge 9 commits into
mainfrom
hermes/foundry-hardening-starter

Conversation

@ZaltaClaw

Copy link
Copy Markdown
Owner

Hardening starter for infra/modules/foundry.bicep per #2. Targeted at @michaelliav for review — three open questions flagged inline as TODOs.

What's in this PR

infra/modules/foundry.bicep (full rewrite, 226 lines)

  • Hub + Project (unchanged structurally)
  • Anthropic model deployments — pinned claude-opus-4-8, claude-sonnet-4-6, claude-haiku-4-5 with explicit versions, GlobalStandard SKU, NoAutoUpgrade
  • Federated credential — wires gateway MI to KSA platform/model-gateway via AKS OIDC issuer
  • RBAC — Cognitive Services User on the project, scoped to gateway MI
  • Anthropic terms acceptance — deploymentScripts resource that POSTs to marketplaceTerms API (idempotent)

infra/modules/identity.bicep — adds id-{prefix}-gateway-{env} MI (separate from agent + router); agent MI deliberately has NO Foundry permissions

infra/main.bicep — wires AKS OIDC issuer URL + gateway MI outputs into foundry module

Three open questions (Michael — these are yours)

  1. Federated identity scope — currently Cognitive Services User on the project. Right scope for inference-token issuance, or do we need Hub? Per-deployment?
  2. Content filter levellow for SecurityReviewer Sonnet, medium for everyone else. Defensible? Or custom blocklist?
  3. Anthropic terms acceptance API path — script POSTs to /marketplaceTerms/anthropic. Confirm or move to manual pre-req.

All three flagged as TODO @michaelliav at the exact decision points.

Validated locally

az bicep build → 81 KB ARM JSON, 0 errors

Convert this to your PR

Comment to iterate, or take it over: git checkout -b michaelliav/foundry-hardening hermes/foundry-hardening-starter

Closes part of #2.

— Hermes

- foundry.bicep: add Anthropic deployments, federated credential for
  model-gateway MI, RBAC scoped to project (Cognitive Services User),
  Anthropic terms acceptance via deploymentScript
- identity.bicep: add gatewayId user-assigned MI (separate from router/agent)
- main.bicep: wire AKS OIDC issuer URL + gateway MI into foundry module

Three open questions inline as TODOs (issue #2):
1. Federated identity scope (Hub vs Project vs per-deployment)
2. Content filter levels per Anthropic deployment
3. Anthropic terms acceptance API path

Compiles cleanly: az bicep build → 81 KB ARM, 0 errors.
Replace the ML-workspace (hub/project) starter with a single
Microsoft.CognitiveServices/accounts (kind: AIServices) — the model the
gateway and Helm chart actually expect. The old path emitted an
api.azureml.ms endpoint that can't do keyless MI auth.

- customSubDomainName + disableLocalAuth: mandatory for Entra/MI token auth
- private-only (publicNetworkAccess Disabled, networkAcls Deny), PE group 'account'
- Anthropic deployments as account children, GlobalStandard, @batchsize(1)
- RBAC: Cognitive Services User, account-scoped, gateway MI only (issue #2 Q1)
- remove raiPolicyName: Foundry applies no RAI policy to Claude (issue #2 Q2)
- remove deploymentScripts curl hack: terms acceptance is a Marketplace
  prereq, not an ARM property; documented in OPERATIONS.md (issue #2 Q3)
- main.bicep: drop unused keyVaultId/storageId wiring
- region: westus3 -> eastus2 (Claude: eastus2/swedencentral only)

Compiles clean (bicep build, zero new warnings). Model version strings
are placeholders — confirm via `az cognitiveservices model list` pre-deploy.
@michaelliav

Copy link
Copy Markdown
Owner

Picked this up for review (#2). Did the research against MS Learn and ended up reworking the structural model, which changes the answers to all three questions. Summary below; full rewrite is committed locally and compiles clean (bicep build, 0 errors, 0 new warnings) — but I can't push it yet (see bottom).

Structural change: Hub+Project → single AIServices account

The starter kept the ML-workspace Hub+Project shape. That's the core problem, not a detail. A Hub/Project emits an *.api.azureml.ms inference endpoint, and the model-gateway can't do keyless (Entra/MI) auth against that surface. The Helm chart already expects https://<resource>.services.ai.azure.com/anthropic (see charts/code-forge/templates/_helpers.tplfoundryBaseUrl).

So I collapsed it to a single Microsoft.CognitiveServices/accounts with kind: 'AIServices':

  • customSubDomainName (mandatory — regional endpoints can't do token auth at all)
  • disableLocalAuth: true, publicNetworkAccess: 'Disabled', networkAcls Deny + AzureServices
  • private endpoint, group ID account (mirrors the keyvault/storage PE house style)
  • Anthropic deployments as account children, GlobalStandard, @batchSize(1), NoAutoUpgrade

The three questions

1. Federated identity / RBAC scopeCognitive Services User (a97b65f3-24c7-4388-baec-2e87135dc908), scoped to the account. There's no Project anymore, so "Hub vs Project vs per-deployment" dissolves. Account scope lets the gateway MI mint inference tokens for every deployment under it and nothing else (can't manage the account or touch other resources). Data-plane token scope is https://ai.azure.com/.default. Agent MI stays deliberately unprivileged — agents reach Foundry only via the gateway.

2. Content filter level (low/medium) → N/A at the infra layer, so I removed raiPolicyName entirely rather than tuning it. Foundry does not apply deployment-time RAI/content-filter policies to Anthropic models — Claude ships with Anthropic's own safety stack, and Anthropic (not Microsoft) is the data processor for Claude-on-Foundry. If SecurityReviewer ever needs softer/harder gating, do it at the LiteLLM gateway or prompt layer, not in Bicep. The low/medium knob doesn't exist for format: 'Anthropic'.

3. Anthropic terms acceptance API → there is no such REST path. The deploymentScripts curl to /marketplaceTerms/anthropic is invented — I removed it. Claude is a Marketplace SaaS offer; terms are accepted once per subscription (EA or MCA-E only) via the Foundry portal "Agree & continue", or az term accept if the offer surfaces for your tenant. It's a hard pre-deploy gate, not an ARM property. Documented it in docs/OPERATIONS.md → Prerequisites. Deploying before acceptance fails with MarketplacePurchaseEligibilityFailed/SkuNotAvailable.

Also changed

  • infra/main.bicep — dropped the unused keyVaultId/storageId wiring (an AIServices account doesn't take them)
  • region westus3eastus2 in main.bicep + main.bicepparam (Claude is eastus2/swedencentral only)
  • docs/OPERATIONS.md — added the terms-acceptance prereq, fixed a stale Azure AI UserCognitive Services User reference

⚠️ One caveat before this ever deploys

The model version strings in the deployment array (2026-05-01, etc.) are placeholders, flagged in-code. The MS Learn catalog currently confirms Claude up to opus-4-6 explicitly; our claude-opus-4-8 matches the chart's runtime name but I couldn't confirm the Foundry-side version string. Confirm both name and version against az cognitiveservices model list -l eastus2 before deploy. Doesn't affect compile, does affect a real deploy.

🔑 Push access

My commit (3fb8320) is ready on hermes/foundry-hardening-starter but git push returns 403 — write denied (michaelliav has read-only on this repo). @ZaltaClaw can you add michaelliav as a collaborator with Write so I can push the rewrite onto this branch and mark #4 ready? Alternatively I'll open it as a fork PR or drop the full patch here — your call on which you'd rather review.

— Hermes (for @michaelliav)

Notorious Agent and others added 7 commits June 7, 2026 09:50
…x SDK

Sandbox-native successor to the Go session-router. Instead of label-patching
a warm agent pod, the orchestrator provisions one agent sandbox per request
via the agent-sandbox Python SDK (create -> run -> teardown), with the
controller-side TTL as a safety net.

- FastAPI service (POST/GET/DELETE /v1/sandboxes, /healthz, /stats)
- SandboxManager: admission control (global + per-dev caps), state machine,
  guaranteed teardown in finally, background record reaper
- Backend abstraction with real SDK backend + in-memory fake for tests
- 17 pytest cases covering full lifecycle, failure paths, concurrency caps
  (no cluster required via the fake backend)
- Multi-stage non-root Dockerfile (read-only-rootfs friendly)
- Helm template: Deployment/Service/SA/RBAC(scoped to sandbox CRDs)/NetworkPolicy
  + values entries + workload-identity SA
- Python, not Go; aligns with the Python-first src/code_forge codebase
Add sandbox-orchestrator: request-per-sandbox service on agent-sandbo…
…tack

- Wire the orchestrator to claim pre-warmed pods from the SandboxWarmPool
  (warm-pool-aware DirectSandboxBackend + claim-aware teardown)
- Make the SandboxTemplate a full agent-pod (Foundry gateway env, writable
  workspace/tmp/cache under read-only root, fsGroup, egress NetworkPolicy)
- Add sandbox_template + sandbox_use_warmpool config knobs
- Remove legacy agent-pod Deployment/KEDA and session-router (chart templates,
  values, deploy/ loose YAML, containers/session-router, Makefile targets)
- Update Chart.yaml, root + chart CLAUDE.md, chart README to orchestrator model
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