Skip to content
Open
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
faithfully reflects on-disk content across Copilot, Claude, and Gemini
targets (including OS-specific `windows`/`linux`/`osx` hook keys). (by
@harshitlarl, closes #316, #1700)
- Experimental, Copilot-only `canvas` primitive: `.apm/extensions/<name>/extension.mjs` deploys to `.github/extensions/<name>/` on `apm install`/`apm pack`, gated by `apm experimental enable canvas` + `--trust-canvas-extensions` for dependencies. (by @sergio-sisternes-epam, #1689)
- Experimental canvas: `apm install --global --trust-canvas-extensions` deploys dependency canvases to `~/.copilot/extensions/<name>/`; `apm uninstall --global` prunes them. (by @sergio-sisternes-epam, #1689)
- Experimental canvas: package validation recognises canvas extensions (canvas-only packages valid, gated-executable warning); `apm audit` (text) lists deployed canvas bundles. (by @sergio-sisternes-epam, #1689)

### Changed

Expand Down
1 change: 1 addition & 0 deletions docs/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ export default defineConfig({
{ label: 'GitHub Agentic Workflows', slug: 'integrations/gh-aw' },
{ label: 'Microsoft 365 Copilot Cowork (Experimental)', slug: 'integrations/copilot-cowork' },
{ label: 'GitHub Copilot App workflows (Experimental)', slug: 'integrations/copilot-app' },
{ label: 'Canvas extensions (Experimental)', slug: 'integrations/canvas' },
{ label: 'Hermes Agent (Experimental)', slug: 'integrations/hermes' },
{ label: 'AI runtime compatibility', slug: 'integrations/runtime-compatibility' },
{ label: 'GitHub rulesets', slug: 'integrations/github-rulesets' },
Expand Down
10 changes: 10 additions & 0 deletions docs/src/content/docs/concepts/primitives-and-targets.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,14 @@ Model Context Protocol servers declared as dependencies. APM writes the per-harn
- Source: `apm.yml` -> `dependencies.mcp:`
- Deep dive: [MCP servers](/apm/consumer/install-mcp-servers/)

### Canvas extensions (experimental)

GitHub Copilot CLI canvas extensions: a directory bundle whose entry file is `extension.mjs` (executable Node.js). Copilot-only. Behind the `canvas` experimental flag; dependency-provided canvases are blocked unless `--trust-canvas-extensions` is passed, because they are arbitrary executable code. Project scope deploys to `.github/extensions/`; `--global` deploys a dependency canvas to `~/.copilot/extensions/` (always requiring the trust flag).

- Source: `.apm/extensions/<name>/extension.mjs`
- Deploys to: `.github/extensions/<name>/` (project) or `~/.copilot/extensions/<name>/` (`--global`)
- Deep dive: [Canvas extensions](/apm/integrations/canvas/)

## Target catalogue

Each target is identified by a slug used in `apm.yml`'s `targets:` field and on the `--target` flag. The output directory is where APM writes deployed primitives. The "agent-skills" and "copilot-cowork" targets exist in the registry but are not end-user runtimes; they are covered separately in the experimental reference.
Expand Down Expand Up @@ -113,6 +121,7 @@ Rows are primitives, columns are harnesses. Cell legend:
| commands | unsupported | native | compiled | unsupported | compiled | compiled | compiled | unsupported |
| plugins | compiled | compiled | compiled | compiled | compiled | compiled | compiled | compiled |
| MCP servers | native | native | native | native | native | native | native | native |
| canvas (experimental) | gated | unsupported | unsupported | unsupported | unsupported | unsupported | unsupported | unsupported |

How to read a cell:

Expand All @@ -122,6 +131,7 @@ How to read a cell:
- `commands / copilot = unsupported` -- Copilot has no commands primitive; the same source `.prompt.md` reaches Copilot as a native prompt instead.
- `plugins / *` -- APM unpacks the plugin at install time into the primitives in the rows above; routing then follows those rows.
- `MCP servers / *` -- APM writes the harness's standard MCP config. Transitive MCP servers brought in by deep dependencies must be explicitly declared or trusted with `--trust-transitive-mcp` -- effectively `gated` for those, `native` for direct dependencies.
- `canvas / copilot = gated` -- requires the `canvas` experimental flag; a canvas shipped by a dependency is executable code, so it stays blocked until you pass `--trust-canvas-extensions`. First-party canvases in your own package deploy at project scope once the flag is on. With `--global`, a dependency canvas deploys to `~/.copilot/extensions/` and always requires the trust flag (first-party global install is not supported). Every other harness is `unsupported`: a canvas is a Copilot CLI construct only.

## Where compiled context files land

Expand Down
2 changes: 1 addition & 1 deletion docs/src/content/docs/enterprise/security.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ APM has no runtime footprint. Once `apm install` or `apm compile` completes, the

- **No runtime component.** APM generates files then terminates. It does not run alongside your application.
- **No network calls after install.** All network activity (git clone/fetch) occurs during dependency resolution. There are no callbacks, webhooks, or phone-home requests.
- **No arbitrary code execution.** APM does not execute scripts from packages, evaluate expressions in templates, or run downloaded code.
- **No arbitrary code execution.** APM does not execute scripts from packages, evaluate expressions in templates, or run downloaded code. (**Canvas exception:** the experimental `canvas` primitive deploys executable `extension.mjs` (Node.js) code to `.github/extensions/` or `~/.copilot/extensions/`; this surface is gated by both the `canvas` experimental flag and `--trust-canvas-extensions` for dependency-provided canvases. See [Canvas extensions](/apm/integrations/canvas/).)
- **No access to application data.** APM never reads databases, API responses, application state, or user data.
- **No persistent background processes.** APM does not install daemons, services, or scheduled tasks.
- **No telemetry or data collection.** APM collects no usage data, analytics, or diagnostics. Nothing is transmitted to Microsoft or any third party.
Expand Down
144 changes: 144 additions & 0 deletions docs/src/content/docs/integrations/canvas.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
---
title: "Canvas extensions (Experimental)"
description: "Ship GitHub Copilot CLI canvas extensions through APM packages (experimental, Copilot-only)."
sidebar:
order: 8
badge:
text: Experimental
variant: caution
---

:::caution[Experimental]
This feature is behind the `canvas` experimental flag and is off by default.
It is **Copilot-only**, and the CLI surface may change. Enable it explicitly
before use.
:::

A **canvas** is a GitHub Copilot CLI extension: a directory bundle whose entry
file is `extension.mjs` (executable Node.js), plus any sibling assets it needs.
Copilot CLI discovers canvases in immediate subdirectories of
`.github/extensions/<name>/` (project scope) and
`~/.copilot/extensions/<name>/` (user scope). APM lets a package carry a canvas
under `.apm/extensions/<name>/` and deploys it to the matching location at
install time so the canvas is available in your Copilot session.

Canvases are typically produced by the Copilot CLI `create-canvas` skill
(scaffolds a working extension in `.github/extensions/`). This page covers
how to ship one through an APM package.

## Enable the feature

```bash
apm experimental enable canvas
```

Default APM behaviour never changes until the flag is enabled. With the flag
off, `.apm/extensions/` is ignored entirely.

## Author a canvas

Place the bundle under your package's `.apm/` directory. The marker file is
`extension.mjs`; a directory without it is ignored.

```
.apm/
extensions/
my-canvas/
extension.mjs # required entry point (executable Node.js)
ui.js # optional sibling assets
styles.css
```

The `<name>` segment becomes both the deploy directory and the extension id, so
it is validated strictly: `[A-Za-z0-9._-]+`, no leading or trailing dot, no
`..`, no path separators, and reserved device names are rejected.

## Install

```bash
apm install --target copilot
```

APM deploys the bundle verbatim to `.github/extensions/my-canvas/`. The deploy
is **atomic**: every file in the bundle is planned and validated first, and any
unmanaged local collision skips the whole bundle (use `apm install --force` to
overwrite) so you never end up with a half-updated executable extension.

After a canvas deploys, start a new Copilot CLI session (exit and relaunch) --
Copilot CLI discovers extensions at session start, so a freshly-deployed canvas
is not picked up mid-session.

## Trust gate for dependency canvases

A canvas shipped by a **dependency** is arbitrary executable Node.js code. APM
blocks dependency-provided canvases by default. To deploy them, opt in
explicitly:

```bash
apm install --target copilot --trust-canvas-extensions
```

The trust gate is independent of the experimental flag:

- The **experimental flag** decides whether the canvas primitive is processed at
all. It is a feature-availability gate, not a security gate.
- The **`--trust-canvas-extensions` flag** decides whether *dependency*
canvases may deploy. Your own first-party canvas (in the root package you are
installing from) deploys freely once the flag is on; only dependency-provided
canvases need the trust flag.

When a dependency canvas is blocked, APM prints a diagnostic naming the package,
the canvas, the `extension.mjs` entry point, the deploy directory, and the
opt-in flag. The same gate is enforced on offline bundle install
(`apm install <bundle>`) and on `apm unpack`, so a vendored bundle cannot
smuggle an executable canvas past trust.

## Install globally (user scope)

To make a canvas available in **every** Copilot session, install it globally so
it lands in `~/.copilot/extensions/<name>/`:

```bash
apm install <package> --global --trust-canvas-extensions
```

Global canvas install is intentionally limited in this experimental release:

- **Dependency-provided only.** Only a canvas shipped by a package you install
(the `--global` flow always treats the canvas as dependency-provided) deploys
globally, so APM records it in the user lockfile and `apm uninstall --global`
can prune it. A first-party root `.apm/extensions/` canvas is **not** deployed
at user scope -- package it and install it as a dependency instead.
- **Trust is always required.** A global canvas has full-account blast radius,
so `--trust-canvas-extensions` is mandatory even though the project-scope
first-party path does not need it.
- **Default `~/.copilot` only.** If `$COPILOT_HOME` is set to a non-default
location, APM refuses the global canvas install rather than deploy to a path
Copilot will not scan.

`apm uninstall --global <package>` removes the deployed
`~/.copilot/extensions/<name>/` files and prunes the empty directories.

## Pack and uninstall

`apm pack` preserves `.apm/extensions/` in the bundle, so a packed package keeps
its canvas. `apm uninstall` removes the deployed `.github/extensions/<name>/`
files and prunes the now-empty directories; uninstall is never gated by the
experimental flag, so a previously-installed canvas can always be removed.

## Scope and limitations

- **Copilot-only.** A canvas is a Copilot CLI construct. Other targets
(`--target claude`, `cursor`, etc.) never receive it.
- **Global install is dependency-only.** User-scope (`--global`) deployment to
`~/.copilot/extensions/` supports dependency-provided canvases (always
requiring `--trust-canvas-extensions`) and the default `~/.copilot` location
only; first-party root canvases deploy at project scope only.
- **No compile/list surfacing yet.** Canvases are not yet shown by
`apm list`/`apm compile`; they are deployed at install only.
- **No policy-file control yet.** Canvas trust is controlled only by the
`--trust-canvas-extensions` CLI flag; governing it via `apm-policy.yml` is
planned but not part of this experimental release.

See the [primitives and targets](/apm/concepts/primitives-and-targets/) matrix
for where the canvas primitive sits.
1 change: 1 addition & 0 deletions docs/src/content/docs/reference/cli/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ With no arguments it installs everything from `apm.yml`. With one or more `PACKA
| `--audit <off\|warn\|block>` | (config/policy) | Run a content audit over the files this install deploys. `warn` records findings in the summary; `block` halts the install on critical findings. Overrides your `audit-on-install` config but cannot relax an org policy floor. Requires the `external-scanners` experimental flag. |
| `--no-audit` | off | Disable the install-time audit for this invocation (equivalent to `--audit off`). Cannot relax an org policy `block` floor. |
| `--trust-transitive-mcp` | off | Trust self-defined MCP servers shipped by transitive packages without re-declaring them in your `apm.yml`. |
| `--trust-canvas-extensions` | off | Trust executable canvas extensions (`extension.mjs`) shipped by dependencies. Required for dependency-provided canvases; first-party canvases deploy without it. Requires the `canvas` experimental flag. |
| `--allow-insecure` | off | Permit direct `http://` (non-TLS) dependencies. |
| `--allow-insecure-host HOSTNAME` | unset | Permit transitive `http://` dependencies from `HOSTNAME`. Repeatable. |

Expand Down
2 changes: 1 addition & 1 deletion docs/src/content/docs/reference/cli/pack.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ apm pack --archive --dry-run -v
A Claude Code plugin directory under `--output`. Contains:

- `plugin.json` -- schema-conformant manifest. Convention-dir keys are stripped because Claude Code auto-discovers them.
- Plugin-native subdirs populated from your `.apm/` content and from installed dependencies: `agents/`, `skills/`, `commands/`, `instructions/`, `hooks/`.
- Plugin-native subdirs populated from your `.apm/` content and from installed dependencies: `agents/`, `skills/`, `commands/`, `instructions/`, `hooks/`, `extensions/` (canvas extensions, when the `canvas` experimental flag is enabled).
- A merged `hooks.json` when multiple sources contribute hooks.
- `apm.lock.yaml` -- enriched copy with `pack:` metadata and a `bundle_files` map of per-file SHA-256 digests, used by `apm install` for install-time integrity verification.
- `devDependencies` are excluded.
Expand Down
1 change: 1 addition & 0 deletions docs/src/content/docs/reference/cli/unpack.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ Extraction is **additive-only**: only files listed in the bundle's lockfile are
| `--skip-verify` | off | Skip the bundle completeness check against the bundle's `apm.lock.yaml`. Useful for partial bundles. |
| `--dry-run` | off | List files that would be unpacked without writing anything. |
| `--force` | off | Deploy despite critical hidden-character findings from the security scan. Use only after independent verification. |
| `--trust-canvas-extensions` | off | Trust executable canvas extensions (`extension.mjs`) in the bundle. Without this, canvas files are stripped during extraction. Requires the `canvas` experimental flag. |
| `--verbose`, `-v` | off | Show per-file paths and full diagnostic context. |

## Examples
Expand Down
1 change: 1 addition & 0 deletions docs/src/content/docs/reference/experimental.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ apm experimental reset verbose-version
| `marketplace-authoring`| Enable marketplace authoring commands (init, build, publish, etc.). |
| `registries` | Enable REST-based APM package registries in `apm.yml`. |
| `external-scanners` | Ingest third-party SARIF scanners into `apm audit` (`--external`, including SkillSpector LLM mode and allowlisted `--external-args`), the `external.<name>.{llm,args}` config keys, and the `security.audit.scanners` policy block. See [External scanners](../integrations/external-scanners/). |
| `canvas` | Ship Copilot CLI canvas extensions (`.apm/extensions/<name>/extension.mjs`) through APM packages. Dependency-provided canvases additionally require `--trust-canvas-extensions`. See [Canvas extensions](../integrations/canvas/). |

New flags are proposed via [CONTRIBUTING.md](https://github.com/microsoft/apm/blob/main/CONTRIBUTING.md#how-to-add-an-experimental-feature-flag) and graduate to default when stable. See the contributor recipe for the full lifecycle.
See also: [Cowork integration](../integrations/copilot-cowork/).
Expand Down
Loading
Loading