# Citadel Hub Deploy

> >

- **Type:** Skill
- **Install:** `agentstack add skill-aiappsgbb-awesome-gbb-citadel-hub-deploy`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [aiappsgbb](https://agentstack.voostack.com/s/aiappsgbb)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [aiappsgbb](https://github.com/aiappsgbb)
- **Source:** https://github.com/aiappsgbb/awesome-gbb/tree/main/skills/citadel-hub-deploy

## Install

```sh
agentstack add skill-aiappsgbb-awesome-gbb-citadel-hub-deploy
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Citadel Hub Deploy — Layer 1 Governance Hub

> **Status:** Public Preview wrapper around the
> [Azure-Samples / ai-hub-gateway-solution-accelerator] branch
> `citadel-v1` (MIT). The accelerator is the canonical source; this skill
> never forks or vendors its Bicep — it pins to a known-good commit, ships
> 3 curated AZD env profiles, and wires the deployment into the
> awesome-gbb conventions (tenant isolation, MCAPS pilot tagging,
> spoke-aware networking).
>
> **Pinned upstream:** see [`references/upstream-pin.md`](references/upstream-pin.md).
> **Live-validated:** ✅ Resource & shape audit + APIM smoke calls against
> a real `rg-citadel-hub-01` in Sweden Central (May 2026, see
> [`references/live-audit-notes.md`](references/live-audit-notes.md)).

[Azure-Samples / ai-hub-gateway-solution-accelerator]: https://github.com/Azure-Samples/ai-hub-gateway-solution-accelerator/tree/citadel-v1

---

## 1. Why this matters

Most "Foundry pilot" decks stop at "deploy a Foundry account, run an
agent". That works for **one** team and **one** use case. The moment a
second team needs the same model, you hit five hard problems:

1. **Cost attribution.** Whose subscription pays for which call?
2. **Quota fairness.** One team's batch run starves another's chat.
3. **Policy uniformity.** PII redaction, content safety, model
   allow-lists — defined once, enforced everywhere.
4. **Auditability.** Who called what model, when, with which prompt?
5. **Backend abstraction.** Switching a model from PTU → PayAsYouGo
   shouldn't require every spoke to re-deploy.

The Citadel Governance Hub is the AI Apps GBB reference design that
solves all five at the platform level. APIM in front of every model
backend, Cosmos for usage telemetry, Logic App for billing aggregation,
Event Hub for streaming events, and a **per-team Access Contract**
(APIM Product) that gives each spoke its own subscription key, scope,
and policy bundle.

Without this you end up with N spoke projects each negotiating their
own Foundry quota and their own PII policy — and your CISO finds out
on day 91.

```
   ┌──────────────────────────────────────────────────────────┐
   │   Layer 1 — Governance Hub (this skill deploys it)        │
   │                                                           │
   │  ┌────────────┐    ┌──────────┐    ┌──────────────────┐  │
   │  │ APIM v2    │ ←→ │ Cosmos   │ ←→ │ Foundry (×N)     │  │
   │  │ AI Gateway │    │ Usage DB │    │ Multi-region     │  │
   │  └────────────┘    └──────────┘    └──────────────────┘  │
   │       ↑                  ↑                                │
   │       │                  │                                │
   │  ┌────┴───┐   ┌──────────┴────┐                          │
   │  │ Event  │   │ Logic App     │                          │
   │  │ Hub    │   │ Usage         │                          │
   │  └────────┘   │ Aggregation   │                          │
   │               └───────────────┘                          │
   └──────────────────────────────────────────────────────────┘
            ↑                          ↑                ↑
            │ per-team access contract │                │
            │ (APIM Product + sub key) │                │
   ┌────────┴──────┐         ┌─────────┴─────┐  ┌──────┴────┐
   │ Spoke project │         │ Spoke project │  │ Foundry   │
   │  team A       │         │   team B      │  │ Workspace │
   │ (use citadel- │         │ (use citadel- │  │ (use      │
   │  spoke-onbrd) │         │  spoke-onbrd) │  │  spoke…)  │
   └───────────────┘         └───────────────┘  └───────────┘
```

---

## 2. What `citadel-hub-deploy` does (and what it doesn't)

### Does

- Captures the upstream `azd` template at a **pinned SHA** (see
  `references/upstream-pin.md`); never silently rolls forward.
- Wraps the deployment in a **tenant-isolated, assertion-gated** workflow
  (see `azure-tenant-isolation`).
- Ships **3 curated AZD environment profiles** in `references/profiles/`:
  - `pilot-quickstart.env` — Developer SKU, cheapest demo
  - `enterprise-baseline.env` — Standard v2, production-grade, BYO Log Analytics
  - `vnet-isolated-spoke-aware.env` — BYO VNet + DNS, pre-wired for
    `foundry-vnet-deploy` spokes
- Documents the **8 upstream validation notebooks** and the
  recommended execution order.
- Documents the **post-deploy hand-off** to `citadel-spoke-onboarding`
  (per-team access contracts) and `foundry-agt` (in-process governance).

### Doesn't

- **Doesn't fork or vendor** the upstream Bicep. The 55 KB `main.bicep`
  + 3-tier module tree lives at the source repo and is fetched fresh by
  `azd init --template ... --branch citadel-v1` at the pinned SHA.
- **Doesn't onboard spokes.** That's `citadel-spoke-onboarding` — a
  single `az deployment sub create` against
  `bicep/infra/citadel-access-contracts/main.bicep`.
- **Doesn't add in-process governance.** That's `foundry-agt` — runs
  inside the agent process, before/after every tool call.
- **Doesn't manage post-deploy upgrades.** The
  `bicep/infra/apim-gateway-upgrade/` flow (StandardV2 → newer SKUs,
  policy fragment refresh) is upstream-owned.
- **Doesn't onboard LLM backends.** That's the
  `validation/llm-backend-onboarding-runner.ipynb` notebook upstream.

| Want to do this | Use this skill instead |
|---|---|
| Wire your agent project into a deployed hub | `citadel-spoke-onboarding` |
| Add per-tool-call governance inside MAF/Foundry agents | `foundry-agt` |
| Deploy a single-resource Foundry inside a private VNet (no APIM) | `foundry-vnet-deploy` |
| Switch tenants, isolate az/azd config dirs | `azure-tenant-isolation` |
| Apply MCAPS pilot tagging conventions (`SecurityControl: Ignore`, `AZURE_TAGS`) | `azd-patterns` |
| Get App Insights traces from the deployed hub into your spoke | `foundry-observability` |

---

## 3. When NOT to deploy a Citadel Hub

The hub is opinionated: APIM Standard v2 + Foundry control plane + Cosmos
+ Event Hub + Logic App + Redis + 13 Private DNS Zones + 4 NSGs + private
endpoints. That's **~$800-2,500/month baseline cost** in pilot config
(see `guides/citadel-sizing-guide.md` upstream) and 30-45 minutes of
APIM provisioning before the first request can flow.

Don't deploy a hub when:

- **Single-team pilot, single-use-case PoC.** You don't need APIM
  arbitration if there's only one consumer. Use `microsoft-foundry`
  + a direct AOAI/AI Services connection.
- **Dev-time Foundry exploration.** Engineers spinning up sandbox
  Foundry workspaces shouldn't pay for a shared APIM. Use
  `foundry-vnet-deploy` for private networking instead.
- **Budget below $1k/mo.** Even the `pilot-quickstart` profile (Developer
  SKU APIM, no SLA) lands around $200-400/mo with realistic usage, before
  Foundry model burn. If you can't justify that for governance, you
  probably shouldn't be running production agents on any platform.
- **Pure offline / batch workloads.** No runtime to govern → no gateway
  needed. `foundry-evals` + direct backend calls suffice.
- **You're inside a Landing Zone with a pre-existing hub.** Reuse it via
  `citadel-spoke-onboarding`. Don't deploy a parallel hub.

---

## 4. Stakeholder TL;DR

- **Engineer:** "It's `azd init --template Azure-Samples/ai-hub-gateway-solution-accelerator -e  --branch citadel-v1` then `azd up`. Profile picks the SKU/network shape. 30-45 min wall clock. Don't forget tenant isolation."
- **Architect:** "Layer 1 of the 4-layer Citadel platform. APIM is the gateway plane; spokes connect via per-team access contracts (Bicep-driven). Pairs with `foundry-agt` for in-process defence in depth. Telemetry sinks: 3 App Insights workspaces + 1 Log Analytics + Cosmos `usage-db`."
- **Compliance:** "PII redaction (Azure AI Language) + Content Safety + JWT-enforceable RBAC + per-team subscription keys with audit trail in Cosmos + private endpoints on every backend service. Documented in `guides/pii-masking-apim.md` and `guides/jwt-client-identity-permissions.md` upstream."
- **Seller:** "One repeatable Bicep deployment that checks the platform-team's first 5 boxes (cost attribution, quota fairness, policy uniformity, audit, backend abstraction) plus the unified-ai-api wildcard route lets you onboard AOAI, Foundry, and Gemini behind one developer-friendly endpoint. Demo runs against the deployed hub via `validation/citadel-universal-llm-api-all-models-tests.ipynb`."

---

## 5. Quickstart

> **TENANT ISOLATION FIRST.** Per `azure-tenant-isolation`, set both
> `AZURE_CONFIG_DIR` and `AZD_CONFIG_DIR` to per-tenant directories
> **before** any `az` / `azd` command. Then run the two-layer assertion
> (`az account show --query tenantId / name`) before `azd up`. Without
> these, you risk deploying a $1k+/mo hub into the wrong subscription.

### Path A — Pilot Quickstart (cheapest demo)

Goal: smallest hub that exercises every API surface. Developer SKU APIM,
public access, all feature flags on, greenfield VNet + Log Analytics.

```bash
# 0. Set the path to your awesome-gbb checkout (or `~/.copilot/skills`
#    user-scope mirror) so the .env profiles below resolve.
SKILL_DIR="$HOME/.copilot/skills/citadel-hub-deploy"  # or your repo path

# 1. Tenant isolation (per azure-tenant-isolation skill)
export AZURE_CONFIG_DIR="$HOME/.azure-tenants/"
export AZD_CONFIG_DIR="$HOME/.azd-tenants/"
az login --tenant "$TENANT_ID"
azd auth login --tenant-id "$TENANT_ID"
az account set --subscription "$DEFAULT_SUB"
[ "$(az account show --query name -o tsv)" = "$DEFAULT_SUB" ] || exit 1

# 2. Init template at pinned branch
mkdir my-citadel-hub && cd my-citadel-hub
azd init --template Azure-Samples/ai-hub-gateway-solution-accelerator \
         -e citadel-pilot-01 \
         --branch citadel-v1

# 3. Apply the pilot-quickstart profile (env-var bundle from the skill)
while IFS='=' read -r k v; do
  [[ -z "$k" || "$k" == \#* ]] && continue
  azd env set "$k" "$v"
done "

# Apply the enterprise-baseline profile
while IFS='=' read -r k v; do
  [[ -z "$k" || "$k" == \#* ]] && continue
  azd env set "$k" "$v"
done /resourceGroups/rg-dns-prod/providers/Microsoft.Network/privateDnsZones/privatelink.openai.azure.com"
# … repeat EXISTING_DNS_ZONE_* for the other 12 zones — see
# `$SKILL_DIR/references/profiles/vnet-isolated-spoke-aware.env` for the
# full list of EXISTING_DNS_ZONE_* env vars.

# Apply the vnet-isolated-spoke-aware profile
while IFS='=' read -r k v; do
  [[ -z "$k" || "$k" == \#* ]] && continue
  azd env set "$k" "$v"
done "
$env:AZD_CONFIG_DIR   = "$env:USERPROFILE\.azd-tenants\"
az login --tenant $tenantId
azd auth login --tenant-id $tenantId
az account set --subscription $defaultSub
if ((az account show --query name -o tsv) -ne $defaultSub) { exit 1 }

# Init + apply profile
mkdir my-citadel-hub; cd my-citadel-hub
azd init --template Azure-Samples/ai-hub-gateway-solution-accelerator `
         -e citadel-pilot-01 --branch citadel-v1
Get-Content "$skillDir\references\profiles\pilot-quickstart.env" |
  Where-Object { $_ -and -not $_.StartsWith('#') } |
  ForEach-Object { $k,$v = $_.Split('=',2); azd env set $k $v }
azd up
```

---

## 6. Pre-flight checklist

See [`references/customer-checklist.md`](references/customer-checklist.md)
for the full pre-flight (tenant verified, providers registered, quota
requested, RBAC, networking decision, DNS ownership). The TL;DR:

- [ ] Tenant + subscription confirmed via two-layer assertion
- [ ] Resource providers registered: `Microsoft.ApiManagement`,
      `Microsoft.CognitiveServices`, `Microsoft.DocumentDB`,
      `Microsoft.EventHub`, `Microsoft.Insights`, `Microsoft.Logic`
- [ ] Quota: APIM Standard v2 (1+ unit), Foundry GlobalStandard tokens
      for each model in your `aiFoundryModelsConfig`, Cosmos RU/s
- [ ] RBAC: deployer is **Owner** or has **Contributor** + **User Access
      Administrator** on the target sub (role assignments are part of
      the deploy)
- [ ] Networking decision made (greenfield vs BYO VNet vs BYO DNS)
- [ ] If BYO Log Analytics: workspace ID + cross-sub RBAC granted
- [ ] If `entraAuth=true` later: app registration ready (use
      `bicep/infra/entra-id-setup/setup.ps1` upstream — outside v1.0.0
      of this skill)

---

## 7. Post-deploy verification

The upstream ships **8 validation notebooks** under `validation/`. The
recommended baseline (run all 4 on every new deployment):

| # | Notebook | What it validates | ⭐ Baseline? |
|---|----------|-------------------|------------|
| 1 | `llm-backend-onboarding-runner.ipynb` | Register AI backends + deploy routing logic into APIM | ⭐ |
| 2 | `citadel-universal-llm-api-all-models-tests.ipynb` | Validate every gateway-configured model through `/models` | ⭐ |
| 3 | `citadel-access-contracts-tests.ipynb` | Per-team access contracts with KV + Foundry connection | ⭐ |
| 4 | `citadel-agent-frameworks-tests.ipynb` | MAF + Foundry SDK + LangChain consumption | ⭐ |
| 5 | `citadel-model-aliases-tests.ipynb` | `resolve-model-alias` policy fragment (priority + weighted) | scenario |
| 6 | `citadel-pii-processing-tests.ipynb` | PII anonymize/deanonymize/block | scenario |
| 7 | `citadel-unified-ai-api-tests.ipynb` | Multi-provider routing through unified-ai wildcard API | scenario |
| 8 | `citadel-jwt-authentication-tests.ipynb` | JWT enforcement + RBAC across endpoints | scenario |

Each notebook auto-loads from your `azd` env via the
`init_from_azd = True` toggle in cell 0:

```python
init_from_azd = True   # auto-pulls AZURE_RESOURCE_GROUP, AZURE_LOCATION, …
                       # from `azd env get-values` of the active env
```

Manually-set values (anything not equal to the `"REPLACE"` sentinel) win
over azd values. See `validation/README.md` upstream for the per-notebook
azd env-var map.

### Quick smoke (no Jupyter)

If you don't have a Python venv handy, this curl (or `Invoke-RestMethod`)
proves the gateway works:

```bash
# Get APIM gateway URL
GW=$(az apim show -g  -n  --query gatewayUrl -o tsv)

# Get the master subscription key (DEMO ONLY — don't use master in prod;
# create a per-team Access Contract via citadel-spoke-onboarding instead)
KEY=$(az rest --method post \
  --url "https://management.azure.com/subscriptions/$(az account show --query id -o tsv)/resourceGroups//providers/Microsoft.ApiManagement/service//subscriptions/master/listSecrets?api-version=2022-08-01" \
  --query primaryKey -o tsv)

# Discover models
curl -s "$GW/models/models" -H "api-key: $KEY" | jq '.value[].name'

# Send one chat completion (NOTE: api-key header, NOT Ocp-Apim-Subscription-Key)
curl -s -X POST "$GW/openai/deployments/gpt-5.4-mini/chat/completions?api-version=2024-12-01-preview" \
  -H "api-key: $KEY" -H "Content-Type: application/json" \
  -d '{"messages":[{"role":"user","content":"ping"}],"max_completion_tokens":10}'
```

Live-tested round-trip latency from this skill's audit run (Sweden
Central, gpt-5.4-mini, warm): **~1 sec end-to-end** through APIM.
Discovery `/models` call: **~250 ms warm**. See
`references/live-audit-notes.md` for the full numbers + gotchas.

---

## 8. Hand-off to `citadel-spoke-onboarding`

After the hub is deployed, every spoke project needs:

1. An **APIM Product** (the access contract, e.g.,
   `LLM-Healthcare-PatientAssistant-DEV`) — created via
   `bicep/infra/citadel-access-contracts/main.bicep`.
2. An **APIM Subscription** scoped to that product (auto-created by the
   contract Bicep, named `-SUB-01`).
3. **Optional Key Vault secrets** (endpoint + API key stored in central
   KV — recommended for managed-identity-only spokes).
4. **Optional Foundry connection** (the contract Bicep can create an
   APIM connection in a target Foundry project, so agents can
   `client.connections.get(...)` their gateway).

Naming: `{serviceCode}-{businessUnit}-{useCase}-{environment}`
(e.g., `LLM-Healthcare-PatientAssistant-DEV`). One product per spoke,
one subscription per product, optional KV secret per subscription.

**Use the `citadel-spoke-onboarding` skill** for the per-spoke wiring;
it documents the contract `.bicepparam`, the opti

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [aiappsgbb](https://github.com/aiappsgbb)
- **Source:** [aiappsgbb/awesome-gbb](https://github.com/aiappsgbb/awesome-gbb)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** yes
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-aiappsgbb-awesome-gbb-citadel-hub-deploy
- Seller: https://agentstack.voostack.com/s/aiappsgbb
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
