# Can IDE extension host identity prove who an agent is?

An editor extension host can carry a valid code signature and still leave you unable to say which extension requested a sensitive action. That is not a defect in code signing. It is the predictable result of asking an operating-system identity mechanism to answer a question that exists inside one process.

This matters when an AI coding agent runs through an IDE, where the host may load several extensions, accept commands from a workspace, and pass work to a gateway that can reach an API or an SSH target. If the gateway approves the host once and treats that approval as proof of a particular extension's intent, it grants more authority than the evidence supports.

I have seen this mistake arrive disguised as a clean security design: verify the signed editor, show the signer in an approval prompt, allow the run. That is a useful control. It becomes dangerous when the prompt implies a precision it does not have. The host may be trustworthy while the request that crossed its boundary remains ambiguous.

## A host signature identifies the container, not its occupants

Code signing proves facts about an executable image. On macOS, Apple describes code signing as a way to establish the origin and integrity of software. The system can check that a trusted signing authority signed the code and that the loaded code still matches the signed material. That is exactly the evidence a security decision needs when it asks, "Which application process is asking?"

It does not prove, "Which extension function inside that process asked?" A process has one executable identity at that boundary. If an editor starts an extension host and the host loads ten plugins, the kernel does not mint ten separate code-signing identities for their JavaScript, bytecode, callbacks, or extension API calls.

That distinction has a practical consequence. A gateway can make a defensible record such as:

```text
caller executable: /Applications/Editor.app/.../extension-host
signing authority: Example Software Team ID ABC123
process id: 8421
parent process: Editor.app pid 8304
```

It cannot derive this from the signature alone:

```text
extension: publisher.cloud-deploy
command: deployCurrentProject
prompt source: chat request 18
```

The first block is operating-system evidence. The second block is application provenance. Both can be useful, but they deserve different treatment in a log and in an approval screen.

People often call both "identity" and then lose the distinction during implementation. Do not. The signer tells you who produced the box. It does not tell you which passenger reached for the controls.

## Shared extension hosts collapse several authorities into one

An extension host exists so the editor can load and coordinate extensions. That design is convenient, but it makes the host an authority bundle. A completion plugin, a formatter, a source control integration, a chat assistant, and a workspace extension may all execute under the same host process.

Consider a gateway that allows an HTTPS call after it verifies the editor's signature. Extension A asks the host to call a deployment endpoint. Extension B has access to an extension API that can induce the same host to make outbound work, perhaps directly, perhaps through a command registered by A. At the gateway, both requests arrive with the same process identifier and signing authority. The gateway cannot separate them by inspecting the host signature.

The ambiguity gets worse when extensions communicate through shared host services. A plugin may register a command. Another plugin may invoke it. A chat extension may receive instructions from a repository file, an issue body, or a pasted terminal result and then dispatch a command. Attribution now has several layers: the signed host, the extension that made the call, the extension that supplied input, and the human or untrusted content that influenced it.

None of this makes IDE extensions inherently unsafe. It means that host-level approval is an approval for the host's combined authority. If that sounds too broad for an operation, you need a second control that sees the operation itself.

## An extension name is provenance only when the host binds it

An extension identifier such as `publisher.name` is useful context, but a gateway should not mistake a caller-supplied string for evidence. Any code that can form a request can write `publisher.name` into a header, JSON body, command argument, or environment variable. That only tells you what it claimed.

A host can make the claim stronger if it obtains the identifier from its own extension registry, binds it to the active execution context, and sends it across a protected local channel that extensions cannot forge. Even then, the result answers a narrower question: which host-managed extension context initiated this request. It may not identify the prompt, repository content, or person that influenced the context.

The useful test is simple. Ask where each field came from and who could alter it.

| Field | What it can establish | Who can forge or alter it |
|---|---|---|
| Host signing authority | The identity of the loaded host executable | An extension cannot normally forge it |
| Process identifier and start time | A particular running host instance | The operating system assigns it |
| Extension ID in a request | A stated extension identity | Any code able to create the request, unless the host binds it |
| Workspace path | The host's stated context | The host or extension can misreport it |
| Approval decision | A human approved the displayed request | The approval interface must bind it to the action |

This is why "we include the extension name in the audit log" is not a complete security statement. Keep the name. Mark it as host reported unless the architecture gives the gateway a reason to regard it as verified. That label prevents future investigators from treating convenience metadata as proof.

## Session approval has a useful boundary and a hard limit

Approving a newly started, verified host process is worthwhile. It catches a different executable, a changed signing authority, a new process lifetime, and a surprise launch path. It also gives the person at the keyboard a chance to see what is about to receive access. For routine work with a narrow credential, that interruption may be the right trade.

The limit is that a session approval grants the whole approved process whatever the session can do. If the process hosts many extensions, it cannot distinguish a request from the extension you expected from a request created by another loaded extension. A clean approval card can communicate the host signer and process identity, but it should not promise plugin-level attribution it lacks.

There is a second failure that teams miss. They approve an editor process in the morning, then install or enable an extension during the same long-lived session. If the host reloads or loads new code without changing the external identity that the gateway checks, the approval remains broader than the person remembers. The appropriate response depends on host behavior, but the principle stays fixed: changes inside an approved host are not automatically visible to a broker outside it.

Use session approval for the question it can answer: "May this signed host process use this category of access while it exists?" Do not stretch it into: "May this specific extension perform this specific irreversible action?"

## Per-use approval compensates for ambiguity at the moment it matters

For sensitive credentials, ask for approval when the action is formed, not only when the host first appears. The prompt should show enough of the proposed action for a person to make a meaningful decision: destination, method or SSH target, credential label, and the portion of the request that creates consequences.

Suppose a host sends this request to a local action gateway:

```json
{
  "channel": "http",
  "credential": "production-deploy",
  "method": "POST",
  "url": "https://deploy.example.internal/releases",
  "body": {"service": "billing", "version": "a1b2c3d"}
}
```

A suitable approval card should bind the decision to the exact method, destination, credential, and body or a stable digest of the body. It should not say only, "Editor wants to use production-deploy." That wording turns an action approval into a blank check for every call the host can make before the decision expires.

Per-use approval is unpopular because it interrupts flow. That objection is fair for harmless calls. It is weak for a production write, a credential with broad scope, or an SSH command that can change a machine. The person approving does not need to identify the guilty extension with certainty. They need to see the consequence about to leave the machine and decide whether it belongs.

A vault gate adds another boundary that session approval cannot provide. While the vault is locked, deny every action, including calls from previously approved hosts. Sallyport applies that absolute gate before session authorization, and a selected credential can require approval on every use. This makes the product of the controls honest: the host signer identifies the caller, the session decision admits that process for a limited lifetime, and the per-use decision covers the sensitive action.

## Narrow credentials reduce the damage of a correct approval

Approval is not a substitute for credential scope. A person can approve the right host and the right request, then still discover that the credential permits far more than the intended operation. That is a credential design failure, not an approval failure.

Split access according to consequence. A token that reads one repository should not also administer every project. A deployment credential should not create users or retrieve unrelated secrets. For SSH, use a separate account or forced command arrangement where the remote side supports it, rather than handing a general shell to a workflow that only needs one maintenance task.

The popular recommendation to give an agent one broad development token is attractive because setup takes five minutes. It is wrong when the token crosses environments or has write authority. A broad token turns every ambiguity inside the extension host into a broad ambiguity outside it. Narrow credentials make the approval prompt easier to read because the available action set already has edges.

For HTTP actions, constrain the destination in the credential record or gateway configuration when the design permits it. A bearer token injected into any arbitrary URL can be exfiltrated by a host that has been induced to call an attacker-controlled endpoint. For SSH actions, record the host, account, and intended command class before you decide that a session approval is enough.

## A familiar failure starts with a harmless command palette action

A developer installs an assistant extension and a deployment extension. Both run in one signed extension host. The developer approves the host when the assistant asks to inspect a staging API, because the approval card correctly displays the editor's signing authority.

Later, the developer opens a repository that contains a task file with instructions for the assistant. The assistant parses the file and invokes a host command registered by the deployment extension. The command asks the same gateway to use a production credential. The request has the same signed process identity as the staging request. A gateway that relies on session approval alone sees an approved caller and proceeds.

Nothing in that sequence requires a forged signature or a compromised operating system. The failure comes from an approval decision that covered the host and a credential that covered an action the developer did not intend to grant. An audit record that says only "approved editor made HTTP call" will not tell the team whether the deployment extension, the assistant, or a task file initiated the sequence.

Change the setup in three places. Require explicit approval for the production credential on every use. Show the destination and release payload in that approval. Record host identity separately from host-reported extension context and retain the connection between the approval event and the call. The request may still be legitimate, but it cannot pass as routine background work.

## Audit records need an evidence column, not a flattering story

Logs become misleading when they flatten verified facts and self-reported context into a single sentence. "Plugin X deployed service Y" looks precise, but it can hide an unverified plugin label and an unknown causal chain. Record the raw event with its source of truth.

A useful event shape separates the claims:

```json
{
  "time": "2026-07-24T10:16:43Z",
  "caller": {
    "signing_authority": "Example Software Team ID ABC123",
    "pid": 8421,
    "started_at": "2026-07-24T09:58:03Z"
  },
  "host_reported_context": {
    "extension_id": "publisher.cloud-deploy",
    "workspace": "/work/payments"
  },
  "action": {
    "channel": "http",
    "method": "POST",
    "destination": "https://deploy.example.internal/releases",
    "credential": "production-deploy"
  },
  "authorization": {
    "session_approved": true,
    "per_use_approved": true
  }
}
```

The point is not to produce more log fields. It is to preserve the boundary between a fact the system verified and a statement the host made. During an incident, that difference determines whether investigators can trace an action or merely repeat a label.

Tamper evidence matters too. A local journal that a compromised process can rewrite is weak evidence about its own behavior. Sallyport projects session and activity views from an encrypted, hash-chained audit log, and `sp audit verify` can verify the chain offline without a vault key. That does not solve extension attribution, but it prevents a later edit from quietly improving the story.

## Isolate the operation when attribution must be exact

Some actions need a stronger answer than a shared host can offer. If a credential can move money, alter production access, delete data, or run unrestricted remote commands, route the operation through a component with its own executable identity and a narrow interface. The gateway can then verify that helper rather than infer intent from a crowded extension host.

The helper should accept an explicit request schema, reject extra parameters, and keep its own authority small. For example, a release helper may accept a service name from an allowlist and a version digest, while rejecting arbitrary URLs and shell fragments. The parent editor can still initiate the work, but it cannot turn the helper into a general network client.

Process separation is not magic. If the host can send arbitrary requests to the helper, the helper has simply moved the same ambiguity into a new process. The interface must remove choices that the host should not have. A separate signed helper plus a wide-open "run anything" request is theatre.

When isolation costs too much, fall back to action-specific approval and narrow credentials. That combination gives a human the chance to catch the consequence even when the source inside the editor remains unclear. Do not claim plugin-level certainty unless the design can show where that certainty comes from.

## Treat the approval wording as part of the access boundary

An approval prompt changes behavior, so its wording needs the same care as the code that enforces it. If it says "Allow the editor to access deployment," users will learn to approve a category. If it says "Allow this signed extension host to POST this release to this destination using this credential," users can evaluate the action they are authorizing.

Keep the process signer prominent, because it catches the wrong application. Keep the extension name visible if the host provides it, because it helps a person recognize expected work. Label it as reported context when the gateway cannot verify it. People handle uncertainty better when the interface states it plainly than when a polished label suggests a guarantee.

Start by inventorying every credential reachable from an IDE host. For each one, write down the host-level approval it can safely inherit, the action shape that requires a fresh decision, and the exact evidence the audit record preserves. If the answer to "which extension did this?" is a guess, do not grant authority as if it were a fact.
