Read more of this story at Slashdot.
Read more of this story at Slashdot.
M.G. Siegler is the author of Spyglass.org. Siegler joins Big Technology to discuss the race to build the AI super app and which companies are best positioned to win. Tune in to hear why OpenAI, Anthropic, Google, Microsoft, Meta, and Apple are all converging on the same idea: an AI interface that can handle more and more of your computing life. We also cover Apple’s new Siri, whether consumer AI will be won by default on the iPhone, and what World Cup automation says about our growing reliance on machines. Hit play for a sharp, wide-ranging conversation about where AI products are headed next.
---
Enjoying Big Technology Podcast? Please rate us five stars ⭐⭐⭐⭐⭐ in your podcast app of choice.
Want a discount for Big Technology on Substack + Discord? Here’s 25% off for the first year: https://www.bigtechnology.com/subscribe?coupon=0843016b
Learn more about your ad choices. Visit megaphone.fm/adchoices
AI represents a major shift in how users interact with organizational data. Instead of finding information one file, email, or chat at a time, users can ask natural language questions and get synthesized answers drawn from content they already have permission to access. That can improve productivity, but it can also amplify the impact of broad access, oversharing, and weak governance.
Microsoft 365 Copilot brings this shift into focus. It doesn’t grant new permissions, but it acts as a force multiplier for existing access, making it faster and easier for users to discover, aggregate, and act on data across Microsoft 365. That makes it critical to understand both who can access Copilot and what data it can surface on a user’s behalf. A second layer of risk follows from how organizational data is structured, shared, and governed. In environments with overshared content, excessive permissions, or inconsistent governance, this acceleration can lead to faster and broader access to sensitive information than organizations may expect.
This shift carries important security implications, especially as organizations move from pilots to scaled deployment. While customers with Microsoft 365 E5 licensing may already have access to tools that help identify and reduce risk, licensing alone does not reduce exposure.
What’s often missing during early Copilot deployments is a clear view of the risk surface itself: what data is exposed, who can access it, and through which vectors. Organizations building their Microsoft 365 Zero Trust posture can use the Zero Trust Workshop to assess their current posture, identify gaps, and better understand how existing permissions and governance impact Copilot readiness.
In this post, we map Copilot-related risk to the key Zero Trust control areas most relevant to deployment: identity, endpoints, apps, and data. Our goal is to help organizations understand where exposure exists and which control areas they should focus on before scaling deployment.
Microsoft defines Zero Trust as a security model built on three core principles:
Instead of trusting users or devices based solely on network location, Zero Trust requires continuous validation of every user, endpoint, and request, regardless of where the request originates. Microsoft’s Zero Trust adoption model groups security controls across technology pillars such as identities, endpoints, apps, data, network, infrastructure, and security operations.
For this analysis, we’ll focus on the four pillars most directly involved in Microsoft 365 Copilot deployments: identity, endpoints, apps, and data.
Figure 1. Four control areas that shape Copilot exposure
Together, these pillars help answer three key questions:
For more information and details about Microsoft’s Zero Trust framework, visit Zero Trust Overview at Microsoft Learn.
Before assessing specific risks, it helps to understand why Copilot changes the risk conversation compared to traditional Microsoft 365 applications. Most enterprise apps operate within a bounded context. The time and effort required to manually locate, connect, and synthesize information across systems creates friction and a practical limit on how quickly users can surface and act on data across systems. Copilot removes much of that friction. At the speed of a prompt, Copilot lets users retrieve and bring together information from across a user’s existing access—spanning emails, files, meetings, chats, and other Microsoft 365 services—in a single response.
This exposure is not a result of new permissions. It comes from how quickly and easily existing access can be discovered, aggregated, and used. To understand where exposure appears, it helps to break Copilot risk into two interconnected layers: access and data.
The foundation of this analysis is simple: Copilot acts as a force multiplier for whatever access a user already has. If that access is governed well, Copilot can improve productivity. If not, it can amplify exposure.
With that framing in mind, we’ll examine Copilot risk in two distinct layers:
The first layer focuses on who can access Copilot and under what conditions. The second focuses on what Copilot can see and surface once access is granted.
The first layer of risk begins at the point of entry: the conditions that determine whether a user can access Copilot. Organizations don’t need to address every potential exposure point at once. Start by using the table below as a map of where exposure can exist across identity and endpoint controls. We’ll explore how to mitigate these risks in the second post of this series.
The risks below are primarily governed by Microsoft’s Zero Trust identity and endpoint pillars. Together, these pillars help determine whether the right user can access Copilot from a trusted endpoint under the right access conditions.
Table 1. Identity and endpoint risks that affect Copilot access
|
Risk |
Pillar |
Description | |
| R1 |
Unmanaged identity access |
Identity |
A compromised, shared, or former employee account can authenticate Microsoft 365 and access Copilot. Because Copilot operates across the full scope of a user’s permissions, compromised accounts can expose a much larger risk surface than before Copilot existed. Strong account hygiene and identity lifecycle management can reduce that exposure. |
| R2 |
Weak or absent multi-factor authentication (MFA) |
Identity |
If MFA isn’t enforced, or if legacy authentication bypasses modern sign-in controls, users can start Copilot sessions with only a password. In environments with inconsistent MFA coverage, stolen credentials may be enough to gain access. Because Copilot can synthesize data across services, compromised accounts create more risk than access to a single application. |
| R3 |
Unmanaged or non-compliant devices |
Endpoints |
Users can access Copilot through browsers and native apps from any device where they can authenticate. Unmanaged or noncompliant endpoints without endpoint protection, encryption, or compliance evaluation can expose Copilot sessions and allow outputs to be stored or exfiltrated locally. |
| R4 |
Broad Copilot licensing without role-based scoping |
Identity and apps |
Broad Copilot licensing without role-based scoping. Copilot pilots often begin with broad license assignments across departments or business units instead of smaller, security-reviewed user groups. When organizations assign licenses without reviewing access rights, permission posture, and role sensitivity, they can expand exposure across both well-governed and minimally governed users. |
| R5 |
Missing real-time risk evaluation at sign-in |
Identity and endpoints |
If Conditional Access doesn’t evaluate sign-in and user risk signals, such as anomalous activity, or identity protection alerts, high-risk sessions may still reach Copilot. Without real-time risk evaluation, controls may respond only after access is granted. |
| R6 |
App protection gap on mobile devices |
Endpoints and apps |
Users can access Copilot from personal mobile devices that are not enrolled in device or app management. Without app protection policies, organizations may not be able to control how Copilot outputs move into unmanaged apps or remove organizational data from lost devices. |
Identity appears most frequently across Layer 1, reflecting how tightly Copilot access depends on authenticated user permissions and sign-in conditions. Endpoint-related risks also play a major role because unmanaged or noncompliant endpoints can expose Copilot sessions and outputs beyond the organization’s trusted environment.
Together, these risks show that securing Copilot access is shaped by two factors: who can sign in, and whether the endpoint and session meet the organization’s access requirements.
The second layer of risk assumes that the user is already authenticated and actively using Copilot. At this stage, the focus shifts from who can access Copilot to what data Copilot can surface on the user’s behalf. These risks reflect the exposure created by existing permissions, overshared content, connected systems, and governance gaps.
Layer 2 risks are governed primarily by the apps and data pillars, with identity playing a secondary role in defining the scope of data each user can access. Much of Copilot’s value and risk comes from how it surfaces information based on user intent.
These risks highlight how permissions, sharing, labeling, and governance directly shape what Copilot can retrieve, synthesize, and surface across organizational data.
|
Risk |
Zero Trust pillar(s) |
Description | |
| R7 |
Overshared SharePoint and OneDrive content |
Apps and identity |
Content shared with "Everyone," "Everyone except external users," or broad groups can become part of Copilot’s queryable surface for licensed users who already have access to that content. Years of oversharing, combined with Copilot’s ability to retrieve and synthesize content in a single prompt, can turn long-standing governance gaps into immediate exposure. Copilot can make data that was once hidden by volume, discoverable by intent. |
| R8 |
Sensitivity label gaps |
Apps and data |
Microsoft Purview sensitivity labels and related protections tell Copilot how to handle content, including whether it can summarize, cite, or include that content in responses. Across containers such as SharePoint sites and Teams, unlabeled, incorrect, or inconsistently labeled content may be treated as unclassified and surfaced freely. Large volumes of legacy content can make that gap harder to manage at scale. |
| R9 |
Excessive user permissions |
Identity and apps |
Copilot follows each user’s existing Microsoft 365 permissions and does not surface content users cannot access. However, users often accumulate access beyond their current role through leftover project permissions, temporary group memberships, and inherited access. Copilot doesn’t create this overprovisioning, but it can make the full scope of it easier to discover and use. |
| R10 |
No DLP coverage on Copilot-generated outputs |
Apps and data |
Copilot outputs, such as summaries and drafts can combine sensitive details from multiple sources. Organizations should review how their DLP labeling and data protection policies apply to generated content, especially when users move Copilot outputs into email, chat, or external documents. |
| R11 |
Plugin and connector data surface expansion |
Apps and identity |
Organizations can extend Copilot through agent and connectors that pull external data from CRM systems, ITSM platforms, and other line-of-business tools. Each connected source expands the set of data users can access through Copilot. Each connected source expands the set of data users can access through Copilot and may introduce governance and access model differences that need to be reviewed. |
| R12 |
Audit and visibility gap |
Apps and data |
Organizations need enough visibility to understand how users interact with Copilot and which resources Copilot accesses in response to prompts. Audit logs and monitoring help security teams investigate suspicious activity, understand usage patterns, and reduce risk over time. |
| R13 |
Privileged user data amplification |
Identity, apps and data |
Privileged users often hold access across sensitive systems and data. A compromised privileged account with Copilot access can expose a much larger slice of organizational data than a standard user account. Privileged identities therefore need tighter access to governance and review. |
Apps-related risks appear throughout Layer 2 because Microsoft 365 Copilot depends on connected systems, accessible data sources, and governance over generated outputs. The data pillar reinforces the need to protect both sensitive source content and the AI-generated responses that bring content together. The table above maps each risk to its primary and secondary Zero Trust pillars to show where to act. Primary pillars represent control areas that most directly govern a given risk, while secondary pillars represent contributing factors.
Copilot typically doesn’t grant broader access than a user already has. Instead, it makes existing access easier to discover, connect, and use across Microsoft 365. Organizations that successfully scale Copilot are the ones that understand and govern that access first. Understanding where exposure exists is the first step. Your next step depends on where your organization is in their Zero Trust journey.
Need to assess your current status? Visit the Zero Trust Workshop.
Ready to learn more about reducing risk by strengthening access controls at the point of entry? Check out post 2 of this series: Mitigating Microsoft 365 Copilot access risk: Identity and device controls for Zero Trust.
When we shipped Shiny.Health.Extensions.AI, it proved a simple point: a chat model becomes far more useful when it can do things instead of just talk about them. Give it a tool to read step counts and “how did I sleep last week?” stops being a guess and starts being an answer pulled from the device.
So we did the same for three more device services. Three new packages expose Shiny modules as Microsoft.Extensions.AI tool functions:
Shiny.Contacts.Extensions.AI — search and manage the device address bookShiny.Notifications.Extensions.AI — schedule and cancel remindersShiny.Locations.Extensions.AI — read-only GPS: where you are, and how far/long to somewhere elseThey all follow the same shape as Health: you opt-in to exactly which operations the model can see (a read/write allow-list you control on behalf of the agent — not an OS permission prompt), it’s read-only by default, and the whole thing is IsAotCompatible — schemas are hand-built and results come back as JsonNode, so there’s no reflection in the tool path.
dotnet add package Shiny.Contacts.Extensions.AIbuilder.Services.AddContactStore();builder.Services.AddContactsAITools(tools => tools .AddContacts(ContactAICapabilities.ReadWrite) // Read is the default; ReadWrite adds create/update/delete);This generates search_contacts, get_contact, and — when you opt-in to Write — create_contact, update_contact, and delete_contact.
What you’d use it for: a personal-assistant or CRM-lite app where the user talks to the address book instead of tapping through it — “grab that number so I can text them,” “add the person on this business card,” “which of my contacts work at Acme?” The agent looks up an id with search_contacts, then acts on it.
Example questions:
dotnet add package Shiny.Notifications.Extensions.AIbuilder.Services.AddNotifications();builder.Services.AddNotificationAITools(tools => tools .AddReminders(ReminderAICapabilities.ReadWrite, channel: "reminders") // channel is optional);Local notifications, framed as reminders. You get list_reminders, and — with Write — create_reminder (fire now, at a specific date/time, or daily at a set time) and cancel_reminder.
What you’d use it for: any “remind me…” flow in a chat assistant — todo apps, habit and medication trackers, follow-up nudges. The user speaks a reminder in natural language and the model schedules a real local notification; scheduleFor and repeatDailyAt cover one-shot and recurring.
Example questions:
dotnet add package Shiny.Locations.Extensions.AIbuilder.Services.AddGps();builder.Services.AddLocationAITool(); // read-only — there's no write capability for GPSGPS is read-only, so there’s no builder to configure — a single AddLocationAITool() registers get_current_location, get_distance_to, and estimate_travel_time. The agent can learn where the user is and reason about distance and rough travel time to a destination.
What you’d use it for: travel and field-service apps, delivery/ETA estimates, “am I near…” checks, or any assistant that should be location-aware. One honest caveat baked into the tools: distances are great-circle (straight-line) and travel times use an assumed average speed — the results say so in a note field, so the model caveats its answer rather than pretending to be a routing engine.
Example questions:
Each package hands you a small bundle you resolve from DI. Concatenate their .Tools and pass the lot to any IChatClient:
var tools = new List<AITool>();tools.AddRange(sp.GetRequiredService<ContactAITools>().Tools);tools.AddRange(sp.GetRequiredService<NotificationAITools>().Tools);tools.AddRange(sp.GetRequiredService<LocationAITools>().Tools);
var response = await chatClient.GetResponseAsync( messages, new ChatOptions { Tools = tools });Now a single message can fan out across all three:
“I’m meeting Sarah at the office at 5 — how far am I, and remind me to leave in an hour.”
The model calls search_contacts for Sarah if it needs her details, get_distance_to for the office, and create_reminder for the nudge — one turn, three device services.
The capability builders (AddContacts(...), AddReminders(...)) are an allow-list for the agent — they decide which operations the model can see. They are not an OS permission prompt. The underlying platform permission must already be granted: call IContactStore.RequestAccess, INotificationManager.RequestAccess, or start a GPS listener from your app before you invoke the agent. The tools assume access is in place and return a clean error object if it isn’t.
dotnet add package Shiny.Contacts.Extensions.AIdotnet add package Shiny.Notifications.Extensions.AIdotnet add package Shiny.Locations.Extensions.AIFull details on each: Contacts AI Tools, Reminder AI Tools, and Location AI Tools. Same pattern as Health AI Tools — opt-in, read-only by default, AOT-safe, and now your agent has hands.
This blog post was created with the help of AI tools. Yes, I used a bit of magic from language models to organize my thoughts and automate the boring parts, but the geeky fun and the
in C# are 100% mine.

Hi!
If you have been playing with Foundry Local to run AI models on your own machine, you probably ran into the same thing I did: you never really know what is running.
You load a model from the CLI. Your C# app loads another one through the SDK. A Python script spins up its own. A .NET Aspire sample launches a proxy. And five minutes later you have several models loaded across several ports, eating memory, and you have no idea which one belongs to what.
Foundry Local does not run on a single fixed port, and the foundry CLI only sees what the CLI itself started. Everything an app loads directly through the SDK is, basically, invisible.
So I built a small tool to fix that: Foundry Local Monitor.
It is a tiny Windows tray app that watches your machine and tells you, in real time, which Foundry Local instances are running, which models are loaded, and on which device (CPU, GPU, CUDA, DirectML…). When a model loads or unloads — from any app — you get a notification.
button to open the process location on disk.
It is published as a .NET global tool, so one line and you are done:
dotnet tool install -g ElBruno.FoundryLocalMonitor
Then run it:
foundrylocalmon
That’s it. Look for the
icon in your system tray. Right-click for options, double-click to open the main window with the Status, Loaded Models, and Available Models tabs.
To update later:
dotnet tool update -g ElBruno.FoundryLocalMonitor
Here is the kind of code the monitor is designed to see. This is a plain .NET console app using the Microsoft.AI.Foundry.Local SDK — no CLI involved. The SDK spins up its own OpenAI-compatible REST server, and that is exactly what the monitor finds and shows in real time.
using Microsoft.AI.Foundry.Local;using Microsoft.Extensions.Logging.Abstractions;// The SDK hosts an internal OpenAI-compatible server on this address.// The C# SDK default is 55588 (Python uses 55589).var config = new Configuration{ AppName = "MyFoundryApp", Web = new Configuration.WebService { Urls = "http://127.0.0.1:55588" }};await FoundryLocalManager.CreateAsync(config, NullLogger.Instance);var manager = FoundryLocalManager.Instance;// Start the web service — this is the endpoint the monitor discovers.await manager.StartWebServiceAsync();var endpoint = manager.Urls?.FirstOrDefault();Console.WriteLine($"Foundry Local is serving at {endpoint}");
From this point on, the app is completely invisible to the foundry CLI — but the monitor sees it, because it answers on an HTTP port. The moment a model loads, you get a toast.
Talking to that endpoint is just OpenAI-compatible HTTP, so any .NET HttpClient works:
using System.Net.Http.Json;using var http = new HttpClient { BaseAddress = new Uri(endpoint!) };var response = await http.PostAsJsonAsync("/v1/chat/completions", new{ model = "qwen2.5-coder-0.5b", messages = new[] { new { role = "system", content = "You are a helpful assistant." }, new { role = "user", content = "What is Foundry Local in one sentence?" } }, max_tokens = 200});var json = await response.Content.ReadAsStringAsync();Console.WriteLine(json);
You can find a full end-to-end sample (init → start → load → chat → unload → stop) in the samples/FoundryLocalChat folder of the repo.
This is the part I like the most, so let me explain the trick.
Foundry Local does not use a well-known port. The daemon picks a random one at startup, the C# SDK defaults to 55588, the Python SDK to 55589, and an Aspire proxy gets whatever Aspire assigns. There is no central registry to ask.
So the monitor does something simple and effective: it scans your local ports.
127.0.0.1. This is a fast kernel call, no guessing.GET /v1/models request.{"object":"list"} is a Foundry Local (OpenAI-compatible) endpoint. Everything else is ignored.Why parallel? A typical machine has 15–30 listening ports. Probing them one by one, with a timeout each, could take 20+ seconds. Fanning out all the probes at once brings the whole scan down to roughly the time of a single probe — about 800ms. The scan re-runs every 30 seconds, so newly launched apps show up on their own.
In .NET, the port enumeration and the parallel probe boil down to something like this:
using System.Net.NetworkInformation;// 1. Ask the OS for every port listening on localhost (fast kernel call).var listeners = IPGlobalProperties.GetIPGlobalProperties() .GetActiveTcpListeners() .Where(ep => IPAddress.IsLoopback(ep.Address)) .Select(ep => ep.Port) .Distinct();// 2. Probe them all in parallel — a Foundry endpoint answers /v1/models// with {"object":"list"}.var probes = listeners.Select(async port =>{ using var http = new HttpClient { Timeout = TimeSpan.FromMilliseconds(800) }; try { var json = await http.GetStringAsync($"http://127.0.0.1:{port}/v1/models"); return json.Contains("\"object\":\"list\"") ? port : (int?)null; } catch { return null; }});var foundryPorts = (await Task.WhenAll(probes)) .Where(p => p is not null) .Select(p => p!.Value);
The nice side effect: because the monitor talks HTTP and not the CLI, it sees everything — CLI, C# SDK, Python SDK, Aspire, or any OpenAI-compatible local server. If it listens and it answers, it shows up.
Once the monitor knows where the endpoints are, it asks each one what it is serving:
GET /v1/models.GET /openai/loadedmodels (the models actually in memory).The interesting bit is the device. Foundry Local encodes the backend right in the model id as a suffix, for example:
Phi-4-mini-instruct-cuda-gpu:5
The monitor strips the version (:5), reads the suffix, and turns it into a friendly badge:
| Suffix | Device | Badge |
|---|---|---|
-cuda-gpu | CUDA | green |
-trtrtx-gpu | TensorRT | emerald |
-gpu | GPU | green |
-cpu | CPU | blue |
-directml-gpu | DirectML | purple |
-winml-cpu | WinML | orange |
The parsing itself is a nice little C# pattern-match — strip the version, match the longest device suffix, map it to a label:
static (string Alias, string Device) ParseModelId(string modelId){ // "Phi-4-mini-instruct-cuda-gpu:5" -> drop the ":5" version. var noVersion = modelId.Split(':')[0]; // Longest suffixes first so "-cuda-gpu" wins over "-gpu". string[] suffixes = [ "-trtrtx-gpu", "-cuda-gpu", "-generic-gpu", "-generic-cpu", "-winml-directml", "-winml-cpu", "-directml-gpu", "-cpu", "-gpu" ]; foreach (var suffix in suffixes) { if (!noVersion.EndsWith(suffix, StringComparison.OrdinalIgnoreCase)) continue; var alias = noVersion[..^suffix.Length]; var device = suffix switch { "-trtrtx-gpu" => "TensorRT", "-cuda-gpu" => "CUDA", "-generic-gpu" or "-gpu" => "GPU", "-generic-cpu" or "-cpu" => "CPU", "-winml-directml" or "-directml-gpu"=> "DirectML", "-winml-cpu" => "WinML", _ => "?" }; return (alias, device); } return (noVersion, "?"); // utility models have no device suffix}
So at a glance you can tell whether that model is actually running on your GPU or quietly sitting on the CPU. Very useful when you are wondering why something is slow.
And about those notifications: to avoid noise, models discovered on the SDK’s own internal ports are marked silent by default. Only load/unload events from real apps or the daemon pop a toast. You can tune this in Settings.
Foundry Local is a fantastic way to run models locally, and this little monitor gives you the missing piece of visibility: what is actually running, right now, and where. Install it, forget about it, and let it tell you when things change.
If you find a bug or want a feature, the repo is open — issues and PRs welcome.
NuGet package: ElBruno.FoundryLocalMonitor
Foundry Local .NET SDK: Microsoft.AI.Foundry.Local
Source code on GitHub: elbruno/ElBruno.FoundryLocalMonitor
Foundry Local documentation: learn.microsoft.com/azure/ai-foundry/foundry-local
Get started with Foundry Local: Foundry Local quickstartHappy coding!
Greetings
El Bruno
More posts in my blog ElBruno.com.
More info in https://beacons.ai/elbruno
