NETWORG internal — July 2025
Each decision includes rationale and alternatives considered.
- Microsoft ecosystem alignment (Azure, GitHub, VS Code)
- Copilot SDK provides programmatic API for future integration
- GitHub CLI + Azure CLI ecosystem synergy
- Enterprise licensing via Copilot for Business seats we already have
- MCP server support for extensibility
- Simplest possible: one command to deploy, one command to delete
- Cheapest: ~$0.03/hr per container, no cluster overhead
- DNS included: automatic FQDN per container
- Sufficient: our containers don't need scaling, service mesh, or ingress
- Container Apps used for the API only (proxy + API)
- Microsoft's own reverse proxy — built by the ASP.NET team, used in Azure App Service and Entra ID
- API + proxy in one app: REST controllers + YARP reverse proxy in one ASP.NET Core deployment
- Container Apps Easy Auth: turnkey Entra ID authentication for container access
- WebSocket: full support via Kestrel — critical for code-server and ttyd
- Dynamic routing: programmatically update routes in-process as containers spawn/die
- C# authorization: per-container ownership checks in middleware (no separate forward_auth)
- Cost: ~$5-10/mo on Consumption plan
- TLS: Cloudflare handles TLS at the edge (free wildcard cert) — Container Apps origin uses HTTP or Cloudflare Origin CA
- Portal separate: React SPA served by Azure Static Web App (free CDN) — Container App handles only API + proxy
- Alternatives eliminated: App Gateway ($20+), Front Door ($35+), APIM (no WebSocket on Consumption), Caddy/Traefik (not Microsoft-native)
See proxy-investigation.md for the full 9-option comparison.
- Phone access: SSH on iPhone is painful; web terminal works perfectly
- No client needed: just a browser URL
- VS Code extensions: full extension marketplace via code-server
- Zero setup: no SSH keys, no client configuration
- VS Code Remote SSH: power users connect local VS Code to the container — full extension support, local keybindings, native performance
- SCP / file transfer: easily move files in and out of the sandbox
- Port forwarding: forward container ports to localhost for debugging web apps
- Standard tooling: integrates with existing SSH workflows,
rsync,gitover SSH - Security: Entra ID SSH certificate authentication — Microsoft-managed CA, short-lived certificates, Conditional Access, centralized revocation. Fallback: custom CA in Azure Key Vault (HSM-backed). Both options: no persistent keys, identity-bound, 24hr expiry.
See entra-ssh-investigation.md for the ACI feasibility study.
- ACI exposes limited ports externally
- User's ISP/DPI blocks HTTP on non-standard ports
- Single port 80 for all services simplifies firewall rules
- Path-based routing:
/= VS Code,/terminal/= web terminal - WebSocket support for both services
- Principle of least privilege: AI only sees explicitly selected sites, not user's full SharePoint access
- Per-site granularity:
POST /sites/{id}/permissionsgrants read access to individual sites - Auto-cleanup: permissions revoked on container destroy
- Zero extra licenses: Graph API included in existing M365 licenses
- Per-container MI (to validate): if the Site Permissions API accepts system-assigned MI
appId, each container gets its own SharePoint identity — strongest isolation + automatic cleanup on destroy - Fallback: shared "AgentBox SharePoint Reader" app registration if MI doesn't work with Site Permissions API
- Alternative rejected: delegated
Sites.Read.Allgives AI the user's full SharePoint access
See sharepoint-investigation.md for the full investigation.
- Decoupled permissions: AI's Dataverse access defined by admin-selected security role, not user's own role
- Per-environment control: user selects environments + role at spawn time
- Zero licenses: application users in Dataverse are unlicensed
- Audit trail: each container has unique identity in Dataverse audit logs
- Auto-cleanup: managed identity auto-deleted when container is destroyed
- Alternative rejected: delegated OAuth (
user_impersonation) gives AI the user's full Dataverse permissions
- Microsoft/GitHub experimental approval: NETWORG has explicit approval to use a shared account for Copilot CLI sessions
- Premium models for everyone: all users get premium Copilot models regardless of personal subscription status
- Credential isolation:
COPILOT_GITHUB_TOKEN(shared) is independent fromGH_TOKEN(user's own) — git operations always use the developer's real identity - No seat management overhead: no JIT provisioning API calls, no per-month billing surprises
- Cost-effective: single Copilot Business seat + premium request billing vs. $19/user/month per seat
- Personal subscriptions preserved: users with their own Pro+/Business seats use their own token, avoiding shared account quota consumption
- Alternatives eliminated: per-user seat assignment ($19/mo × N), JIT provisioning (billing complexity), Copilot Free tier (no premium models, 2K completions limit)
- Free tier: custom domain, SSL, global CDN, built-in Entra ID auth — $0/month
- Separation of concerns: React SPA deployed independently from the API/proxy Container App
- Global CDN: static assets served from edge nodes — faster load times worldwide, great for phone access
- Built-in auth: SWA Entra ID integration (
/.auth/login/aad) — no custom auth code needed for the dashboard - Independent deployments: portal changes deploy in ~30 seconds (static files), no API restart
- Mobile-first: excellent Core Web Vitals from CDN-served static files
- Alternative considered: React bundled inside ASP.NET Core
wwwroot/(simpler single deployment, but no CDN, no independent scaling, mixes concerns)
- Rich UX: Complex spawn form with multi-step OAuth flows, site pickers, environment dropdowns — benefits from component model
- Ecosystem: Largest community, most npm packages, easy to hire/onboard
- Decoupled from API: React SPA on Azure Static Web App, API calls to Container App
- Mobile-first: Excellent responsive component libraries (e.g., Radix, shadcn/ui)
- Alternative considered: Blazor (C# everywhere, but smaller ecosystem and less mobile polish)
- Free wildcard TLS: Cloudflare proxy mode (orange cloud) on
*.agentbox.networg.com— zero cost, zero renewal - Already there: networg.com DNS already managed in Cloudflare — no migration needed
- Bonus: DDoS protection, caching static assets, analytics
- Simple origin: Container Apps origin can use HTTP or Cloudflare Origin CA cert (free, 15-year validity)
- No Azure cert complexity: no managed cert provisioning delays, no rate limits on wildcard certs
- Trade-off accepted: Cloudflare terminates TLS (sees plaintext traffic) — acceptable for internal dev tooling
- 8-hour expiry: even if a user extracts
COPILOT_GITHUB_TOKENfrom the container, it's useless after 8 hours - Server-side refresh: API holds the refresh token in Key Vault, mints fresh access tokens per container spawn
- No long-lived secrets in containers: PATs would be extractable and valid for months
- Cost protection: $0.04/premium request means a leaked long-lived token could run up significant bills
- Alternative rejected: Fine-grained PAT (simpler but long-lived, higher risk if extracted)
- Cheapest serverless option: ~$0.01/mo for our scale, pay only for storage + transactions
- Simple key-value + partitioning: PartitionKey = userId, RowKey = boxName — covers all our queries
- Azure SDK native:
Azure.Data.TablesNuGet package, works with managed identity - Sufficient: we don't need complex queries, joins, or full-text search for box metadata
- Alternative rejected: Cosmos DB (~$25/mo minimum, overkill), SQLite (single-instance only, no HA)
- HCL is provider-agnostic:
azurermfor Azure resources +azureadfor Entra ID app registrations + groups in one language - State management: drift detection,
planbeforeapply, import existing resources - Modular: reusable modules, separate
.tfvarsper environment (dev/prod) - Ecosystem: vast community, well-documented providers, integrates with GitHub Actions via OIDC
- App registrations in code: Entra ID apps, service principals, federated credentials, API permissions — all version-controlled
- Alternative considered: Bicep (Azure-only, can't manage Entra ID app registrations natively)
- Same repo: pipelines live next to the code in
.github/workflows/ - OIDC workload identity federation: authenticate to Azure without stored secrets
- Matrix builds: multi-arch Docker images with Buildx + QEMU in one job
- PR integration: Terraform plan posted as PR comment, status checks gate merge
- Free tier: 2,000 minutes/month included in GitHub plan
- Alternative considered: Azure DevOps Pipelines (separate system, overkill for this project's CI/CD needs)