diff --git a/.github/workflows/enterprise-dates.yml b/.github/workflows/enterprise-dates.yml
index f12bf24642d6..047618db6539 100644
--- a/.github/workflows/enterprise-dates.yml
+++ b/.github/workflows/enterprise-dates.yml
@@ -32,7 +32,7 @@ jobs:
app-id: ${{ secrets.DOCS_BOT_APP_ID }}
private-key: ${{ secrets.DOCS_BOT_APP_PRIVATE_KEY }}
owner: github
- repositories: docs-internal,docs-engineering
+ repositories: docs-internal,docs-engineering,enterprise-releases
- uses: ./.github/actions/node-npm-setup
diff --git a/.github/workflows/enterprise-release-issue.yml b/.github/workflows/enterprise-release-issue.yml
index 4b9f59498cb2..5b95ca5864d8 100644
--- a/.github/workflows/enterprise-release-issue.yml
+++ b/.github/workflows/enterprise-release-issue.yml
@@ -27,7 +27,7 @@ jobs:
app-id: ${{ secrets.DOCS_BOT_APP_ID }}
private-key: ${{ secrets.DOCS_BOT_APP_PRIVATE_KEY }}
owner: github
- repositories: docs-content,docs-engineering
+ repositories: docs-content,docs-engineering,enterprise-releases
- uses: ./.github/actions/node-npm-setup
diff --git a/.github/workflows/link-check-external.yml b/.github/workflows/link-check-external.yml
index 3c9877844c6c..a8440699ae87 100644
--- a/.github/workflows/link-check-external.yml
+++ b/.github/workflows/link-check-external.yml
@@ -61,15 +61,6 @@ jobs:
run: |
if [ -f "artifacts/external-link-report.md" ]; then
echo "has_report=true" >> $GITHUB_OUTPUT
- # Prepend disclaimer banner
- tmp=$(mktemp)
- {
- echo "> [!NOTE]"
- echo "> **No action needed right now.** The link checker is being actively worked on and may produce false positives. You can safely ignore this report for now. We'll let you know when it's reliable."
- echo ""
- cat "artifacts/external-link-report.md"
- } > "$tmp"
- mv "$tmp" "artifacts/external-link-report.md"
else
echo "has_report=false" >> $GITHUB_OUTPUT
echo "No broken link report generated - all links valid!"
diff --git a/.github/workflows/link-check-github-github.yml b/.github/workflows/link-check-github-github.yml
index fcbc1c11f1ee..b48532edd83e 100644
--- a/.github/workflows/link-check-github-github.yml
+++ b/.github/workflows/link-check-github-github.yml
@@ -55,6 +55,9 @@ jobs:
curl --retry-connrefused --retry 5 -I http://localhost:4000/
- name: Run broken github/github link check
+ env:
+ # Needs a token with access to github/github; the app token is scoped to it above
+ GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
run: |
npm run check-github-github-links -- broken_github_github_links.md
diff --git a/.github/workflows/link-check-internal.yml b/.github/workflows/link-check-internal.yml
index aedda28a08ea..1d8a394e6bae 100644
--- a/.github/workflows/link-check-internal.yml
+++ b/.github/workflows/link-check-internal.yml
@@ -248,9 +248,6 @@ jobs:
# Combine all markdown reports
echo "# Internal Links Report" > combined-report.md
echo "" >> combined-report.md
- echo "> [!NOTE]" >> combined-report.md
- echo "> **No action needed right now.** The link checker is being actively worked on and may produce false positives. You can safely ignore this report for now. We'll let you know when it's reliable." >> combined-report.md
- echo "" >> combined-report.md
echo "Generated: $(date -u +'%Y-%m-%d %H:%M UTC')" >> combined-report.md
echo "[Action run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})" >> combined-report.md
echo "" >> combined-report.md
diff --git a/.github/workflows/link-check-on-pr.yml b/.github/workflows/link-check-on-pr.yml
index 904596143a8f..6d77c5627e6a 100644
--- a/.github/workflows/link-check-on-pr.yml
+++ b/.github/workflows/link-check-on-pr.yml
@@ -8,7 +8,7 @@ on:
workflow_dispatch:
# merge_group:
pull_request:
- types: [labeled, opened, synchronize, reopened]
+ types: [opened, synchronize, reopened]
permissions:
contents: read
@@ -24,9 +24,7 @@ jobs:
check-links:
name: Check links
runs-on: ubuntu-latest
- if: |
- (github.repository == 'github/docs-internal' || github.repository == 'github/docs') &&
- (github.event_name == 'workflow_dispatch' || contains(github.event.pull_request.labels.*.name, 'check-links'))
+ if: github.repository == 'github/docs-internal' || github.repository == 'github/docs'
steps:
- name: Checkout
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
@@ -61,12 +59,14 @@ jobs:
- name: Check links in changed files
if: steps.changed-files.outputs.any_changed == 'true'
+ # Work in progress: never fail the PR. The comment is informational only.
+ continue-on-error: true
env:
FILES_CHANGED: ${{ steps.changed-files.outputs.all_changed_files }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ACTION_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
SHOULD_COMMENT: ${{ secrets.DOCS_BOT_APP_ID != '' }}
- FAIL_ON_FLAW: true
+ FAIL_ON_FLAW: false
ENABLED_LANGUAGES: en
run: npm run check-links-pr
diff --git a/Dockerfile b/Dockerfile
index b31271ee0caf..a593f03633b4 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -10,7 +10,7 @@
# ---------------------------------------------------------------
# To update the sha:
# https://github.com/github/gh-base-image/pkgs/container/gh-base-image%2Fgh-base-noble
-FROM ghcr.io/github/gh-base-image/gh-base-noble:20260625-083609-gedf42de01@sha256:bf0a2f43957531d73dcb2abceabf2228560f1f67d14e23f5bb0c0ccbabd77dee AS base
+FROM ghcr.io/github/gh-base-image/gh-base-noble:20260630-205522-g4a33dc6bb@sha256:2b8edd7da1718e2e9c25b98fd31dae58e57e756ef19842eac212255765cf4ada AS base
# Install curl for Node install and determining the early access branch
# Install git for cloning docs-early-access & translations repos
diff --git a/assets/images/help/copilot/copilot-sdk/features-agent-loop-diagram-1.png b/assets/images/help/copilot/copilot-sdk/features-agent-loop-diagram-1.png
index 9263455a5ac2..f2f0500a9d3e 100644
Binary files a/assets/images/help/copilot/copilot-sdk/features-agent-loop-diagram-1.png and b/assets/images/help/copilot/copilot-sdk/features-agent-loop-diagram-1.png differ
diff --git a/assets/images/help/copilot/copilot-sdk/features-agent-loop-diagram-2.png b/assets/images/help/copilot/copilot-sdk/features-agent-loop-diagram-2.png
index 6fe2706dff63..0c7b17986298 100644
Binary files a/assets/images/help/copilot/copilot-sdk/features-agent-loop-diagram-2.png and b/assets/images/help/copilot/copilot-sdk/features-agent-loop-diagram-2.png differ
diff --git a/assets/images/help/copilot/copilot-sdk/setup-azure-managed-identity-diagram-0.png b/assets/images/help/copilot/copilot-sdk/setup-azure-managed-identity-diagram-0.png
index 5123a687db31..44d0054435a4 100644
Binary files a/assets/images/help/copilot/copilot-sdk/setup-azure-managed-identity-diagram-0.png and b/assets/images/help/copilot/copilot-sdk/setup-azure-managed-identity-diagram-0.png differ
diff --git a/assets/images/help/copilot/copilot-sdk/setup-choosing-a-setup-path-diagram-0.png b/assets/images/help/copilot/copilot-sdk/setup-choosing-a-setup-path-diagram-0.png
index 8d1ed7b19f4c..ec49fe30e3e5 100644
Binary files a/assets/images/help/copilot/copilot-sdk/setup-choosing-a-setup-path-diagram-0.png and b/assets/images/help/copilot/copilot-sdk/setup-choosing-a-setup-path-diagram-0.png differ
diff --git a/assets/images/help/copilot/copilot-sdk/setup-scaling-diagram-0.png b/assets/images/help/copilot/copilot-sdk/setup-scaling-diagram-0.png
index a20cb534ffcc..9b6bb90c3292 100644
Binary files a/assets/images/help/copilot/copilot-sdk/setup-scaling-diagram-0.png and b/assets/images/help/copilot/copilot-sdk/setup-scaling-diagram-0.png differ
diff --git a/content/actions/reference/workflows-and-actions/workflow-syntax.md b/content/actions/reference/workflows-and-actions/workflow-syntax.md
index 5ca629d02b49..dc693471414b 100644
--- a/content/actions/reference/workflows-and-actions/workflow-syntax.md
+++ b/content/actions/reference/workflows-and-actions/workflow-syntax.md
@@ -915,6 +915,9 @@ Outputs and environment changes from a background step are only available after
Use `background` when you need fine-grained control: starting a long-running process (like a server or database) that stays up while later steps run, referencing a specific step with [`wait`](#jobsjob_idstepswait) or [`cancel`](#jobsjob_idstepscancel), or interleaving background work with other steps. If you instead have a self-contained group of steps that should all finish before the job continues, [`parallel`](#jobsjob_idstepsparallel) is a more convenient shorthand.
+> [!NOTE]
+> You cannot use `background` on steps inside a composite action. A composite action can itself run as a background step, but it cannot declare background steps internally.
+
### Example: Running a step in the background
```yaml
@@ -937,6 +940,9 @@ Pauses the job until one or more background steps complete. A `wait` step perfor
After a `wait` step completes, the outputs of the referenced background steps become available to subsequent steps. If a referenced background step failed, the `wait` step fails too.
+> [!NOTE]
+> A `wait` step always runs and does not support the [`if`](#jobsjob_idstepsif) conditional.
+
### Example: Waiting for specific background steps
```yaml
@@ -967,6 +973,9 @@ Pauses the job until all active background steps complete. This is useful when s
The `wait-all` keyword takes no arguments.
+> [!NOTE]
+> A `wait-all` step always runs and does not support the [`if`](#jobsjob_idstepsif) conditional.
+
### Example: Waiting for all background steps
```yaml
@@ -992,6 +1001,9 @@ steps:
Gracefully terminates a running background step. The runner sends the step's process a termination signal (`SIGTERM`) so it can clean up, and forcibly stops it (`SIGKILL`) if it does not exit within a short grace period. The `cancel` keyword targets a single background step by its `id`.
+> [!NOTE]
+> A `cancel` step always runs and does not support the [`if`](#jobsjob_idstepsif) conditional.
+
### Example: Canceling a background step
```yaml
@@ -1016,6 +1028,9 @@ Use `parallel` when you have a self-contained group of steps that should all fin
Each step in the group is subject to the same 10-step concurrency limit as other background steps.
+> [!NOTE]
+> You cannot use `parallel` inside a composite action.
+
### Example: Running steps in parallel
```yaml
diff --git a/content/copilot/concepts/billing/usage-based-billing-for-individuals.md b/content/copilot/concepts/billing/usage-based-billing-for-individuals.md
index 540f1f705719..43cd02e2e6fb 100644
--- a/content/copilot/concepts/billing/usage-based-billing-for-individuals.md
+++ b/content/copilot/concepts/billing/usage-based-billing-for-individuals.md
@@ -49,6 +49,8 @@ Each paid plan includes the following:
Your base credits are used first. If you go beyond your base credits, the flex allotment is applied automatically at the same rates across your IDE, {% data variables.product.prodname_dotcom_the_website %}, and {% data variables.copilot.copilot_cli_short %}. No additional setup is required. Your usage dashboard shows your available allowance and what you've used.
+Included {% data variables.product.prodname_ai_credits_short %} do not carry over between months. Unused credits are forfeited, and your allowance resets to the full monthly amount at 00:00:00 UTC on the first day of each calendar month. This reset date is fixed and does not change based on your subscription billing date. See [AUTOTITLE](/billing/concepts/billing-cycles#billing-cycles-for-metered-products).
+
### {% data variables.product.prodname_ai_credits %} allowance by plan
The following table shows what's included with each paid plan.
diff --git a/content/copilot/concepts/billing/usage-based-billing-for-organizations-and-enterprises.md b/content/copilot/concepts/billing/usage-based-billing-for-organizations-and-enterprises.md
index 152d4de0053c..4e959cd0d3c4 100644
--- a/content/copilot/concepts/billing/usage-based-billing-for-organizations-and-enterprises.md
+++ b/content/copilot/concepts/billing/usage-based-billing-for-organizations-and-enterprises.md
@@ -40,6 +40,8 @@ A user's included {% data variables.product.prodname_ai_credits_short %} are poo
Adding licenses mid-cycle increases the pool immediately. Removing licenses mid-cycle doesn't shrink the pool: the decrease is reflected at the start of the next billing cycle.
+Included {% data variables.product.prodname_ai_credits_short %} do not carry over between months. Unused credits are forfeited, and the pool resets to the full monthly amount at 00:00:00 UTC on the first day of each calendar month. This reset date is fixed and does not change based on when licenses are added, removed, or billed. See [AUTOTITLE](/billing/concepts/billing-cycles#billing-cycles-for-metered-products).
+
### Promotional amounts for existing customers
diff --git a/content/copilot/concepts/context/repository-indexing.md b/content/copilot/concepts/context/repository-indexing.md
index 0991e421347f..8c742a100dca 100644
--- a/content/copilot/concepts/context/repository-indexing.md
+++ b/content/copilot/concepts/context/repository-indexing.md
@@ -47,9 +47,9 @@ Once an index has been created for a repository, it can be used by:
> [!TIP] There is no limit to how many repositories you can index.
-## Semantic indexing for non-GitHub repositories
+## Semantic indexing for non-{% data variables.product.github %} repositories
-{% data variables.product.prodname_copilot_short %} in {% data variables.product.prodname_vscode %} can use semantic indexing for workspace files from repositories hosted outside {% data variables.product.github %}, such as GitLab and local repositories.
+{% data variables.product.prodname_copilot_short %} in {% data variables.product.prodname_vscode %} can use semantic indexing for workspace files from repositories hosted outside {% data variables.product.github %}, such as GitLab and local repositories. This feature uploads your data to {% data variables.product.github %} to make it searchable.
This feature is controlled by policy and is disabled by default. For organizations and enterprises with {% data variables.copilot.copilot_business_short %} or {% data variables.copilot.copilot_enterprise_short %}, an enterprise owner or organization owner must explicitly set the `Semantic indexing for non-GitHub repositories` policy to **Enabled** before members can use it. If the policy remains **Unconfigured**, the feature stays unavailable. See:
diff --git a/content/copilot/how-tos/copilot-cli/customize-copilot/add-skills.md b/content/copilot/how-tos/copilot-cli/customize-copilot/add-skills.md
index 3a13ac0bbb05..fd865d8c002a 100644
--- a/content/copilot/how-tos/copilot-cli/customize-copilot/add-skills.md
+++ b/content/copilot/how-tos/copilot-cli/customize-copilot/add-skills.md
@@ -53,6 +53,8 @@ Use the /frontend-design skill to create a responsive navigation bar in React.
* **Remove skills:** to remove a skill that you have added directly—not via a plugin—use the command `/skills remove SKILL-DIRECTORY`. To remove skills added as part of a plugin you must manage the plugin itself. Use the `info` subcommand to find out which plugin a skill came from.
+The `/skills` commands above run inside an interactive session. The same list, add, and remove operations are also available from the terminal command line by using the `copilot skill` subcommand. This is useful for scripting or for setting up skills before you start a session. For example, run `copilot skill list` to list your skills, or `copilot skill add ` to add one. For the full set of subcommands for {% data variables.copilot.copilot_cli_short %}, see [AUTOTITLE](/copilot/reference/copilot-cli-reference/cli-command-reference).
+
{% data reusables.copilot.skills-compared %}
To learn more about how skills differ from other customization features, see [AUTOTITLE](/copilot/concepts/agents/copilot-cli/comparing-cli-features).
diff --git a/content/copilot/how-tos/copilot-sdk/auth/byok.md b/content/copilot/how-tos/copilot-sdk/auth/byok.md
index 9a37fb8202ab..a24aa9f1cf69 100644
--- a/content/copilot/how-tos/copilot-sdk/auth/byok.md
+++ b/content/copilot/how-tos/copilot-sdk/auth/byok.md
@@ -413,7 +413,7 @@ func main() {
Name: "My Custom Model",
Capabilities: copilot.ModelCapabilities{
Supports: copilot.ModelSupports{Vision: false, ReasoningEffort: false},
- Limits: copilot.ModelLimits{MaxContextWindowTokens: 128000},
+ Limits: copilot.ModelLimits{MaxContextWindowTokens: copilot.Int(128000)},
},
},
}, nil
@@ -479,7 +479,7 @@ When using BYOK, be aware of these limitations:
### Identity limitations
-BYOK authentication uses **static credentials only**.
+BYOK authentication uses **static credentials only**.
You must use an API key or static bearer token that you manage yourself.
diff --git a/content/copilot/how-tos/copilot-sdk/features/cloud-sessions.md b/content/copilot/how-tos/copilot-sdk/features/cloud-sessions.md
index fe36d3c6f2d0..522537546bde 100644
--- a/content/copilot/how-tos/copilot-sdk/features/cloud-sessions.md
+++ b/content/copilot/how-tos/copilot-sdk/features/cloud-sessions.md
@@ -2,8 +2,10 @@
title: Cloud sessions
shortTitle: Cloud Sessions
intro: >-
- Run Copilot sessions on GitHub-hosted compute through Mission Control instead
- of local CLI sessions.
+ Cloud sessions run Copilot work on GitHub-hosted compute through Mission
+ Control. Use them when your app should create a session that executes remotely
+ instead of starting a local Copilot CLI session on the user's machine or your
+ server.
versions:
fpt: '*'
ghec: '*'
diff --git a/content/copilot/how-tos/copilot-sdk/features/custom-agents.md b/content/copilot/how-tos/copilot-sdk/features/custom-agents.md
index d93076614715..87b42f8122d5 100644
--- a/content/copilot/how-tos/copilot-sdk/features/custom-agents.md
+++ b/content/copilot/how-tos/copilot-sdk/features/custom-agents.md
@@ -3,7 +3,8 @@ title: Custom agents and sub-agent orchestration
shortTitle: Custom Agents
intro: >-
Define specialized agents with scoped tools and prompts, then let Copilot
- orchestrate them as sub-agents within a single session.
+ orchestrate them as sub-agents within a single session. For dispatching
+ multiple sub-agents in parallel, see [AUTOTITLE](/copilot/how-tos/copilot-sdk/features/fleet-mode).
versions:
fpt: '*'
ghec: '*'
diff --git a/content/copilot/how-tos/copilot-sdk/features/fleet-mode.md b/content/copilot/how-tos/copilot-sdk/features/fleet-mode.md
index 817959982f40..0605fd9dfedc 100644
--- a/content/copilot/how-tos/copilot-sdk/features/fleet-mode.md
+++ b/content/copilot/how-tos/copilot-sdk/features/fleet-mode.md
@@ -2,8 +2,13 @@
title: Fleet mode
shortTitle: Fleet Mode
intro: >-
- Use fleet mode to split work across multiple sub-agents and combine their
- results in one parent session.
+ Fleet mode is Copilot's parallel orchestration pattern for work that can be
+ split across independent sub-agents. In the runtime research notes, fleet mode
+ is described as "the runtime's built-in pattern for dispatching multiple
+ sub-agents in parallel via the `task` tool, with SQL todos as the shared
+ coordination state." Use it when one parent session should coordinate several
+ workers, collect their results, and continue the conversation with the
+ combined context.
versions:
fpt: '*'
ghec: '*'
@@ -59,7 +64,7 @@ if (result.started) {
{% codetab python %}
```python
-from copilot.generated.rpc import FleetStartRequest
+from copilot.rpc import FleetStartRequest
result = await session.rpc.fleet.start(
FleetStartRequest(
@@ -151,7 +156,7 @@ if (result.Started)
{% codetab rust %}
```rust
-use github_copilot_sdk::generated::api_types::FleetStartRequest;
+use github_copilot_sdk::rpc::FleetStartRequest;
let result = session
.rpc()
diff --git a/content/copilot/how-tos/copilot-sdk/features/image-input.md b/content/copilot/how-tos/copilot-sdk/features/image-input.md
index 5f95fc5db566..db4f625e9edd 100644
--- a/content/copilot/how-tos/copilot-sdk/features/image-input.md
+++ b/content/copilot/how-tos/copilot-sdk/features/image-input.md
@@ -326,7 +326,7 @@ func main() {
Prompt: "Describe what you see in this image",
Attachments: []copilot.Attachment{
&copilot.AttachmentBlob{
- Data: base64ImageData,
+ Data: &base64ImageData,
MIMEType: mimeType,
DisplayName: &displayName,
},
@@ -342,7 +342,7 @@ session.Send(ctx, copilot.MessageOptions{
Prompt: "Describe what you see in this image",
Attachments: []copilot.Attachment{
&copilot.AttachmentBlob{
- Data: base64ImageData, // base64-encoded string
+ Data: &base64ImageData, // base64-encoded string
MIMEType: mimeType,
DisplayName: &displayName,
},
diff --git a/content/copilot/how-tos/copilot-sdk/features/mcp.md b/content/copilot/how-tos/copilot-sdk/features/mcp.md
index 94f126e367c4..a6abc3d437e8 100644
--- a/content/copilot/how-tos/copilot-sdk/features/mcp.md
+++ b/content/copilot/how-tos/copilot-sdk/features/mcp.md
@@ -135,7 +135,7 @@ func main() {
"my-local-server": copilot.MCPStdioServerConfig{
Command: "node",
Args: []string{"./mcp-server.js"},
- Tools: &[]string{"*"},
+ Tools: []string{"*"},
},
},
})
diff --git a/content/copilot/how-tos/copilot-sdk/features/plugin-directories.md b/content/copilot/how-tos/copilot-sdk/features/plugin-directories.md
index f66586d55a10..2aa5601d537b 100644
--- a/content/copilot/how-tos/copilot-sdk/features/plugin-directories.md
+++ b/content/copilot/how-tos/copilot-sdk/features/plugin-directories.md
@@ -2,8 +2,11 @@
title: Plugin directories
shortTitle: Plugin Directories
intro: >-
- Use plugin directories to load skills, hooks, MCP servers, custom agents, and
- LSP settings from a single manifest.
+ A **plugin** is a directory that bundles SDK extensions — skills, hooks, MCP
+ servers, custom agents, and LSP configuration — behind a single manifest.
+ Pointing the SDK at a plugin directory loads everything the plugin
+ contributes, so you can ship reusable capability packs without writing
+ per-extension wiring in every host application.
versions:
fpt: '*'
ghec: '*'
diff --git a/content/copilot/how-tos/copilot-sdk/features/streaming-events.md b/content/copilot/how-tos/copilot-sdk/features/streaming-events.md
index 202b768e05a6..e597af9e50f9 100644
--- a/content/copilot/how-tos/copilot-sdk/features/streaming-events.md
+++ b/content/copilot/how-tos/copilot-sdk/features/streaming-events.md
@@ -65,7 +65,7 @@ session.on("assistant.message_delta", (event) => {
```python
from copilot import CopilotClient
-from copilot.generated.session_events import SessionEventType
+from copilot.session_events import SessionEventType
client = CopilotClient()
@@ -79,7 +79,7 @@ def handle(event):
```
```python
-from copilot.generated.session_events import SessionEventType
+from copilot.session_events import SessionEventType
def handle(event):
if event.type == SessionEventType.ASSISTANT_MESSAGE_DELTA:
diff --git a/content/copilot/how-tos/copilot-sdk/getting-started.md b/content/copilot/how-tos/copilot-sdk/getting-started.md
index 1dd730d0233c..666c71ee5114 100644
--- a/content/copilot/how-tos/copilot-sdk/getting-started.md
+++ b/content/copilot/how-tos/copilot-sdk/getting-started.md
@@ -32,7 +32,7 @@ Copilot: In Tokyo it's 75°F and sunny. Great day to be outside!
Before you begin, make sure you have:
-* **GitHub Copilot CLI** installed and authenticated (the Node.js, Python, and .NET SDKs bundle the CLI automatically—see [AUTOTITLE](/copilot/how-tos/copilot-sdk/setup/bundled-cli). Required for Go, Java, and Rust unless using their application-level CLI bundling features.)
+* **GitHub Copilot CLI** installed and authenticated (the Node.js, Python, and .NET SDKs provide the CLI automatically—see [AUTOTITLE](/copilot/how-tos/copilot-sdk/setup/bundled-cli). Required for Go, Java, and Rust unless using their application-level CLI bundling features.)
* Your preferred language runtime:
* **Node.js** 20+ or **Python** 3.11+ or **Go** 1.24+ or **Rust** 1.94+ or **Java** 17+ or **.NET** 8.0+
@@ -408,7 +408,7 @@ import asyncio
import sys
from copilot import CopilotClient
from copilot.session import PermissionHandler
-from copilot.generated.session_events import SessionEventType
+from copilot.session_events import SessionEventType
async def main():
client = CopilotClient()
@@ -652,7 +652,7 @@ unsubscribeIdle();
```python
from copilot import CopilotClient, PermissionDecisionApproveOnce
-from copilot.generated.session_events import SessionEvent, SessionEventType
+from copilot.session_events import SessionEvent, SessionEventType
client = CopilotClient()
@@ -920,7 +920,7 @@ import sys
from copilot import CopilotClient
from copilot.session import PermissionHandler
from copilot.tools import define_tool
-from copilot.generated.session_events import SessionEventType
+from copilot.session_events import SessionEventType
from pydantic import BaseModel, Field
# Define the parameters for the tool using Pydantic
@@ -1336,7 +1336,7 @@ import sys
from copilot import CopilotClient
from copilot.session import PermissionHandler
from copilot.tools import define_tool
-from copilot.generated.session_events import SessionEventType
+from copilot.session_events import SessionEventType
from pydantic import BaseModel, Field
class GetWeatherParams(BaseModel):
@@ -1870,11 +1870,13 @@ const session = await client.createSession({
});
```
-Available section IDs: `identity`, `tone`, `tool_efficiency`, `environment_context`, `code_change_rules`, `guidelines`, `safety`, `tool_instructions`, `custom_instructions`, `runtime_instructions`, `last_instructions`.
+Available section IDs: `preamble`, `identity`, `tone`, `tool_efficiency`, `environment_context`, `code_change_rules`, `guidelines`, `safety`, `tool_instructions`, `custom_instructions`, `runtime_instructions`, `last_instructions`.
-Each override supports four actions: `replace`, `remove`, `append`, and `prepend`. Unknown section IDs are handled gracefully—content is appended to additional instructions and a warning is emitted; `remove` on unknown sections is silently ignored.
+`identity` and `tool_instructions` are section *groups*: they target a collection of related sub-sections as a unit. Use `preamble` to target just the identity preamble without affecting its sibling sub-sections.
-See the language-specific SDK READMEs for examples in [TypeScript](https://github.com/github/copilot-sdk/tree/main/nodejs/README.md?utm_source=docs-copilot-sdk-typescript-readme&utm_medium=docs&utm_campaign=msbuild-2026), [Python](https://github.com/github/copilot-sdk/tree/main/python/README.md?utm_source=docs-copilot-sdk-python-readme&utm_medium=docs&utm_campaign=msbuild-2026), [Go](https://github.com/github/copilot-sdk/tree/main/go/README.md?utm_source=docs-copilot-sdk-go-readme&utm_medium=docs&utm_campaign=msbuild-2026), [Rust](https://github.com/github/copilot-sdk/tree/main/rust/README.md?utm_source=docs-copilot-sdk-rust-readme&utm_medium=docs&utm_campaign=msbuild-2026), [Java](https://github.com/github/copilot-sdk/tree/main/java/README.md?utm_source=docs-copilot-sdk-java-readme&utm_medium=docs&utm_campaign=msbuild-2026), and [C#](https://github.com/github/copilot-sdk/tree/main/dotnet/README.md?utm_source=docs-copilot-sdk-csharp-readme&utm_medium=docs&utm_campaign=msbuild-2026).
+Each override supports five actions: `replace`, `remove`, `append`, `prepend`, and `preserve`. The `preserve` action is a no-op that opts an individually-addressable section out of a group-level `remove` (for example, keep `tone` when removing the `identity` group). Unknown section IDs are handled gracefully: content from `replace`/`append`/`prepend` overrides is appended to additional instructions, and `remove` overrides are silently ignored.
+
+See the language-specific SDK READMEs for examples in [TypeScript](https://github.com/github/copilot-sdk/tree/main/nodejs/README.md), [Python](https://github.com/github/copilot-sdk/tree/main/python/README.md), [Go](https://github.com/github/copilot-sdk/tree/main/go/README.md), [Rust](https://github.com/github/copilot-sdk/tree/main/rust/README.md), [Java](https://github.com/github/copilot-sdk/tree/main/java/README.md), and [C#](https://github.com/github/copilot-sdk/tree/main/dotnet/README.md).
## Connecting to an external CLI server
@@ -2172,11 +2174,14 @@ Dependency: `io.opentelemetry:opentelemetry-api`
| Option | Node.js | Python | Go | Rust | Java | .NET | Description |
|---|---|---|---|---|---|---|---|
| OTLP endpoint | `otlpEndpoint` | `otlp_endpoint` | `OTLPEndpoint` | `otlp_endpoint` | `otlpEndpoint` | `OtlpEndpoint` | OTLP HTTP endpoint URL |
+| OTLP protocol | `otlpProtocol` | `otlp_protocol` | `OTLPProtocol` | `otlp_protocol` | `otlpProtocol` | `OtlpProtocol` | OTLP HTTP protocol for all signals: `"http/json"` or `"http/protobuf"` |
| File path | `filePath` | `file_path` | `FilePath` | `file_path` | `filePath` | `FilePath` | File path for JSON-lines trace output |
| Exporter type | `exporterType` | `exporter_type` | `ExporterType` | `exporter_type` | `exporterType` | `ExporterType` | `"otlp-http"` or `"file"` |
| Source name | `sourceName` | `source_name` | `SourceName` | `source_name` | `sourceName` | `SourceName` | Instrumentation scope name |
| Capture content | `captureContent` | `capture_content` | `CaptureContent` | `capture_content` | `captureContent` | `CaptureContent` | Whether to capture message content |
+The OTLP protocol field configures the CLI's `"otlp-http"` exporter for all signals. Leave it unset to use the CLI default, or set it to `"http/protobuf"` to export protobuf over HTTP.
+
### File export
To write traces to a local file instead of an OTLP endpoint:
@@ -2205,12 +2210,12 @@ Trace context is propagated automatically—no manual instrumentation is needed:
* [AUTOTITLE](/copilot/how-tos/copilot-sdk/auth/authenticate) - GitHub OAuth, environment variables, and BYOK
* [AUTOTITLE](/copilot/how-tos/copilot-sdk/auth/byok) - Use your own API keys from Azure AI Foundry, OpenAI, etc.
-* [Node.js SDK Reference](https://github.com/github/copilot-sdk/tree/main/nodejs/README.md?utm_source=docs-copilot-sdk-nodejs-reference&utm_medium=docs&utm_campaign=msbuild-2026)
-* [Python SDK Reference](https://github.com/github/copilot-sdk/tree/main/python/README.md?utm_source=docs-copilot-sdk-python-reference&utm_medium=docs&utm_campaign=msbuild-2026)
-* [Go SDK Reference](https://github.com/github/copilot-sdk/tree/main/go/README.md?utm_source=docs-copilot-sdk-go-reference&utm_medium=docs&utm_campaign=msbuild-2026)
-* [Rust SDK Reference](https://github.com/github/copilot-sdk/tree/main/rust/README.md?utm_source=docs-copilot-sdk-rust-reference&utm_medium=docs&utm_campaign=msbuild-2026)
-* [.NET SDK Reference](https://github.com/github/copilot-sdk/tree/main/dotnet/README.md?utm_source=docs-copilot-sdk-dotnet-reference&utm_medium=docs&utm_campaign=msbuild-2026)
-* [Java SDK Reference](https://github.com/github/copilot-sdk/tree/main/java/README.md?utm_source=docs-copilot-sdk-java-reference&utm_medium=docs&utm_campaign=msbuild-2026)
+* [Node.js SDK Reference](https://github.com/github/copilot-sdk/tree/main/nodejs/README.md)
+* [Python SDK Reference](https://github.com/github/copilot-sdk/tree/main/python/README.md)
+* [Go SDK Reference](https://github.com/github/copilot-sdk/tree/main/go/README.md)
+* [Rust SDK Reference](https://github.com/github/copilot-sdk/tree/main/rust/README.md)
+* [.NET SDK Reference](https://github.com/github/copilot-sdk/tree/main/dotnet/README.md)
+* [Java SDK Reference](https://github.com/github/copilot-sdk/tree/main/java/README.md)
* [AUTOTITLE](/copilot/how-tos/copilot-sdk/features/mcp) - Integrate external tools via Model Context Protocol
* [GitHub MCP Server Documentation](https://github.com/github/github-mcp-server)
* [MCP Servers Directory](https://github.com/modelcontextprotocol/servers) - Explore more MCP servers
diff --git a/content/copilot/how-tos/copilot-sdk/observability/opentelemetry.md b/content/copilot/how-tos/copilot-sdk/observability/opentelemetry.md
index cd017a2892da..39d471e5c8f6 100644
--- a/content/copilot/how-tos/copilot-sdk/observability/opentelemetry.md
+++ b/content/copilot/how-tos/copilot-sdk/observability/opentelemetry.md
@@ -112,11 +112,14 @@ let client = Client::start(ClientOptions::new()
| Option | Node.js | Python | Go | .NET | Java | Rust | Description |
|---|---|---|---|---|---|---|---|
| OTLP endpoint | `otlpEndpoint` | `otlp_endpoint` | `OTLPEndpoint` | `OtlpEndpoint` | `otlpEndpoint` | `otlp_endpoint` | OTLP HTTP endpoint URL |
+| OTLP protocol | `otlpProtocol` | `otlp_protocol` | `OTLPProtocol` | `OtlpProtocol` | `otlpProtocol` | `otlp_protocol` | OTLP HTTP protocol for all signals: `"http/json"` or `"http/protobuf"` |
| File path | `filePath` | `file_path` | `FilePath` | `FilePath` | `filePath` | `file_path` | File path for JSON-lines trace output |
| Exporter type | `exporterType` | `exporter_type` | `ExporterType` | `ExporterType` | `exporterType` | `exporter_type` | `"otlp-http"` or `"file"` |
| Source name | `sourceName` | `source_name` | `SourceName` | `SourceName` | `sourceName` | `source_name` | Instrumentation scope name |
| Capture content | `captureContent` | `capture_content` | `CaptureContent` | `CaptureContent` | `captureContent` | `capture_content` | Whether to capture message content |
+The OTLP protocol field configures the CLI's `"otlp-http"` exporter for all signals. Leave it unset to use the CLI default, or set it to `"http/protobuf"` to export protobuf over HTTP.
+
### Trace context propagation
> **Most users don't need this.** The `TelemetryConfig` above is all you need to collect traces from the CLI. The trace context propagation described in this section is an **advanced feature** for applications that create their own OpenTelemetry spans and want them to appear in the **same distributed trace** as the CLI's spans.
diff --git a/content/copilot/how-tos/copilot-sdk/setup/azure-managed-identity.md b/content/copilot/how-tos/copilot-sdk/setup/azure-managed-identity.md
index 6e898e07262c..7f228f5edcf9 100644
--- a/content/copilot/how-tos/copilot-sdk/setup/azure-managed-identity.md
+++ b/content/copilot/how-tos/copilot-sdk/setup/azure-managed-identity.md
@@ -3,8 +3,8 @@ title: Azure managed identity with BYOK
shortTitle: Azure Managed Identity
intro: >-
The Copilot SDK's [AUTOTITLE](/copilot/how-tos/copilot-sdk/auth/byok) accepts static API keys, but
- Azure deployments often use **Managed Identity** (Entra ID) instead of
- long-lived keys. Since the SDK doesn't natively support Entra ID
+ Azure deployments often use **Managed Identity** (Microsoft Entra ID) instead
+ of long-lived keys. Since the SDK doesn't natively support Microsoft Entra
authentication, you can use a short-lived bearer token via the `bearer_token`
provider config field.
versions:
@@ -18,28 +18,99 @@ contentType: how-tos
-This guide shows how to use `DefaultAzureCredential` from the [Azure Identity](https://learn.microsoft.com/python/api/azure-identity/azure.identity.defaultazurecredential) library to authenticate with Azure AI Foundry models through the Copilot SDK.
+This guide shows how to use the Azure Identity SDK's `DefaultAzureCredential` API to authenticate with Microsoft Foundry models through the Copilot SDK.
## How it works
-Azure AI Foundry's OpenAI-compatible endpoint accepts bearer tokens from Entra ID in place of static API keys. The pattern is:
+Microsoft Foundry's OpenAI-compatible endpoint accepts bearer tokens from Microsoft Entra ID in place of static API keys. The pattern is:
-1. Use `DefaultAzureCredential` to obtain a token for the `https://cognitiveservices.azure.com/.default` scope
+1. Use `DefaultAzureCredential` to obtain a token for the `https://ai.azure.com/.default` scope
1. Pass the token as the `bearer_token` in the BYOK provider config
1. Refresh the token before it expires (tokens are typically valid for ~1 hour)

-## Python example
+## Code samples
### Prerequisites
+Install the Azure Identity and Copilot SDK packages for your language:
+
+{% codetabs %}
+{% codetab dotnet %}
+
+
+
+```bash
+dotnet add package GitHub.Copilot.SDK
+dotnet add package Azure.Core
+```
+
+{% endcodetab %}
+{% codetab python %}
+
+
+
```bash
pip install github-copilot-sdk azure-identity
```
+{% endcodetab %}
+{% codetab typescript %}
+
+
+
+```bash
+npm install @github/copilot-sdk @azure/identity
+```
+
+{% endcodetab %}
+{% endcodetabs %}
+
### Basic usage
+Get a token using `DefaultAzureCredential` and pass it as the bearer token in your provider configuration:
+
+{% codetabs %}
+{% codetab dotnet %}
+
+
+
+```csharp
+using Azure.Core;
+using Azure.Identity;
+using GitHub.Copilot;
+
+DefaultAzureCredential credential = new(
+ DefaultAzureCredential.DefaultEnvironmentVariableName);
+AccessToken token = await credential.GetTokenAsync(
+ new TokenRequestContext(new[] { "https://ai.azure.com/.default" }));
+
+await using CopilotClient client = new();
+string? foundryUrl = Environment.GetEnvironmentVariable("FOUNDRY_RESOURCE_URL");
+
+await using CopilotSession session = await client.CreateSessionAsync(new SessionConfig
+{
+ Model = "gpt-5.5",
+ Provider = new ProviderConfig
+ {
+ Type = "openai",
+ BaseUrl = $"{foundryUrl!.TrimEnd('/')}/openai/v1/",
+ BearerToken = token.Token,
+ WireApi = "responses",
+ },
+});
+
+AssistantMessageEvent? response = await session.SendAndWaitAsync(
+ new MessageOptions { Prompt = "Hello from Managed Identity!" });
+Console.WriteLine(response?.Data.Content);
+```
+
+{% endcodetab %}
+{% codetab python %}
+
+
+
```python
import asyncio
import os
@@ -48,22 +119,22 @@ from azure.identity import DefaultAzureCredential
from copilot import CopilotClient
from copilot.session import PermissionHandler, ProviderConfig
-COGNITIVE_SERVICES_SCOPE = "https://cognitiveservices.azure.com/.default"
+SCOPE = "https://ai.azure.com/.default"
async def main():
# Get a token using Managed Identity, Azure CLI, or other credential chain
- credential = DefaultAzureCredential()
- token = credential.get_token(COGNITIVE_SERVICES_SCOPE).token
+ credential = DefaultAzureCredential(require_envvar=True)
+ token = credential.get_token(SCOPE).token
- foundry_url = os.environ["AZURE_AI_FOUNDRY_RESOURCE_URL"]
+ foundry_url = os.environ["FOUNDRY_RESOURCE_URL"]
client = CopilotClient()
await client.start()
session = await client.create_session(
on_permission_request=PermissionHandler.approve_all,
- model="gpt-4.1",
+ model="gpt-5.5",
provider=ProviderConfig(
type="openai",
base_url=f"{foundry_url.rstrip('/')}/openai/v1/",
@@ -81,30 +152,69 @@ async def main():
asyncio.run(main())
```
+{% endcodetab %}
+{% codetab typescript %}
+
+
+
+```typescript
+import { DefaultAzureCredential } from "@azure/identity";
+import { CopilotClient } from "@github/copilot-sdk";
+
+const credential = new DefaultAzureCredential({
+ requiredEnvVars: ["AZURE_TOKEN_CREDENTIALS"],
+});
+const tokenResponse = await credential.getToken(
+ "https://ai.azure.com/.default"
+);
+
+const client = new CopilotClient();
+
+const session = await client.createSession({
+ model: "gpt-5.5",
+ provider: {
+ type: "openai",
+ baseUrl: `${process.env.FOUNDRY_RESOURCE_URL}/openai/v1/`,
+ bearerToken: tokenResponse.token,
+ wireApi: "responses",
+ },
+});
+
+const response = await session.sendAndWait({ prompt: "Hello!" });
+console.log(response?.data.content);
+
+await client.stop();
+```
+
+{% endcodetab %}
+{% endcodetabs %}
+
### Token refresh for long-running applications
-Bearer tokens expire (typically after ~1 hour). For servers or long-running agents, refresh the token before creating each session:
+Bearer tokens expire (typically after ~1 hour). For servers or long-running agents, refresh the token before creating each session. The following Python example demonstrates this pattern:
+
+
```python
from azure.identity import DefaultAzureCredential
from copilot import CopilotClient
from copilot.session import PermissionHandler, ProviderConfig
-COGNITIVE_SERVICES_SCOPE = "https://cognitiveservices.azure.com/.default"
+SCOPE = "https://ai.azure.com/.default"
class ManagedIdentityCopilotAgent:
- """Copilot agent that refreshes Entra ID tokens for Azure AI Foundry."""
+ """Copilot agent that refreshes Microsoft Entra tokens for Microsoft Foundry."""
- def __init__(self, foundry_url: str, model: str = "gpt-4.1"):
+ def __init__(self, foundry_url: str, model: str = "gpt-5.5"):
self.foundry_url = foundry_url.rstrip("/")
self.model = model
- self.credential = DefaultAzureCredential()
+ self.credential = DefaultAzureCredential(require_envvar=True)
self.client = CopilotClient()
def _get_provider_config(self) -> ProviderConfig:
"""Build a ProviderConfig with a fresh bearer token."""
- token = self.credential.get_token(COGNITIVE_SERVICES_SCOPE).token
+ token = self.credential.get_token(SCOPE).token
return ProviderConfig(
type="openai",
base_url=f"{self.foundry_url}/openai/v1/",
@@ -127,75 +237,12 @@ class ManagedIdentityCopilotAgent:
return response.data.content if response else ""
```
-## Node.js / TypeScript example
-
-
-
-```typescript
-import { DefaultAzureCredential } from "@azure/identity";
-import { CopilotClient } from "@github/copilot-sdk";
-
-const credential = new DefaultAzureCredential();
-const tokenResponse = await credential.getToken(
- "https://cognitiveservices.azure.com/.default"
-);
-
-const client = new CopilotClient();
-
-const session = await client.createSession({
- model: "gpt-4.1",
- provider: {
- type: "openai",
- baseUrl: `${process.env.AZURE_AI_FOUNDRY_RESOURCE_URL}/openai/v1/`,
- bearerToken: tokenResponse.token,
- wireApi: "responses",
- },
-});
-
-const response = await session.sendAndWait({ prompt: "Hello!" });
-console.log(response?.data.content);
-
-await client.stop();
-```
-
-## .NET example
-
-
-
-```csharp
-using Azure.Identity;
-using GitHub.Copilot;
-
-var credential = new DefaultAzureCredential();
-var token = await credential.GetTokenAsync(
- new Azure.Core.TokenRequestContext(
- new[] { "https://cognitiveservices.azure.com/.default" }));
-
-await using var client = new CopilotClient();
-var foundryUrl = Environment.GetEnvironmentVariable("AZURE_AI_FOUNDRY_RESOURCE_URL");
-
-await using var session = await client.CreateSessionAsync(new SessionConfig
-{
- Model = "gpt-4.1",
- Provider = new ProviderConfig
- {
- Type = "openai",
- BaseUrl = $"{foundryUrl!.TrimEnd('/')}/openai/v1/",
- BearerToken = token.Token,
- WireApi = "responses",
- },
-});
-
-var response = await session.SendAndWaitAsync(
- new MessageOptions { Prompt = "Hello from Managed Identity!" });
-Console.WriteLine(response?.Data.Content);
-```
-
## Environment configuration
| Variable | Description | Example |
|----------|-------------|---------|
-| `AZURE_AI_FOUNDRY_RESOURCE_URL` | Your Azure AI Foundry resource URL | `https://myresource.openai.azure.com` |
+| `AZURE_TOKEN_CREDENTIALS` | When running in **Azure**, set it to `ManagedIdentityCredential`. When running **locally**, set it to either `dev` or a developer tool credential name, such as `AzureCliCredential`. | `ManagedIdentityCredential` |
+| `FOUNDRY_RESOURCE_URL` | Your Microsoft Foundry resource URL | `https://.openai.azure.com` |
No API key environment variable is needed—authentication is handled by `DefaultAzureCredential`, which automatically supports:
@@ -204,14 +251,18 @@ No API key environment variable is needed—authentication is handled by `Defaul
* **Environment variables** (`AZURE_CLIENT_ID`, `AZURE_TENANT_ID`, `AZURE_CLIENT_SECRET`): for service principals
* **Workload Identity**: for Kubernetes
-See the [DefaultAzureCredential documentation](https://learn.microsoft.com/python/api/azure-identity/azure.identity.defaultazurecredential) for the full credential chain.
+See the `DefaultAzureCredential` documentation for the full credential chain:
+
+* [.NET](https://aka.ms/azsdk/net/identity/credential-chains#defaultazurecredential-overview)
+* [Python](https://aka.ms/azsdk/python/identity/credential-chains#defaultazurecredential-overview)
+* [TypeScript](https://aka.ms/azsdk/js/identity/credential-chains#defaultazurecredential-overview)
## When to use this pattern
| Scenario | Recommendation |
|----------|----------------|
| Azure-hosted app with Managed Identity | ✅ Use this pattern |
-| App with existing Azure AD service principal | ✅ Use this pattern |
+| App with existing Microsoft Entra service principal | ✅ Use this pattern |
| Local development with `az login` | ✅ Use this pattern |
| Non-Azure environment with static API key | Use [AUTOTITLE](/copilot/how-tos/copilot-sdk/auth/byok) |
| GitHub Copilot subscription available | Use [AUTOTITLE](/copilot/how-tos/copilot-sdk/setup/github-oauth) |
diff --git a/content/copilot/how-tos/copilot-sdk/setup/bundled-cli.md b/content/copilot/how-tos/copilot-sdk/setup/bundled-cli.md
index 059cbd25ee8a..4c9b04c41185 100644
--- a/content/copilot/how-tos/copilot-sdk/setup/bundled-cli.md
+++ b/content/copilot/how-tos/copilot-sdk/setup/bundled-cli.md
@@ -2,9 +2,9 @@
title: Default setup (bundled CLI)
shortTitle: Bundled CLI
intro: >-
- The Node.js, Python, and .NET SDKs include the Copilot CLI as a
- dependency—your app ships with everything it needs, with no extra installation
- or configuration required.
+ The Node.js and .NET SDKs include the Copilot CLI as a dependency—your app
+ ships with everything it needs, with no extra installation or configuration
+ required.
versions:
fpt: '*'
ghec: '*'
@@ -16,11 +16,19 @@ contentType: how-tos
+The Python SDK recommends a one-time download step after installation:
+
+```bash
+python -m copilot download-runtime
+```
+
+This downloads the matching runtime and caches it locally. If you skip this step, the SDK will attempt to download it automatically on first use as a fallback.
+
**Best for:** Most applications—desktop apps, standalone tools, CLI utilities, prototypes, and more.
## How it works
-When you install the SDK, the Copilot CLI binary is included automatically. The SDK starts it as a child process and communicates over stdio. There's nothing extra to configure.
+When you install the SDK, the Copilot runtime is included automatically (Node.js, .NET) or downloaded via `python -m copilot download-runtime` (Python). The SDK starts it as a child process and communicates over stdio. There's nothing extra to configure.

diff --git a/content/copilot/how-tos/copilot-sdk/setup/local-cli.md b/content/copilot/how-tos/copilot-sdk/setup/local-cli.md
index 9586703faa53..81adbda1a09e 100644
--- a/content/copilot/how-tos/copilot-sdk/setup/local-cli.md
+++ b/content/copilot/how-tos/copilot-sdk/setup/local-cli.md
@@ -2,9 +2,9 @@
title: Local CLI setup
shortTitle: Local CLI
intro: >-
- Use a specific CLI binary instead of the SDK's bundled CLI. This is an
- advanced option—you supply the CLI path explicitly, and you are responsible
- for ensuring version compatibility with the SDK.
+ Use a specific CLI binary instead of the SDK's automatic CLI management. This
+ is an advanced option—you supply the CLI path explicitly, and you are
+ responsible for ensuring version compatibility with the SDK.
versions:
fpt: '*'
ghec: '*'
@@ -56,7 +56,7 @@ await client.stop();
```python
from copilot import CopilotClient
-from copilot.generated.session_events import AssistantMessageData
+from copilot.session_events import AssistantMessageData
from copilot.session import PermissionHandler
client = CopilotClient({
diff --git a/content/copilot/how-tos/copilot-sdk/setup/multi-tenancy.md b/content/copilot/how-tos/copilot-sdk/setup/multi-tenancy.md
index 3d6e4aedccc0..9fbe6d3870c3 100644
--- a/content/copilot/how-tos/copilot-sdk/setup/multi-tenancy.md
+++ b/content/copilot/how-tos/copilot-sdk/setup/multi-tenancy.md
@@ -2,8 +2,12 @@
title: Multi-tenancy and server deployments
shortTitle: Multi Tenancy
intro: >-
- Run the Copilot SDK in multi-user server deployments with per-session
- isolation for state, authentication, and tools.
+ Multi-user server mode means running the Copilot SDK from backend code that
+ serves more than one human, tenant, workspace, or integration account. In this
+ setup, the application owns request routing and authorization, while the SDK
+ and runtime provide per-session state, per-session authentication, and
+ explicit tool registration so one user's session does not inherit another
+ user's tools or identity.
versions:
fpt: '*'
ghec: '*'
@@ -112,7 +116,7 @@ func main() {
runtimeInstanceID := "instance-1"
runtimeURL := "http://127.0.0.1:8080"
requestID := "req-1"
- user := appUser{ID: "alice", GitHubToken: "YOUR_GITHUB_TOKEN"}
+ user := appUser{ID: "alice", GitHubToken: "gho_xxx"}
client := copilot.NewClient(&copilot.ClientOptions{
Mode: copilot.ModeEmpty,
@@ -157,7 +161,7 @@ using GitHub.Copilot;
var runtimeInstanceId = "instance-1";
var runtimeUrl = "http://127.0.0.1:8080";
var requestId = "req-1";
-var user = new { Id = "alice", GitHubToken = "YOUR_GITHUB_TOKEN" };
+var user = new { Id = "alice", GitHubToken = "gho_xxx" };
var client = new CopilotClient(new CopilotClientOptions
{
diff --git a/content/copilot/how-tos/copilot-sdk/troubleshooting/compatibility.md b/content/copilot/how-tos/copilot-sdk/troubleshooting/compatibility.md
index 5eed74af1409..60cd2712584f 100644
--- a/content/copilot/how-tos/copilot-sdk/troubleshooting/compatibility.md
+++ b/content/copilot/how-tos/copilot-sdk/troubleshooting/compatibility.md
@@ -167,7 +167,9 @@ The Copilot SDK communicates with the CLI via JSON-RPC protocol. Features must b
| **Development** | | |
| Toggle experimental | `/experimental`, `--experimental` | Runtime flag |
| Custom instructions control | `--no-custom-instructions` | CLI flag |
+| Diagnose session | `/diagnose` | TUI command |
| View/manage instructions | `/instructions` | TUI command |
+| Collect debug logs | `/collect-debug-logs` | Diagnostic tool |
| Reindex workspace | `/reindex` | TUI command |
| IDE integration | `/ide` | IDE-specific workflow |
| **Non-interactive Mode** | | |
diff --git a/content/copilot/reference/ai-models/model-hosting.md b/content/copilot/reference/ai-models/model-hosting.md
index 8b537ba1b772..c6a859e065e3 100644
--- a/content/copilot/reference/ai-models/model-hosting.md
+++ b/content/copilot/reference/ai-models/model-hosting.md
@@ -50,6 +50,7 @@ Used for:
* {% data variables.copilot.copilot_claude_haiku_45 %}
* {% data variables.copilot.copilot_claude_sonnet_45 %}
* {% data variables.copilot.copilot_claude_sonnet_46 %}
+* {% data variables.copilot.copilot_claude_sonnet_5 %}
* {% data variables.copilot.copilot_claude_opus_45 %}
* {% data variables.copilot.copilot_claude_opus_46 %}
* {% data variables.copilot.copilot_claude_opus_47 %}
diff --git a/content/copilot/reference/ai-models/supported-models.md b/content/copilot/reference/ai-models/supported-models.md
index 470ee888960b..2672b81cbd49 100644
--- a/content/copilot/reference/ai-models/supported-models.md
+++ b/content/copilot/reference/ai-models/supported-models.md
@@ -83,6 +83,7 @@ Choosing a larger context window or higher reasoning will impact {% data variabl
| {% data variables.copilot.copilot_claude_opus_46 %} | {% octicon "check" aria-label="Supported" %} | {% octicon "check" aria-label="Supported" %} |
| {% data variables.copilot.copilot_claude_opus_47 %} | {% octicon "check" aria-label="Supported" %} | {% octicon "check" aria-label="Supported" %} |
| {% data variables.copilot.copilot_claude_opus_48 %} | {% octicon "check" aria-label="Supported" %} | {% octicon "check" aria-label="Supported" %} |
+| {% data variables.copilot.copilot_claude_sonnet_5 %} | {% octicon "check" aria-label="Supported" %} | {% octicon "check" aria-label="Supported" %} |
| {% data variables.copilot.copilot_claude_opus_48_fast %} | {% octicon "x" aria-label="Not supported" %} | {% octicon "check" aria-label="Supported" %} |
| {% data variables.copilot.copilot_claude_fable_5 %} | {% octicon "check" aria-label="Supported" %} | {% octicon "check" aria-label="Supported" %} |
| {% data variables.copilot.copilot_gpt_53_codex %} | {% octicon "check" aria-label="Supported" %} | {% octicon "check" aria-label="Supported" %} |
@@ -141,8 +142,9 @@ Some {% data variables.product.prodname_copilot_short %} models require minimum
| {% data variables.copilot.copilot_gpt_54_mini %} | `v1.104.1` and later | `17.14.19` and later | `1.5.66` and later | `0.47.0` and later | `0.15.0` and later |
| {% data variables.copilot.copilot_gpt_55 %} | `v1.117` and later | `17.14.19` and later | `1.5.66` and later | `0.47.0` and later | `0.15.0` and later |
| {% data variables.copilot.copilot_claude_opus_48 %} | `v1.118` and later | `17.14.6` and later | TBD | TBD | TBD |
+| {% data variables.copilot.copilot_claude_sonnet_5 %} | `v1.124` and later | `17.14.6` and later | TBD | TBD | TBD |
| {% data variables.copilot.copilot_claude_fable_5 %} | `v1.124` and later | `17.14.6` and later | TBD | TBD | TBD |
-| {% data variables.copilot.copilot_mai_code_1_flash %} | `v1.121` and later | Not available | Not available | Not available | Not available |
+| {% data variables.copilot.copilot_mai_code_1_flash %} | `v1.121` and later | TBD | TBD | TBD | TBD |
{% endrowheaders %}
diff --git a/content/copilot/reference/copilot-billing/license-changes.md b/content/copilot/reference/copilot-billing/license-changes.md
index 310aea2fde05..9996b11550ea 100644
--- a/content/copilot/reference/copilot-billing/license-changes.md
+++ b/content/copilot/reference/copilot-billing/license-changes.md
@@ -30,6 +30,14 @@ What you need to know about the following actions:
* **Upgrading:** If you upgrade your plan (for example, from {% data variables.copilot.copilot_pro_short %} to {% data variables.copilot.copilot_pro_plus_short %}), the change is **immediate**. You are charged a prorated amount for the new plan.
* **Downgrading/canceling:** Access remains until the end of the current billing cycle. **No refund for unused time**.
+### Included monthly allowance reset
+
+Paying for, renewing, upgrading, downgrading, converting from a trial, or resuming a plan after a lapse does not grant a fresh {% data variables.product.prodname_ai_credits_short %} allowance immediately. Your included monthly allowance resets at 00:00:00 UTC on the first day of each calendar month, regardless of your subscription billing date.
+
+For example, if you exhaust your {% data variables.product.prodname_ai_credits_short %} on May 28 and renew or upgrade your plan on May 30, your allowance does not reset until June 1.
+
+Any additional usage beyond the included allowance is charged separately and is unaffected by this monthly reset. See [AUTOTITLE](/copilot/concepts/billing/usage-based-billing-for-individuals) and [AUTOTITLE](/billing/how-tos/set-up-budgets#managing-budgets-for-your-personal-account).
+
## Organizations
What you need to know about the following actions:
diff --git a/content/copilot/reference/copilot-billing/models-and-pricing.md b/content/copilot/reference/copilot-billing/models-and-pricing.md
index 87b5c0f23e7b..63b94d004f25 100644
--- a/content/copilot/reference/copilot-billing/models-and-pricing.md
+++ b/content/copilot/reference/copilot-billing/models-and-pricing.md
@@ -97,3 +97,5 @@ You can view your current {% data variables.product.prodname_actions %} usage fo
## Model multipliers for annual {% data variables.copilot.copilot_pro_short %} and {% data variables.copilot.copilot_pro_plus_short %} subscribers
{% data variables.copilot.copilot_pro_short %} and {% data variables.copilot.copilot_pro_plus_short %} subscribers on **existing annual billing plans** using the **request-based billing** model have different model multipliers. See [AUTOTITLE](/copilot/reference/copilot-billing/model-multipliers-for-annual-plans).
+
+[^sonnet-5-promo]: {% data variables.copilot.copilot_claude_sonnet_5 %} is available at the promotional pricing of $2.00 per 1M input tokens, $0.20 per 1M cached input tokens, $2.50 per 1M cache write tokens, and $10.00 per 1M output tokens through August 31, 2026.
diff --git a/content/copilot/reference/copilot-cli-reference/cli-command-reference.md b/content/copilot/reference/copilot-cli-reference/cli-command-reference.md
index 35c4b1785c1e..efbafcfe7c66 100644
--- a/content/copilot/reference/copilot-cli-reference/cli-command-reference.md
+++ b/content/copilot/reference/copilot-cli-reference/cli-command-reference.md
@@ -26,6 +26,7 @@ docsTeamMetrics:
| `copilot login` [OPTION] | Authenticate with {% data variables.product.prodname_copilot_short %} via the OAuth device flow. See [`copilot login` options](#copilot-login-options). |
| `copilot mcp` | Manage MCP server configurations from the command line. |
| `copilot plugin` | Manage plugins and plugin marketplaces. |
+| `copilot skill` | Manage agent skills from the command line (list, add, and remove skills). See [AUTOTITLE](/copilot/how-tos/copilot-cli/customize-copilot/add-skills). |
| `copilot update` | Download and install the latest version. |
| `copilot version` | Display version information and check for updates. |
diff --git a/content/copilot/reference/copilot-usage-metrics/copilot-usage-metrics.md b/content/copilot/reference/copilot-usage-metrics/copilot-usage-metrics.md
index e8358e457d31..a8f8bae09aa2 100644
--- a/content/copilot/reference/copilot-usage-metrics/copilot-usage-metrics.md
+++ b/content/copilot/reference/copilot-usage-metrics/copilot-usage-metrics.md
@@ -67,7 +67,7 @@ These fields appear in the exported NDJSON reports and in the {% data variables.
Reports come in different shapes depending on their scope and granularity, so the fields available in a record depend on which report it comes from:
-* **Per-user reports** (`*-users-1-day` and `*-users-28-day`) contain one record per user, including `user_id`, `user_login`, the `used_*` indicators, and `ai_adoption_phase`. They do not contain active-user counts, `pull_requests`, or `totals_by_ai_adoption_phase`.
+* **Per-user reports** (`*-users-1-day` and `*-users-28-day`) contain one record per user, including `user_id`, `user_login`, `ai_credits_used`, the `used_*` indicators, and `ai_adoption_phase`. They do not contain active-user counts, `pull_requests`, or `totals_by_ai_adoption_phase`.
* **Aggregated reports** (`enterprise-1-day` and `org-1-day`) contain one aggregated record per enterprise or organization, including active-user counts, `pull_requests`, and `totals_by_ai_adoption_phase`. They do not contain `user_id`, `user_login`, or the `used_*` indicators.
* **28-day reports** (`enterprise-28-day` and `org-28-day`) wrap an array of daily aggregated records in a `day_totals` field, with the reporting window at the top level.
* **User-teams reports** (`*-user-teams-1-day`) map users to the teams they belong to, so you can construct team-level metrics.
@@ -96,6 +96,7 @@ Per-user reports contain one record per user for the reporting period. The 28-da
|:--|:--|:--|:--|
| `user_id` | `integer` | No | Unique identifier for the user. |
| `user_login` | `string` | No | {% data variables.product.github %} username for the user. |
+| `ai_credits_used` | `number` | No | Total AI credits consumed by the user in the reporting period. This field is included in per-user reports only and is not broken down by feature, model, or surface. This metric is for consumption analysis, not invoicing totals. |
| `user_initiated_interaction_count` | `integer` | No | Number of explicit prompts sent to {% data variables.product.prodname_copilot_short %}.
Only counts messages or prompts actively sent to the model. Does **not** include opening the chat panel, switching modes (for example, ask, edit, plan, or agent), using keyboard shortcuts to open the inline UI, or making configuration changes. |
| `code_generation_activity_count` | `integer` | No | Number of distinct {% data variables.product.prodname_copilot_short %} output events generated.
**Includes:** All generated content, including comments and docstrings. **Multiple blocks:** Each distinct code block from a single user prompt counts as a separate generation. **Note:** This metric is not directly comparable to `user_initiated_interaction_count`, since one prompt can produce multiple generations. |
| `code_acceptance_activity_count` | `integer` | No | Number of suggestions or code blocks accepted by users.
**Counts:** All built-in accept actions, such as “apply to file,” “insert at cursor,” “insert into terminal,” and use of the **Copy** button. **Does not count:** Manual OS clipboard actions (for example, Ctrl+C). **Granularity:** Each acceptance action increments the count once, regardless of how many code blocks were generated by the initial prompt. |
diff --git a/content/copilot/reference/copilot-usage-metrics/example-schema.md b/content/copilot/reference/copilot-usage-metrics/example-schema.md
index 2e7db8049773..f4050a182336 100644
--- a/content/copilot/reference/copilot-usage-metrics/example-schema.md
+++ b/content/copilot/reference/copilot-usage-metrics/example-schema.md
@@ -19,6 +19,7 @@ The following are example schemas for the user-level and enterprise-level data r
```json copy
[{
+ "ai_credits_used": 12.5,
"code_acceptance_activity_count": 1,
"code_generation_activity_count": 1,
"day": "2025-10-01",
diff --git a/content/migrations/elm/about-live-migrations.md b/content/migrations/elm/about-live-migrations.md
index 4c149e55dbbf..906316c44faa 100644
--- a/content/migrations/elm/about-live-migrations.md
+++ b/content/migrations/elm/about-live-migrations.md
@@ -7,7 +7,6 @@ versions:
ghes: '*'
ghec: '*'
contentType: concepts
-product: '{% data reusables.elm.ghes-version-requirement %}'
---
{% data reusables.elm.preview-note %}
@@ -43,7 +42,7 @@ The high-level phases of a migration are:
1. **Creation**: The site admin runs CLI commands to create and start the migration, specifying the source repository and destination.
1. **Preflight checks**: The migration service verifies parameters, tokens, network connectivity, and repository configuration.
1. **Backfill**: The {% data variables.product.prodname_elm_short %} tool does an initial crawl to capture all repository data and sends it to the migration service on the destination platform. During the backfill phase, webhooks check for live updates to the repository as the migration continues.
-1. **Cutover**: The source repository is locked and any final live updates are sent to {% data variables.product.prodname_elm_short %}. This is the downtime period for developers.
+1. **Cutover**: The source repository is archived (made read-only) and any final live updates are sent to {% data variables.product.prodname_elm_short %}. This is the downtime period for developers.
1. **Completion**: The migration is finished. The site admin can check the data was migrated successfully.
1. **Follow-up**: An organization owner performs follow-up tasks on the destination enterprise, such as reconfiguring organization settings and reattributing activity to users.
diff --git a/content/migrations/elm/elm-cli-reference.md b/content/migrations/elm/elm-cli-reference.md
index b74b873f85b6..fa2ab60bc75f 100644
--- a/content/migrations/elm/elm-cli-reference.md
+++ b/content/migrations/elm/elm-cli-reference.md
@@ -20,7 +20,7 @@ contentType: reference
| `elm migration status --migration-id MIGRATION-ID` | Shows the status, progress, cutover readiness, and timing of a migration |
| `elm migration list` | Lists all migrations and their statuses |
| `elm migration cancel --migration-id MIGRATION-ID` | Cancels a migration in progress |
-| `elm migration cutover-to-destination --migration-id MIGRATION-ID` | Initiates the final cutover, locking the source repository and completing the migration |
+| `elm migration cutover-to-destination --migration-id MIGRATION-ID` | Initiates the final cutover, archiving the source repository and completing the migration |
Some of these commands can take additional options. See the later sections in this article.
diff --git a/content/migrations/elm/migrate-your-repository.md b/content/migrations/elm/migrate-your-repository.md
index a9441a56bbe8..10ba3bf63e90 100644
--- a/content/migrations/elm/migrate-your-repository.md
+++ b/content/migrations/elm/migrate-your-repository.md
@@ -7,7 +7,6 @@ versions:
ghes: '*'
ghec: '*'
contentType: how-tos
-product: '{% data reusables.elm.ghes-version-requirement %}'
permissions: 'Site administrators on {% data variables.product.prodname_ghe_server %} who are also enterprise owners on {% data variables.enterprise.data_residency_site %}.'
---
@@ -60,18 +59,7 @@ You must set some configuration on the {% data variables.product.prodname_ghe_se
| `secrets.elm-exporter.migration-target-token` | The access token you created for {% data variables.enterprise.data_residency_site %}. |
| `secrets.elm-exporter.source-token` | The access token you created for {% data variables.product.prodname_ghe_server %}. |
| `secrets.elm-exporter.source-user` | The username associated with the {% data variables.product.prodname_ghe_server %} token (for example: `ghe-admin`). |
-
-1. If you **don't** already have migrations enabled and blob storage configured on the instance, you can configure them now. You can check your existing settings in in "Migrations" section of the Management Console (`HOSTNAME/setup/settings`).
-
- You can use the following default values, which will not introduce any unexpected functionality.
-
- ```shell copy
- ghe-config app.migrations.enabled true
- ```
-
- ```shell copy
- ghe-config secrets.migrations.blob-storage-type local-storage
- ```
+ | `app.migrations.enabled` | If you don't already have migrations enabled on the instance, you must set this to `true`. |
1. Apply the configuration.
@@ -194,7 +182,7 @@ Tips:
## 7. Complete the migration
-When a migration is ready for cutover, you can complete the migration. The cutover process will lock the source repository, making it **permanently unavailable** for developers unless an administrator unlocks it.
+When a migration is ready for cutover, you can complete the migration. The cutover process will archive the source repository, making it **permanently read-only** unless a repository administrator unarchives it.
``` shell copy
elm migration cutover-to-destination --migration-id $MIGRATION_ID
diff --git a/content/migrations/elm/prepare-for-your-migration.md b/content/migrations/elm/prepare-for-your-migration.md
index 86d4eb38e42b..afafef08806d 100644
--- a/content/migrations/elm/prepare-for-your-migration.md
+++ b/content/migrations/elm/prepare-for-your-migration.md
@@ -13,18 +13,22 @@ contentType: concepts
## Is our {% data variables.product.prodname_ghe_server %} instance ready?
-{% data variables.product.prodname_elm_short %} has been backported to supported releases. To use it, you must upgrade to one of the following minor versions or later:
+{% data variables.product.prodname_elm_short %} is available in the latest patch releases for {% data variables.product.prodname_ghe_server %} 3.17 and later. This documentation assumes you are using the following patch version or later. The instructions may not work on earlier versions.
-* `3.21.0`
-* `3.20.2`
-* `3.19.6`
-* `3.18.9`
-* `3.17.15`
+
+
+* `3.21.2`
+* `3.20.4`
+* `3.19.8`
+* `3.18.11`
+* `3.17.17`
Your {% data variables.product.prodname_ghe_server %} instance must also:
* Use an **HTTPS** URL. HTTP URLs are not supported.
-* Have migrations enabled and blob storage configured. You can check these settings in the "Migrations" section of the Management Console. If you don't already have these settings configured, we will explain how to set them to default values in [AUTOTITLE](/migrations/elm/migrate-your-repository).
+* Allow outbound traffic to the destination of the migration.
+* Have migrations enabled in the "Migrations" section of the Management Console.
+* Be prepared for some additional load during the migration: repository archiving causes all issues and pull requests in the repository to be pulled from MySQL and reindexed in Elasticsearch.
## What will our destination organization look like?
@@ -65,7 +69,7 @@ After the migration, someone will need to perform some follow-up tasks on {% dat
Before you start, communicate with developers that:
-* The repository is moving to a new location. Users can continue to use the source repository during the migration until the operator begins the final cutover to the new location.
+* The repository is moving to a new location. Users can continue to use the source repository during the migration until the operator begins the final cutover to the new location. After cutover, the source repository will be archived, so it will be read-only unless it is unarchived by a repository administrator.
* While the migration is in progress, developers should avoid force pushes to the repository, because these will disrupt the Git history in a way that {% data variables.product.prodname_elm_short %} cannot resolve.
* Certain actions that developers perform during the migration process may not be reflected in the migrated repository. For details, see the unsupported actions in [AUTOTITLE](/migrations/elm/migrated-data-reference#events-included-in-live-updates).
diff --git a/content/migrations/elm/troubleshooting.md b/content/migrations/elm/troubleshooting.md
index 6448dd0cfadd..36d1afba4fad 100644
--- a/content/migrations/elm/troubleshooting.md
+++ b/content/migrations/elm/troubleshooting.md
@@ -22,7 +22,7 @@ If your migration encounters a problem, check the migration status with `elm mig
| **Exporting** | Data is being exported from the source | Monitor with `elm migration status` |
| **Processing** | Exported data is being imported to the destination | Monitor with `elm migration status` |
| **Ready for cutover** | The initial migration is complete and the migration is ready for cutover | When ready, run `elm migration cutover-to-destination` |
-| **Cutting over** | The source repository is locked and remaining changes are being applied to the destination | Monitor; the status will transition to **Completed** |
+| **Cutting over** | The source repository is archived and remaining changes are being applied to the destination | Monitor; the status will transition to **Completed** |
| **Completed** | The migration has finished successfully | Verify the destination repository and reclaim mannequins |
| **Failed** | The migration encountered an unrecoverable failure | Investigate the error (see below) |
| **Paused** | The migration is paused | Check the pause reason and resolve (see below) |
@@ -95,7 +95,7 @@ Failed resources are only shown after all automatic retries have been exhausted,
If the number and types of failed resources are acceptable, you can proceed with cutover. If not, abort the migration, resolve the underlying issue, then start a new migration.
-## Cutover failed and the source repository is locked
+## Cutover failed and the source repository is unavailable
{% data reusables.elm.locked-repo %}
diff --git a/content/migrations/overview/about-locked-repositories.md b/content/migrations/overview/about-locked-repositories.md
index b964b610d8dc..00e84121c0e1 100644
--- a/content/migrations/overview/about-locked-repositories.md
+++ b/content/migrations/overview/about-locked-repositories.md
@@ -35,7 +35,9 @@ While a migration is in progress, access to the destination repository is locked
For information about how to unlock repositories that were locked by {% data variables.product.prodname_importer_proper_name %}, see [AUTOTITLE](/migrations/using-github-enterprise-importer/completing-your-migration-with-github-enterprise-importer/troubleshooting-your-migration-with-github-enterprise-importer#locked-repositories).
-## Repositories locked by {% data variables.product.prodname_elm %}
+## Repositories archived by {% data variables.product.prodname_elm %}
+
+In the latest {% data variables.product.prodname_ghe_server %} releases, {% data variables.product.prodname_elm %} archives, rather than locks, the source repository. This keeps the repository available for read operations.
{% data reusables.elm.locked-repo %}
diff --git a/data/release-notes/enterprise-server/3-17/17.yml b/data/release-notes/enterprise-server/3-17/17.yml
new file mode 100644
index 000000000000..2d928414e322
--- /dev/null
+++ b/data/release-notes/enterprise-server/3-17/17.yml
@@ -0,0 +1,90 @@
+date: '2026-06-30'
+sections:
+ security_fixes:
+ - |
+ **HIGH**: Current configurations of the GitHub API access controls could allow an attacker to create issues in any public repository via a u2s token without requiring the underlying installation to have issues write permission. This therefore allows an attacker to impersonate the victim in public repositories by creating issues and commit comments.
+ - |
+ **MEDIUM**: An attacker with site administrator privileges could extract arbitrary data from the instance's database, including user password hashes, by exploiting a blind SQL injection vulnerability in the `dependenciesPrefers` argument of the `dependencyGraphManifests` GraphQL field. This vulnerability affected instances with the dependency graph enabled and was reported via the GitHub Bug Bounty program.
+ - |
+ **MEDIUM**: An attacker could gain unintended access to an organization's runner management by directing a user to authorize an OAuth app whose requested manage_runners:org scope was not displayed on the authorization consent screen. GitHub has requested CVE ID [CVE-2026-9106](https://www.cve.org/cverecord?id=CVE-2026-9106) for this vulnerability, which was reported via the [GitHub Bug Bounty](https://bounty.github.com/) program.
+ - |
+ **MEDIUM**: An authenticated GHES user could read source code from private repositories they did not have access to by supplying a cross-repository comparison range to the Copilot pull request description diff summary endpoint, which did not verify the user's permission to view the target repository. GitHub has requested CVE ID [CVE-2026-9132](https://www.cve.org/cverecord?id=CVE-2026-9132) for this vulnerability, which was reported via the [GitHub Bug Bounty program](https://bounty.github.com/).
+ - |
+ **MEDIUM**: An attacker could execute arbitrary JavaScript in a victim's browser on a GitHub Enterprise Server instance by creating a discussion in the Q\&A category with a crafted title that breaks out of the JSON-LD structured-data script block when a comment is marked as the answer. To mitigate this issue, GitHub has updated the rendering of JSON-LD structured data in discussions to properly escape user-controlled input. GitHub has requested CVE ID [CVE-2026-10585](https://www.cve.org/cverecord?id=CVE-2026-10585) for this vulnerability, which was reported via the [GitHub Bug Bounty](https://bounty.github.com/) program.
+ - |
+ GitHub has updated `dnsmasq` to fix the following vulnerabilities: CVE-2026-4891, CVE-2026-4890, CVE-2026-4892, CVE-2026-4893, CVE-2026-5172, CVE-2026-2291.
+ - |
+ Packages have been updated to the latest security versions.
+ bugs:
+ - |
+ After administrators updated a GitHub Enterprise Server instance's TLS certificate, GitHub Pages deployments failed because the necessary services did not restart when `ghe-config-apply` was run.
+ - |
+ Administrators could not generate support bundles on stateless high availability nodes because the `ghe-support-bundle` command failed when attempting to query Elasticsearch on nodes without the `elasticsearch-server` role.
+ - |
+ On an instance with Enterprise Live Migrations enabled, running `/usr/local/bin/elm` without arguments dropped into a degraded container shell where standard administrative commands were unavailable.
+ - |
+ The scheduled job that repairs search indexes could run long enough that its next execution interrupted the in-progress repair, causing the process to restart before completing. The job used a locking mechanism to prevent overlapping runs and was scheduled to run once per month, on the first Saturday, instead of weekly.
+ - |
+ On instances with multiple data disks configured, administrators who ran `ghe-restore` encountered an error message: `Runtime error (func=(main), adr=10): Divide by zero`.
+ - |
+ For administrators using the Backup Service to back up a high availability (HA) appliance from the replica with GitHub Actions enabled, MSSQL backups would fail with the error `This command can only be run on the primary mssql node.`
+ - |
+ When running `ghe-backup` on an instance configured with the Backup Service, the `data` directory inside the backups mountpoint (usually `/data/backup/data`) failed to be automatically created due to missing permissions.
+ - |
+ Users who created custom patterns for secret scanning could bypass the restriction on unbounded wildcards by wrapping them in capture groups, which could cause performance degradation during scans.
+ changes:
+ - |
+ To reduce response times for repositories with a large number of code scanning alerts, GitHub Enterprise Server uses an optimized query when counting alerts.
+ - |
+ Site administrators can include Enterprise Live Migrations (ELM) Exporter appliance data in scheduled backups using GitHub Enterprise Server Backup Service. For more information, see [AUTOTITLE](/admin/backing-up-and-restoring-your-instance).
+ - |
+ Site administrators can restore Enterprise Live Migrations (ELM) Exporter appliance data using GitHub Enterprise Server Backup Service. For more information, see [AUTOTITLE](/admin/backing-up-and-restoring-your-instance).
+ known_issues:
+ - |
+ During an upgrade of GitHub Enterprise Server, custom firewall rules are removed. If you use custom firewall rules, you must reapply them after upgrading.
+ - |
+ During the validation phase of a configuration run, a `No such object` error may occur for the Notebook and Viewscreen services. This error can be ignored as the services should still correctly start.
+ - |
+ If the root site administrator is locked out of the Management Console after failed login attempts, the account does not unlock automatically after the defined lockout time. Someone with administrative SSH access to the instance must unlock the account using the administrative shell. For more information, see [Troubleshooting access to the Management Console](/enterprise-server@latest/admin/administering-your-instance/administering-your-instance-from-the-web-ui/troubleshooting-access-to-the-management-console#unlocking-the-root-site-administrator-account).
+ - |
+ On an instance with the HTTP `X-Forwarded-For` header configured for use behind a load balancer, all client IP addresses in the instance's audit log erroneously appear as 127.0.0.1.
+ - |
+ {% data reusables.release-notes.large-adoc-files-issue %}
+ - |
+ Admin stats REST API endpoints may time out on appliances with many users or repositories. Retrying the request until data is returned is advised.
+ - |
+ When following the steps for [Replacing the primary MySQL node](/admin/monitoring-managing-and-updating-your-instance/configuring-clustering/replacing-a-cluster-node#replacing-the-primary-mysql-node), step 14 (running `ghe-cluster-config-apply`) might fail with errors. If this occurs, re-running `ghe-cluster-config-apply` is expected to succeed.
+ - |
+ Running a config apply as part of the steps for [Replacing a node in an emergency](/admin/monitoring-managing-and-updating-your-instance/configuring-clustering/replacing-a-cluster-node#replacing-a-node-in-an-emergency) may fail with errors if the node being replaced is still reachable. If this occurs, shutdown the node and repeat the steps.
+ - |
+ {% data reusables.release-notes.2024-06-possible-frontend-5-minute-outage-during-hotpatch-upgrade %}
+ - |
+ When restoring data originally backed up from a 3.13 or later appliance version, the Elasticsearch indices need to be reindexed before some of the data will show up. This happens via a nightly scheduled job. It can also be forced by running `/usr/local/share/enterprise/ghe-es-search-repair`.
+ - |
+ An organization-level code scanning configuration page is displayed on instances that do not use GitHub Advanced Security or code scanning.
+ - |
+ When enabling automatic update checks for the first time in the Management Console, the status is not dynamically reflected until the "Updates" page is reloaded.
+ - |
+ When restoring from a backup snapshot, a large number of `mapper_parsing_exception` errors may be displayed.
+ - |
+ When initializing a new GHES cluster, nodes with the `consul-server` role should be added to the cluster before adding additional nodes. Adding all nodes simultaneously creates a race condition between nomad server registration and nomad client registration.
+ - |
+ When initializing a new GHES cluster, nodes with the `consul-server` role should be added to the cluster before adding additional nodes. Adding all nodes simultaneously creates a race condition between nomad server registration and nomad client registration.
+ - |
+ In a cluster, the host running restore requires access to the storage nodes via their private IPs.
+ - |
+ On an instance hosted on Azure, commenting on an issue via email meant the comment was not added to the issue.
+ - |
+ After a restore, existing outside collaborators cannot be added to repositories in a new organization. This issue can be resolved by running `/usr/local/share/enterprise/ghe-es-search-repair` on the appliance.
+ - |
+ After a geo-replica is promoted to be a primary by running `ghe-repl-promote`, the GitHub Actions workflow of a repository does not have any suggested workflows.
+ - |
+ Unexpected elements may appear in the UI on the repository overview page for locked repositories.
+ - |
+ When publishing npm packages in a workflow after restoring from a backup to GitHub Enterprise Server 3.13.5.gm4 or 3.14.2.gm3, you may encounter a `401 Unauthorized` error from the GitHub Packages service. This can happen if the restore is from an N-1 or N-2 version and the workflow targets the npm endpoint on the backup instance. To avoid this issue, ensure the access token is valid and includes the correct scopes for publishing to GitHub Packages.
+ - |
+ When applying an enterprise security configuration to all repositories (for example, enabling Secret Scanning or Code Scanning across all repositories), the system immediately enqueues enablement jobs for every organization in the enterprise simultaneously. For enterprises with a large number of repositories, this can result in significant system load and potential performance degradation. If you manage a large enterprise with many organizations and repositories, we recommend applying security configurations at the organization level rather than at the enterprise level in the UI. This allows you to enable security features incrementally and monitor system performance as you roll out changes.
+ - |
+ Git versions are mismatched between containers on the instance.
+ - |
+ Administrators upgrading to certain patch releases encountered a `Failed to generate an OIDC token` error during the `Update Servicing Resources` step when GitHub Actions was configured with Google Cloud Storage (GCS) or AWS S3 using OpenID Connect (OIDC) authentication. The upgrade was blocked and could not complete. To work around this issue, administrators could apply a manual patch by running `sudo sed -i.bak 's|ghe-actions-console -s -c Update-Service|ghe-actions-console -s --no-blob-creds -c Update-Service|g' /usr/local/bin/ghe-actions-update` followed by `ghe-config-apply`.
diff --git a/data/release-notes/enterprise-server/3-18/11.yml b/data/release-notes/enterprise-server/3-18/11.yml
new file mode 100644
index 000000000000..2e3325559cf8
--- /dev/null
+++ b/data/release-notes/enterprise-server/3-18/11.yml
@@ -0,0 +1,94 @@
+date: '2026-06-30'
+sections:
+ security_fixes:
+ - |
+ **HIGH**: Current configurations of the GitHub API access controls could allow an attacker to create issues in any public repository via a u2s token without requiring the underlying installation to have issues write permission. This therefore allows an attacker to impersonate the victim in public repositories by creating issues and commit comments.
+ - |
+ **MEDIUM**: An attacker with site administrator privileges could extract arbitrary data from the instance's database, including user password hashes, by exploiting a blind SQL injection vulnerability in the `dependenciesPrefers` argument of the `dependencyGraphManifests` GraphQL field. This vulnerability affected instances with the dependency graph enabled and was reported via the GitHub Bug Bounty program.
+ - |
+ **MEDIUM**: An attacker could gain unintended access to an organization's runner management by directing a user to authorize an OAuth app whose requested manage_runners:org scope was not displayed on the authorization consent screen. GitHub has requested CVE ID [CVE-2026-9106](https://www.cve.org/cverecord?id=CVE-2026-9106) for this vulnerability, which was reported via the [GitHub Bug Bounty](https://bounty.github.com/) program.
+ - |
+ **MEDIUM**: An authenticated GHES user could read source code from private repositories they did not have access to by supplying a cross-repository comparison range to the Copilot pull request description diff summary endpoint, which did not verify the user's permission to view the target repository. GitHub has requested CVE ID [CVE-2026-9132](https://www.cve.org/cverecord?id=CVE-2026-9132) for this vulnerability, which was reported via the [GitHub Bug Bounty program](https://bounty.github.com/).
+ - |
+ **MEDIUM**: An attacker could execute arbitrary JavaScript in a victim's browser on a GitHub Enterprise Server instance by creating a discussion in the Q\&A category with a crafted title that breaks out of the JSON-LD structured-data script block when a comment is marked as the answer. To mitigate this issue, GitHub has updated the rendering of JSON-LD structured data in discussions to properly escape user-controlled input. GitHub has requested CVE ID [CVE-2026-10585](https://www.cve.org/cverecord?id=CVE-2026-10585) for this vulnerability, which was reported via the [GitHub Bug Bounty](https://bounty.github.com/) program.
+ - |
+ GitHub has updated `dnsmasq` to fix the following vulnerabilities: CVE-2026-4891, CVE-2026-4890, CVE-2026-4892, CVE-2026-4893, CVE-2026-5172, CVE-2026-2291.
+ - |
+ Packages have been updated to the latest security versions.
+ bugs:
+ - |
+ Administrators experienced "Permission denied" errors when the `ghes-manage-agent` attempted to write logs under `/var/log/license-upgrade` during license upgrade restart tasks.
+ - |
+ After administrators updated a GitHub Enterprise Server instance's TLS certificate, GitHub Pages deployments failed because the necessary services did not restart when `ghe-config-apply` was run.
+ - |
+ On an instance with Enterprise Live Migrations enabled, running `/usr/local/bin/elm` without arguments dropped into a degraded container shell where standard administrative commands were unavailable.
+ - |
+ The scheduled job that repairs search indexes could run long enough that its next execution interrupted the in-progress repair, causing the process to restart before completing. The job used a locking mechanism to prevent overlapping runs and was scheduled to run once per month, on the first Saturday, instead of weekly.
+ - |
+ When a site administrator ran `ghe-remove-node` to evacuate and remove a node, orphaned configuration entries remained in node subsections such as `hostname.app.github`.
+ - |
+ On instances with multiple data disks configured, administrators who ran `ghe-restore` encountered an error message: `Runtime error (func=(main), adr=10): Divide by zero`.
+ - |
+ For administrators using the Backup Service to back up a high availability (HA) appliance from the replica with GitHub Actions enabled, MSSQL backups would fail with the error `This command can only be run on the primary mssql node.`
+ - |
+ When running `ghe-backup` on an instance configured with the Backup Service, the `data` directory inside the backups mountpoint (usually `/data/backup/data`) failed to be automatically created due to missing permissions.
+ - |
+ On instances processing webhooks, notifications, or other background events, the `github-stream-processors` Nomad job consumed increasing memory and processed events progressively more slowly the longer it ran, requiring site administrators to restart the job to restore baseline throughput.
+ - |
+ Administrators experienced gradual memory growth in long-running GitHub Enterprise Server background event processor services. Upgrading resets service-mapping context between messages, and administrators do not need to take any action after upgrading.
+ changes:
+ - |
+ To reduce response times for repositories with a large number of code scanning alerts, GitHub Enterprise Server uses an optimized query when counting alerts.
+ - |
+ Site administrators can include Enterprise Live Migrations (ELM) Exporter appliance data in scheduled backups using GitHub Enterprise Server Backup Service. For more information, see [AUTOTITLE](/admin/backing-up-and-restoring-your-instance).
+ - |
+ Site administrators can restore Enterprise Live Migrations (ELM) Exporter appliance data using GitHub Enterprise Server Backup Service. For more information, see [AUTOTITLE](/admin/backing-up-and-restoring-your-instance).
+ known_issues:
+ - |
+ During an upgrade of GitHub Enterprise Server, custom firewall rules are removed. If you use custom firewall rules, you must reapply them after upgrading.
+ - |
+ During the validation phase of a configuration run, a `No such object` error may occur for the Notebook and Viewscreen services. This error can be ignored as the services should still correctly start.
+ - |
+ If the root site administrator is locked out of the Management Console after failed login attempts, the account does not unlock automatically after the defined lockout time. Someone with administrative SSH access to the instance must unlock the account using the administrative shell. For more information, see [Troubleshooting access to the Management Console](/enterprise-server@latest/admin/administering-your-instance/administering-your-instance-from-the-web-ui/troubleshooting-access-to-the-management-console#unlocking-the-root-site-administrator-account).
+ - |
+ On an instance with the HTTP `X-Forwarded-For` header configured for use behind a load balancer, all client IP addresses in the instance's audit log erroneously appear as 127.0.0.1.
+ - |
+ {% data reusables.release-notes.large-adoc-files-issue %}
+ - |
+ Admin stats REST API endpoints may time out on appliances with many users or repositories. Retrying the request until data is returned is advised.
+ - |
+ When following the steps for [Replacing the primary MySQL node](/admin/monitoring-managing-and-updating-your-instance/configuring-clustering/replacing-a-cluster-node#replacing-the-primary-mysql-node), step 14 (running `ghe-cluster-config-apply`) might fail with errors. If this occurs, re-running `ghe-cluster-config-apply` is expected to succeed.
+ - |
+ Running a config apply as part of the steps for [Replacing a node in an emergency](/admin/monitoring-managing-and-updating-your-instance/configuring-clustering/replacing-a-cluster-node#replacing-a-node-in-an-emergency) may fail with errors if the node being replaced is still reachable. If this occurs, shutdown the node and repeat the steps.
+ - |
+ {% data reusables.release-notes.2024-06-possible-frontend-5-minute-outage-during-hotpatch-upgrade %}
+ - |
+ When restoring data originally backed up from a 3.13 or later appliance version, the Elasticsearch indices need to be reindexed before some of the data will show up. This happens via a nightly scheduled job. It can also be forced by running `/usr/local/share/enterprise/ghe-es-search-repair`.
+ - |
+ An organization-level code scanning configuration page is displayed on instances that do not use GitHub Advanced Security or code scanning.
+ - |
+ When enabling automatic update checks for the first time in the Management Console, the status is not dynamically reflected until the "Updates" page is reloaded.
+ - |
+ When restoring from a backup snapshot, a large number of `mapper_parsing_exception` errors may be displayed.
+ - |
+ When initializing a new GHES cluster, nodes with the `consul-server` role should be added to the cluster before adding additional nodes. Adding all nodes simultaneously creates a race condition between nomad server registration and nomad client registration.
+ - |
+ In a cluster, the host running restore requires access the storage nodes via their private IPs.
+ - |
+ On an instance hosted on Azure, commenting on an issue via email meant the comment was not added to the issue.
+ - |
+ After a restore, existing outside collaborators cannot be added to repositories in a new organization. This issue can be resolved by running `/usr/local/share/enterprise/ghe-es-search-repair` on the appliance.
+ - |
+ After a geo-replica is promoted to be a primary by running `ghe-repl-promote`, the GitHub Actions workflow of a repository does not have any suggested workflows.
+ - |
+ Unexpected elements may appear in the UI on the repository overview page for locked repositories.
+ - |
+ When publishing npm packages in a workflow after restoring from a backup to GitHub Enterprise Server 3.13.5.gm4 or 3.14.2.gm3, you may encounter a `401 Unauthorized` error from the GitHub Packages service. This can happen if the restore is from an N-1 or N-2 version and the workflow targets the npm endpoint on the backup instance. To avoid this issue, ensure the access token is valid and includes the correct scopes for publishing to GitHub Packages.
+ - |
+ The setting to define private registries at the organization level for code scanning is only available if Dependabot is also enabled for the instance.
+ - |
+ Custom NTP settings are removed during the upgrade process.
+ - |
+ When applying an enterprise security configuration to all repositories (for example, enabling Secret Scanning or Code Scanning across all repositories), the system immediately enqueues enablement jobs for every organization in the enterprise simultaneously. For enterprises with a large number of repositories, this can result in significant system load and potential performance degradation. If you manage a large enterprise with many organizations and repositories, we recommend applying security configurations at the organization level rather than at the enterprise level in the UI. This allows you to enable security features incrementally and monitor system performance as you roll out changes.
+ - |
+ Administrators upgrading to certain patch releases encountered a "Failed to generate an OIDC token" error during the `Update Servicing Resources` step when GitHub Actions was configured with Google Cloud Storage (GCS) or AWS S3 using OpenID Connect (OIDC) authentication. The upgrade was blocked and could not complete. To work around this issue, administrators could apply a manual patch by running `sudo sed -i.bak 's|ghe-actions-console -s -c Update-Service|ghe-actions-console -s --no-blob-creds -c Update-Service|g' /usr/local/bin/ghe-actions-update` followed by `ghe-config-apply`.
diff --git a/data/release-notes/enterprise-server/3-19/8.yml b/data/release-notes/enterprise-server/3-19/8.yml
new file mode 100644
index 000000000000..dd6bae2b2ba5
--- /dev/null
+++ b/data/release-notes/enterprise-server/3-19/8.yml
@@ -0,0 +1,98 @@
+date: '2026-06-30'
+sections:
+ security_fixes:
+ - |
+ **HIGH**: Current configurations of the GitHub API access controls could allow an attacker to create issues in any public repository via a u2s token without requiring the underlying installation to have issues write permission. This therefore allows an attacker to impersonate the victim in public repositories by creating issues and commit comments.
+ - |
+ **MEDIUM**: An attacker with site administrator privileges could extract arbitrary data from the instance's database, including user password hashes, by exploiting a blind SQL injection vulnerability in the `dependenciesPrefers` argument of the `dependencyGraphManifests` GraphQL field. This vulnerability affected instances with the dependency graph enabled and was reported via the GitHub Bug Bounty program.
+ - |
+ **MEDIUM**: An attacker could gain unintended access to an organization's runner management by directing a user to authorize an OAuth app whose requested manage_runners:org scope was not displayed on the authorization consent screen. GitHub has requested CVE ID [CVE-2026-9106](https://www.cve.org/cverecord?id=CVE-2026-9106) for this vulnerability, which was reported via the [GitHub Bug Bounty](https://bounty.github.com/) program.
+ - |
+ **MEDIUM**: An authenticated GHES user could read source code from private repositories they did not have access to by supplying a cross-repository comparison range to the Copilot pull request description diff summary endpoint, which did not verify the user's permission to view the target repository. GitHub has requested CVE ID [CVE-2026-9132](https://www.cve.org/cverecord?id=CVE-2026-9132) for this vulnerability, which was reported via the [GitHub Bug Bounty program](https://bounty.github.com/).
+ - |
+ **MEDIUM**: An attacker could execute arbitrary JavaScript in a victim's browser on a GitHub Enterprise Server instance by creating a discussion in the Q\&A category with a crafted title that breaks out of the JSON-LD structured-data script block when a comment is marked as the answer. To mitigate this issue, GitHub has updated the rendering of JSON-LD structured data in discussions to properly escape user-controlled input. GitHub has requested CVE ID [CVE-2026-10585](https://www.cve.org/cverecord?id=CVE-2026-10585) for this vulnerability, which was reported via the [GitHub Bug Bounty](https://bounty.github.com/) program.
+ - |
+ GitHub has updated `dnsmasq` to fix the following vulnerabilities: CVE-2026-4891, CVE-2026-4890, CVE-2026-4892, CVE-2026-4893, CVE-2026-5172, CVE-2026-2291.
+ - |
+ Packages have been updated to the latest security versions.
+ bugs:
+ - |
+ Administrators experienced "Permission denied" errors when the `ghes-manage-agent` attempted to write logs under `/var/log/license-upgrade` during license upgrade restart tasks.
+ - |
+ After administrators updated a GitHub Enterprise Server instance's TLS certificate, GitHub Pages deployments failed because the necessary services did not restart when `ghe-config-apply` was run.
+ - |
+ When uploading an invalid or expired license in the Management Console, the page would not correctly indicate the failure and restart the page without any additional information.
+ - |
+ On an instance with Enterprise Live Migrations enabled, running `/usr/local/bin/elm` without arguments dropped into a degraded container shell where standard administrative commands were unavailable.
+ - |
+ The scheduled job that repairs search indexes could run long enough that its next execution interrupted the in-progress repair, causing the process to restart before completing. The job used a locking mechanism to prevent overlapping runs and was scheduled to run once per month, on the first Saturday, instead of weekly.
+ - |
+ When a site administrator ran `ghe-remove-node` to evacuate and remove a node, orphaned configuration entries remained in node subsections such as `hostname.app.github`.
+ - |
+ On instances with multiple data disks configured, administrators who ran `ghe-restore` encountered an error message: `Runtime error (func=(main), adr=10): Divide by zero`.
+ - |
+ For administrators using the Backup Service to back up a high availability (HA) appliance from the replica with GitHub Actions enabled, MSSQL backups would fail with the error `This command can only be run on the primary mssql node.`
+ - |
+ When running `ghe-backup` on an instance configured with the Backup Service, the `data` directory inside the backups mountpoint (usually `/data/backup/data`) failed to be automatically created due to missing permissions.
+ - |
+ On instances processing webhooks, notifications, or other background events, the `github-stream-processors` Nomad job consumed increasing memory and processed events progressively more slowly the longer it ran, requiring site administrators to restart the job to restore baseline throughput.
+ - |
+ Administrators experienced gradual memory growth in long-running GitHub Enterprise Server background event processor services. Upgrading resets service-mapping context between messages, and administrators do not need to take any action after upgrading.
+ - |
+ On an instance with dependency graph enabled, dependency snapshot uploads via the dependency submission API occasionally failed with internal server errors.
+ - |
+ On instances without a GitHub Advanced Security license, the organization Dependabot dashboard in Security Overview returned a 404 error, even though Dependabot is available on those instances.
+ - |
+ On an instance with GitHub Code Security enabled, users could bypass code scanning merge protection when the scan had passed on an earlier commit but had not yet run on the latest commit of a pull request.
+ changes:
+ - |
+ To reduce response times for repositories with a large number of code scanning alerts, GitHub Enterprise Server uses an optimized query when counting alerts.
+ - |
+ Site administrators can include Enterprise Live Migrations (ELM) Exporter appliance data in scheduled backups using GitHub Enterprise Server Backup Service. For more information, see [AUTOTITLE](/admin/backing-up-and-restoring-your-instance).
+ - |
+ Site administrators can restore Enterprise Live Migrations (ELM) Exporter appliance data using GitHub Enterprise Server Backup Service. For more information, see [AUTOTITLE](/admin/backing-up-and-restoring-your-instance).
+ known_issues:
+ - |
+ During an upgrade of GitHub Enterprise Server, custom firewall rules are removed. If you use custom firewall rules, you must reapply them after upgrading.
+ - |
+ During the validation phase of a configuration run, a `No such object` error may occur for the Notebook and Viewscreen services. This error can be ignored as the services should still correctly start.
+ - |
+ If the root site administrator is locked out of the Management Console after failed login attempts, the account does not unlock automatically after the defined lockout time. Someone with administrative SSH access to the instance must unlock the account using the administrative shell. For more information, see [Troubleshooting access to the Management Console](/enterprise-server@latest/admin/administering-your-instance/administering-your-instance-from-the-web-ui/troubleshooting-access-to-the-management-console#unlocking-the-root-site-administrator-account).
+ - |
+ {% data reusables.release-notes.large-adoc-files-issue %}
+ - |
+ Admin stats REST API endpoints may timeout on appliances with many users or repositories. Retrying the request until data is returned is advised.
+ - |
+ When following the steps for [Replacing the primary MySQL node](/admin/monitoring-managing-and-updating-your-instance/configuring-clustering/replacing-a-cluster-node#replacing-the-primary-mysql-node), step 14 (running `ghe-cluster-config-apply`) might fail with errors. If this occurs, re-running `ghe-cluster-config-apply` is expected to succeed.
+ - |
+ Running a config apply as part of the steps for [Replacing a node in an emergency](/admin/monitoring-managing-and-updating-your-instance/configuring-clustering/replacing-a-cluster-node#replacing-a-node-in-an-emergency) may fail with errors if the node being replaced is still reachable. If this occurs, shutdown the node and repeat the steps.
+ - |
+ {% data reusables.release-notes.2024-06-possible-frontend-5-minute-outage-during-hotpatch-upgrade %}
+ - |
+ When restoring data originally backed up from a 3.13 or greater appliance version, the Elasticsearch indices need to be reindexed before some of the data will show up. This happens via a nightly scheduled job. It can also be forced by running `/usr/local/share/enterprise/ghe-es-search-repair`.
+ - |
+ When enabling automatic update checks for the first time in the Management Console, the status is not dynamically reflected until the "Updates" page is reloaded.
+ - |
+ When restoring from a backup snapshot, a large number of `mapper_parsing_exception` errors may be displayed.
+ - |
+ When initializing a new GHES cluster, nodes with the `consul-server` role should be added to the cluster before adding additional nodes. Adding all nodes simultaneously creates a race condition between nomad server registration and nomad client registration.
+ - |
+ In a cluster, the host running restore requires access the storage nodes via their private IPs.
+ - |
+ On an instance hosted on Azure, commenting on an issue via email means the comment is not added to the issue.
+ - |
+ After a restore, existing outside collaborators cannot be added to repositories in a new organization. This issue can be resolved by running `/usr/local/share/enterprise/ghe-es-search-repair` on the appliance.
+ - |
+ After a geo-replica is promoted to be a primary by running `ghe-repl-promote`, the GitHub Actions workflow of a repository does not have any suggested workflows.
+ - |
+ When publishing npm packages in a workflow after restoring from a backup to GitHub Enterprise Server 3.13.5.gm4 or 3.14.2.gm3, you may encounter a `401 Unauthorized` error from the GitHub Packages service. This can happen if the restore is from an N-1 or N-2 version and the workflow targets the npm endpoint on the backup instance. To avoid this issue, ensure the access token is valid and includes the correct scopes for publishing to GitHub Packages.
+ - |
+ The setting to define private registries at the organization level for code scanning is only available if Dependabot is also enabled for the instance.
+ - |
+ Upgrading or hotpatching to 3.19.1 may fail on nodes that have been continuously upgraded from versions older than 2021 (i.e. 2.17). If this issue occurs, you will see log entries prefixed with `invalid secret` in ghe-config.log. If you are running nodes from these older versions, it is recommended not to upgrade to 3.19.1.
+ - |
+ An issue in the Management Console means the Backups (Preview) and Updates tabs may fail to open and instead return an Internal Server Error. We recommend using the command line interface (CLI) for backups and updates.
+ - |
+ When applying an enterprise security configuration to all repositories (for example, enabling Secret Scanning or Code Scanning across all repositories), the system immediately enqueues enablement jobs for every organization in the enterprise simultaneously. For enterprises with a large number of repositories, this can result in significant system load and potential performance degradation. If you manage a large enterprise with many organizations and repositories, we recommend applying security configurations at the organization level rather than at the enterprise level in the UI. This allows you to enable security features incrementally and monitor system performance as you roll out changes.
+ - |
+ Administrators upgrading to certain patch releases encountered a "Failed to generate an OIDC token" error during the `Update Servicing Resources` step when GitHub Actions was configured with Google Cloud Storage (GCS) or AWS S3 using OpenID Connect (OIDC) authentication. The upgrade was blocked and could not complete. To work around this issue, administrators could apply a manual patch by running `sudo sed -i.bak 's|ghe-actions-console -s -c Update-Service|ghe-actions-console -s --no-blob-creds -c Update-Service|g' /usr/local/bin/ghe-actions-update` followed by `ghe-config-apply`.
diff --git a/data/release-notes/enterprise-server/3-20/4.yml b/data/release-notes/enterprise-server/3-20/4.yml
new file mode 100644
index 000000000000..7b28892c117d
--- /dev/null
+++ b/data/release-notes/enterprise-server/3-20/4.yml
@@ -0,0 +1,90 @@
+date: '2026-06-30'
+sections:
+ security_fixes:
+ - |
+ **HIGH**: Current configurations of the GitHub API access controls could allow an attacker to create issues in any public repository via a u2s token without requiring the underlying installation to have issues write permission. This therefore allows an attacker to impersonate the victim in public repositories by creating issues and commit comments.
+ - |
+ **MEDIUM**: An attacker with site administrator privileges could extract arbitrary data from the instance's database, including user password hashes, by exploiting a blind SQL injection vulnerability in the `dependenciesPrefers` argument of the `dependencyGraphManifests` GraphQL field. This vulnerability affected instances with the dependency graph enabled and was reported via the GitHub Bug Bounty program.
+ - |
+ **MEDIUM**: An attacker could gain unintended access to an organization's runner management by directing a user to authorize an OAuth app whose requested manage_runners:org scope was not displayed on the authorization consent screen. GitHub has requested CVE ID [CVE-2026-9106](https://www.cve.org/cverecord?id=CVE-2026-9106) for this vulnerability, which was reported via the [GitHub Bug Bounty](https://bounty.github.com/) program.
+ - |
+ **MEDIUM**: An authenticated GHES user could read source code from private repositories they did not have access to by supplying a cross-repository comparison range to the Copilot pull request description diff summary endpoint, which did not verify the user's permission to view the target repository. GitHub has requested CVE ID [CVE-2026-9132](https://www.cve.org/cverecord?id=CVE-2026-9132) for this vulnerability, which was reported via the [GitHub Bug Bounty program](https://bounty.github.com/).
+ - |
+ **MEDIUM**: An attacker could execute arbitrary JavaScript in a victim's browser on a GitHub Enterprise Server instance by creating a discussion in the Q\&A category with a crafted title that breaks out of the JSON-LD structured-data script block when a comment is marked as the answer. To mitigate this issue, GitHub has updated the rendering of JSON-LD structured data in discussions to properly escape user-controlled input. GitHub has requested CVE ID [CVE-2026-10585](https://www.cve.org/cverecord?id=CVE-2026-10585) for this vulnerability, which was reported via the [GitHub Bug Bounty](https://bounty.github.com/) program.
+ - |
+ GitHub has updated `dnsmasq` to fix the following vulnerabilities: CVE-2026-4891, CVE-2026-4890, CVE-2026-4892, CVE-2026-4893, CVE-2026-5172, CVE-2026-2291.
+ - |
+ Packages have been updated to the latest security versions.
+ - |
+ Upgraded the Notebooks container base image from Ubuntu Focal (20.04) to Noble (24.04) for continued security support.
+ bugs:
+ - |
+ Administrators experienced "Permission denied" errors when the `ghes-manage-agent` attempted to write logs under `/var/log/license-upgrade` during license upgrade restart tasks.
+ - |
+ When uploading an invalid or expired license in the Management Console, the page would not correctly indicate the failure and restart the page without any additional information.
+ - |
+ After administrators updated a GitHub Enterprise Server instance's TLS certificate, GitHub Pages deployments failed because the necessary services did not restart when `ghe-config-apply` was run.
+ - |
+ On an instance with Enterprise Live Migrations enabled, running `/usr/local/bin/elm` without arguments dropped into a degraded container shell where standard administrative commands were unavailable.
+ - |
+ The scheduled job that repairs search indexes could run long enough that its next execution interrupted the in-progress repair, causing the process to restart before completing. The job used a locking mechanism to prevent overlapping runs and was scheduled to run once per month, on the first Saturday, instead of weekly.
+ - |
+ When a site administrator ran `ghe-remove-node` to evacuate and remove a node, orphaned configuration entries remained in node subsections such as `hostname.app.github`.
+ - |
+ Administrators who had upgraded to GHES 3.20 or later saw validation errors when running a `config-apply`. This is because the required backup service configuration was not populated in `github.conf` during the upgrade.
+ - |
+ On instances with multiple data disks configured, administrators who ran `ghe-restore` encountered an error message: `Runtime error (func=(main), adr=10): Divide by zero`.
+ - |
+ For administrators using the Backup Service to back up a high availability (HA) appliance from the replica with GitHub Actions enabled, MSSQL backups would fail with the error `This command can only be run on the primary mssql node.`
+ - |
+ When running `ghe-backup` on an instance configured with the Backup Service, the `data` directory inside the backups mountpoint (usually `/data/backup/data`) failed to be automatically created due to missing permissions.
+ - |
+ Administrators experienced gradual memory growth in long-running GitHub Enterprise Server background event processor services. Upgrading resets service-mapping context between messages, and administrators do not need to take any action after upgrading.
+ - |
+ Submitting a dependency snapshot via the REST API could return a 500 internal server error instead of a successful response.
+ - |
+ On instances without a GitHub Advanced Security license, the organization Dependabot dashboard in Security Overview returned a 404 error, even though Dependabot is available on those instances.
+ - |
+ On an instance with GitHub Code Security enabled, users could bypass code scanning merge protection when the scan had passed on an earlier commit but had not yet run on the latest commit of a pull request.
+ changes:
+ - |
+ To reduce response times for repositories with a large number of code scanning alerts, GitHub Enterprise Server uses an optimized query when counting alerts.
+ - |
+ Site administrators can include Enterprise Live Migrations (ELM) Exporter appliance data in scheduled backups using GitHub Enterprise Server Backup Service. For more information, see [AUTOTITLE](/admin/backing-up-and-restoring-your-instance).
+ - |
+ Site administrators can restore Enterprise Live Migrations (ELM) Exporter appliance data using GitHub Enterprise Server Backup Service. For more information, see [AUTOTITLE](/admin/backing-up-and-restoring-your-instance).
+ known_issues:
+ - |
+ During an upgrade of GitHub Enterprise Server, custom firewall rules are removed. If you use custom firewall rules, you must reapply them after upgrading.
+ - |
+ During the validation phase of a configuration run, a `No such object` error may occur for the Notebook and Viewscreen services. This error can be ignored as the services should still correctly start.
+ - |
+ If the root site administrator is locked out of the Management Console after failed login attempts, the account does not unlock automatically after the defined lockout time. Someone with administrative SSH access to the instance must unlock the account using the administrative shell. For more information, see [Troubleshooting access to the Management Console](/enterprise-server@latest/admin/administering-your-instance/administering-your-instance-from-the-web-ui/troubleshooting-access-to-the-management-console#unlocking-the-root-site-administrator-account).
+ - |
+ {% data reusables.release-notes.large-adoc-files-issue %}
+ - |
+ Admin stats REST API endpoints may time out on appliances with many users or repositories. Retrying the request until data is returned is advised.
+ - |
+ When following the steps for [Replacing the primary MySQL node](/admin/monitoring-managing-and-updating-your-instance/configuring-clustering/replacing-a-cluster-node#replacing-the-primary-mysql-node), step 14 (running `ghe-cluster-config-apply`) might fail with errors. If this occurs, re-running `ghe-cluster-config-apply` is expected to succeed.
+ - |
+ Running a config apply as part of the steps for [Replacing a node in an emergency](/admin/monitoring-managing-and-updating-your-instance/configuring-clustering/replacing-a-cluster-node#replacing-a-node-in-an-emergency) may fail with errors if the node being replaced is still reachable. If this occurs, shutdown the node and repeat the steps.
+ - |
+ When restoring data originally backed up from a 3.13 or later appliance version, the Elasticsearch indices need to be reindexed before some of the data will show up. This happens via a nightly scheduled job. It can also be forced by running `/usr/local/share/enterprise/ghe-es-search-repair`.
+ - |
+ When initializing a new GHES cluster, nodes with the `consul-server` role should be added to the cluster before adding additional nodes. Adding all nodes simultaneously creates a race condition between nomad server registration and nomad client registration.
+ - |
+ In a cluster, the host running restore requires access the storage nodes via their private IPs.
+ - |
+ On an instance hosted on Azure, commenting on an issue via email means the comment is not added to the issue.
+ - |
+ After a restore, existing outside collaborators cannot be added to repositories in a new organization. This issue can be resolved by running `/usr/local/share/enterprise/ghe-es-search-repair` on the appliance.
+ - |
+ After a geo-replica is promoted to be a primary by running `ghe-repl-promote`, the GitHub Actions workflow of a repository does not have any suggested workflows.
+ - |
+ When publishing npm packages in a workflow after restoring from a backup to GitHub Enterprise Server 3.13.5.gm4 or 3.14.2.gm3, you may encounter a `401 Unauthorized` error from the GitHub Packages service. This can happen if the restore is from an N-1 or N-2 version and the workflow targets the npm endpoint on the backup instance. To avoid this issue, ensure the access token is valid and includes the correct scopes for publishing to GitHub Packages.
+ - |
+ When applying an enterprise security configuration to all repositories (for example, enabling Secret Scanning or Code Scanning across all repositories), the system immediately enqueues enablement jobs for every organization in the enterprise simultaneously. For enterprises with a large number of repositories, this can result in significant system load and potential performance degradation. If you manage a large enterprise with many organizations and repositories, we recommend applying security configurations at the organization level rather than at the enterprise level in the UI. This allows you to enable security features incrementally and monitor system performance as you roll out changes.
+ - |
+ On instances with multiple Git storage nodes in a voting configuration, including cluster and geo-replication high availability topologies, upgrading may fail to correctly install Actions that ship with the new version. In some cases, previous versions of these Actions remain on the instance. To resolve this issue, run the following commands on the primary node: `ghe-config --unset 'app.actions.actions-repos-sha1sum'`, `ghe-config-apply`, and `/usr/local/share/enterprise/ghe-run-init-actions-graph`.
+ - |
+ Administrators upgrading to certain patch releases encountered a "Failed to generate an OIDC token" error during the `Update Servicing Resources` step when GitHub Actions was configured with Google Cloud Storage (GCS) or AWS S3 using OpenID Connect (OIDC) authentication. The upgrade was blocked and could not complete. To work around this issue, administrators could apply a manual patch by running `sudo sed -i.bak 's|ghe-actions-console -s -c Update-Service|ghe-actions-console -s --no-blob-creds -c Update-Service|g' /usr/local/bin/ghe-actions-update` followed by `ghe-config-apply`.
diff --git a/data/release-notes/enterprise-server/3-21/2.yml b/data/release-notes/enterprise-server/3-21/2.yml
new file mode 100644
index 000000000000..bf0e3af4d0a1
--- /dev/null
+++ b/data/release-notes/enterprise-server/3-21/2.yml
@@ -0,0 +1,94 @@
+date: '2026-06-30'
+sections:
+ features:
+ - |
+ You can now run the pre-upgrade stage of an upgrade outside the maintenance window using `ghe-upgrade --phase pre-upgrade -y UPGRADE-PACKAGE-FILENAME`. Running the pre-upgrade stage in advance can reduce in-maintenance upgrade time by up to 20 minutes. For more information, see [AUTOTITLE](/admin/administering-your-instance/administering-your-instance-from-the-command-line/command-line-utilities#ghe-upgrade). Additional functionality and documentation will be available when you upgrade to 3.22 and later versions.
+ security_fixes:
+ - |
+ **HIGH**: Current configurations of the GitHub API access controls could allow an attacker to create issues in any public repository via a u2s token without requiring the underlying installation to have issues write permission. This therefore allows an attacker to impersonate the victim in public repositories by creating issues and commit comments.
+ - |
+ **MEDIUM**: An attacker with site administrator privileges could extract arbitrary data from the instance's database, including user password hashes, by exploiting a blind SQL injection vulnerability in the `dependenciesPrefers` argument of the `dependencyGraphManifests` GraphQL field. This vulnerability affected instances with the dependency graph enabled and was reported via the GitHub Bug Bounty program.
+ - |
+ **MEDIUM**: An attacker could gain unintended access to an organization's runner management by directing a user to authorize an OAuth app whose requested manage_runners:org scope was not displayed on the authorization consent screen. GitHub has requested CVE ID [CVE-2026-9106](https://www.cve.org/cverecord?id=CVE-2026-9106) for this vulnerability, which was reported via the [GitHub Bug Bounty](https://bounty.github.com/) program.
+ - |
+ GitHub has updated `dnsmasq` to fix the following vulnerabilities: CVE-2026-4891, CVE-2026-4890, CVE-2026-4892, CVE-2026-4893, CVE-2026-5172, CVE-2026-2291.
+ - |
+ Packages have been updated to the latest security versions.
+ bugs:
+ - |
+ After administrators updated a GitHub Enterprise Server instances TLS certificate, GitHub Pages deployments failed because the necessary services did not restart when `ghe-config-apply` was run.
+ - |
+ When uploading an invalid or expired license in the Management Console, the page would not correctly indicate the failure and restart the page without any additional information.
+ - |
+ On an instance with Enterprise Live Migrations enabled, running `/usr/local/bin/elm` without arguments dropped into a degraded container shell where standard administrative commands were unavailable.
+ - |
+ On instances with multiple data disks configured, administrators who ran `ghe-restore` encountered an error message: `Runtime error (func=(main), adr=10): Divide by zero`.
+ - |
+ The scheduled job that repairs search indexes could run long enough that its next execution interrupted the in-progress repair, causing the process to restart before completing. The job now uses a locking mechanism to prevent overlapping runs and is scheduled to run once per month, on the first Saturday, instead of weekly.
+ - |
+ When a site administrator ran `ghe-remove-node` to evacuate and remove a node, orphaned configuration entries remained in node subsections such as `hostname.app.github`.
+ - |
+ Administrators who had upgraded to GHES 3.20 or later saw validation errors when running a `config-apply`. This is because the required backup service configuration was not populated in `github.conf` during the upgrade.
+ - |
+ For administrators using the Backup Service to back up a high availability (HA) appliance from the replica with GitHub Actions enabled, MSSQL backups would fail with the error `This command can only be run on the primary mssql node.`
+ - |
+ When running `ghe-backup` on an instance configured with the Backup Service, the `data` directory inside the backups mountpoint (usually `/data/backup/data`) failed to be automatically created due to missing permissions.
+ - |
+ Administrators experienced gradual memory growth in long-running GitHub Enterprise Server background event processor services. Upgrading resets service-mapping context between messages, and administrators do not need to take any action after upgrading.
+ - |
+ On instances without a GitHub Advanced Security license, the organization Dependabot dashboard in Security Overview returned a 404 error, even though Dependabot is available on those instances.
+ - |
+ On an instance with GitHub Code Security enabled, users could bypass code scanning merge protection when the scan had passed on an earlier commit but had not yet run on the latest commit of a pull request.
+ - |
+ When a user viewed an issue or pull request containing image attachments, the images could appear broken after the page had been open or idle for several minutes, and reloading the page was required to display them again.
+ changes:
+ - |
+ The default `maxconn` (maximum connections) value for HAProxy has increased from 1,024 to 8,192 across all proxies. Previously, large customers reported surpassing the limit on high-traffic services.
+ - |
+ Administrators who forward GitHub Enterprise Server logs to external syslog systems can now configure syslog forwarding to use RFC 5424 formatting with microsecond-precision timestamps. This improves event ordering and correlation in SIEM and monitoring systems. To enable this format, run `ghe-config syslog.format RFC5424` and `ghe-config-apply`.
+ - |
+ To reduce response times for repositories with a large number of code scanning alerts, GitHub Enterprise Server uses an optimized query when counting alerts.
+ - |
+ Site administrators can include Enterprise Live Migrations (ELM) Exporter appliance data in scheduled backups using GitHub Enterprise Server Backup Service. For more information, see [AUTOTITLE](/admin/backing-up-and-restoring-your-instance).
+ - |
+ Site administrators can restore Enterprise Live Migrations (ELM) Exporter appliance data using GitHub Enterprise Server Backup Service. For more information, see [AUTOTITLE](/admin/backing-up-and-restoring-your-instance).
+ known_issues:
+ - |
+ During an upgrade of GitHub Enterprise Server, custom firewall rules are removed. If you use custom firewall rules, you must reapply them after upgrading.
+ - |
+ During the validation phase of a configuration run, a `No such object` error may occur for the Notebook and Viewscreen services. This error can be ignored as the services should still correctly start.
+ - |
+ If the root site administrator is locked out of the Management Console after failed login attempts, the account does not unlock automatically after the defined lockout time. Someone with administrative SSH access to the instance must unlock the account using the administrative shell. For more information, see [Troubleshooting access to the Management Console](/enterprise-server@latest/admin/administering-your-instance/administering-your-instance-from-the-web-ui/troubleshooting-access-to-the-management-console#unlocking-the-root-site-administrator-account).
+ - |
+ {% data reusables.release-notes.large-adoc-files-issue %}
+ - |
+ Admin stats REST API endpoints may timeout on appliances with many users or repositories. Retrying the request until data is returned is advised.
+ - |
+ When following the steps for [Replacing the primary MySQL node](/admin/monitoring-managing-and-updating-your-instance/configuring-clustering/replacing-a-cluster-node#replacing-the-primary-mysql-node), step 14 (running `ghe-cluster-config-apply`) might fail with errors. If this occurs, re-running `ghe-cluster-config-apply` is expected to succeed.
+ - |
+ Running a config apply as part of the steps for [Replacing a node in an emergency](/admin/monitoring-managing-and-updating-your-instance/configuring-clustering/replacing-a-cluster-node#replacing-a-node-in-an-emergency) may fail with errors if the node being replaced is still reachable. If this occurs, shutdown the node and repeat the steps.
+ - |
+ When restoring data originally backed up from a 3.13 or greater appliance version, the Elasticsearch indices need to be reindexed before some of the data will show up. This happens via a nightly scheduled job. It can also be forced by running `/usr/local/share/enterprise/ghe-es-search-repair`.
+ - |
+ When initializing a new GHES cluster, nodes with the `consul-server` role should be added to the cluster before adding additional nodes. Adding all nodes simultaneously creates a race condition between nomad server registration and nomad client registration.
+ - |
+ When initializing a new GHES cluster, nodes with the `consul-server` role should be added to the cluster before adding additional nodes. Adding all nodes simultaneously creates a race condition between nomad server registration and nomad client registration.
+ - |
+ In a cluster, the host running restore requires access the storage nodes via their private IPs.
+ - |
+ On an instance hosted on Azure, commenting on an issue via email meant the comment was not added to the issue.
+ - |
+ After a restore, existing outside collaborators cannot be added to repositories in a new organization. This issue can be resolved by running `/usr/local/share/enterprise/ghe-es-search-repair` on the appliance.
+ - |
+ After a geo-replica is promoted to be a primary by running `ghe-repl-promote`, the actions workflow of a repository does not have any suggested workflows.
+ - |
+ When publishing npm packages in a workflow after restoring from a backup to GitHub Enterprise Server 3.13.5.gm4 or 3.14.2.gm3, you may encounter a `401 Unauthorized` error from the GitHub Packages service. This can happen if the restore is from an N-1 or N-2 version and the workflow targets the npm endpoint on the backup instance. To avoid this issue, ensure the access token is valid and includes the correct scopes for publishing to GitHub Packages.
+ - |
+ When applying an enterprise security configuration to all repositories (for example, enabling Secret Scanning or Code Scanning across all repositories), the system immediately enqueues enablement jobs for every organization in the enterprise simultaneously. For enterprises with a large number of repositories, this can result in significant system load and potential performance degradation. If you manage a large enterprise with many organizations and repositories, we recommend applying security configurations at the organization level rather than at the enterprise level in the UI. This allows you to enable security features incrementally and monitor system performance as you roll out changes.
+ - |
+ On instances with multiple Git storage nodes in a voting configuration, including cluster and geo-replication high availability topologies, upgrading may fail to correctly install Actions that ship with the new version. In some cases, previous versions of these Actions remain on the instance. To resolve this issue, run the following commands on the primary node: `ghe-config --unset 'app.actions.actions-repos-sha1sum'`, `ghe-config-apply`, and `/usr/local/share/enterprise/ghe-run-init-actions-graph`.
+ - |
+ Users attempting to clone repositories via HTTPS receive an "Internal Server Error" message, and the clone operation fails with a 500 error. This issue affects all deployment topologies including cluster, standalone, high availability, and geo-replication configurations.
+ errata:
+ - |
+ A note about running the pre-upgrade stage outside the maintenance window has been added to the release notes. [Updated: 2026-06-30]
diff --git a/data/reusables/elm/locked-repo.md b/data/reusables/elm/locked-repo.md
index 7cd9ef640042..4377959ca7f7 100644
--- a/data/reusables/elm/locked-repo.md
+++ b/data/reusables/elm/locked-repo.md
@@ -1,5 +1,5 @@
-If a cutover fails partway through, the source repository may remain locked or archived. This prevents developers from pushing to the source while the destination may still be incomplete.
+If a cutover fails after the source repository has been archived, the {% data variables.product.prodname_elm_short %} service will attempt to unarchive the repository. If this fails, a repository administrator can unarchive the repository. See [AUTOTITLE](/repositories/archiving-a-github-repository/archiving-repositories#unarchiving-a-repository).
-To unlock the source repository, a site administrator must unlock it from the {% data variables.product.prodname_ghe_server %} {% data variables.enterprise.management_console %}.
+Be aware that unarchiving a repository will cause additional load on the instance, as all issues and pull requests in the repository will be reindexed in Elasticsearch.
-After the source is unlocked, you can either retry cutover using `elm migration cutover-to-destination --migration-id MIGRATION-ID`, or abort the migration with `elm migration cancel --migration-id MIGRATION-ID` and start a new migration when you're ready.
+After the source repository is unarchived, you can either retry cutover using `elm migration cutover-to-destination --migration-id MIGRATION-ID`, or abort the migration with `elm migration cancel --migration-id MIGRATION-ID` and start a new migration when you're ready.
diff --git a/data/reusables/organizations/types-of-team-visibility.md b/data/reusables/organizations/types-of-team-visibility.md
index 36b0228741c1..0a73d2f6446f 100644
--- a/data/reusables/organizations/types-of-team-visibility.md
+++ b/data/reusables/organizations/types-of-team-visibility.md
@@ -1,6 +1,6 @@
Teams can be visible or secret:
* Visible teams can be [viewed and @mentioned](/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#mentioning-people-and-teams) by every organization member.
-* Secret teams are only visible to the people on the team and people with owner permissions. They're great for hiding teams with sensitive names or members, such as those used for working with external partners or clients. Secret teams cannot be nested under parent teams or have child teams.
+* Secret teams are only visible to the people on the team and organization owners. They're great for hiding the members of teams that work with external partners or clients. However, if a team member or organization owner mentions a secret team, the team name is visible in the mention text. Secret teams cannot be nested under parent teams or have child teams.
People who are not members of the organization cannot view any teams.
diff --git a/data/tables/copilot/model-comparison.yml b/data/tables/copilot/model-comparison.yml
index 0ab19e0b16db..109577762406 100644
--- a/data/tables/copilot/model-comparison.yml
+++ b/data/tables/copilot/model-comparison.yml
@@ -68,6 +68,11 @@
excels_at: Complex problem-solving challenges, sophisticated reasoning
further_reading: '[Claude Sonnet 4.6 model card](https://www-cdn.anthropic.com/78073f739564e986ff3e28522761a7a0b4484f84.pdf)'
+- name: Claude Sonnet 5
+ task_area: General-purpose coding and agent tasks
+ excels_at: Complex problem-solving challenges, sophisticated reasoning
+ further_reading: 'Not available'
+
# Google
- name: Gemini 2.5 Pro
task_area: Deep reasoning and debugging
diff --git a/data/tables/copilot/model-release-status.yml b/data/tables/copilot/model-release-status.yml
index b291035bf752..9e05c227c2f7 100644
--- a/data/tables/copilot/model-release-status.yml
+++ b/data/tables/copilot/model-release-status.yml
@@ -40,6 +40,11 @@
release_status: 'GA'
# Anthropic models
+
+- name: 'Claude Fable 5'
+ provider: 'Anthropic'
+ release_status: 'GA'
+
- name: 'Claude Haiku 4.5'
provider: 'Anthropic'
release_status: 'GA'
@@ -64,15 +69,15 @@
provider: 'Anthropic'
release_status: 'GA'
-- name: 'Claude Fable 5'
+- name: 'Claude Sonnet 4.5'
provider: 'Anthropic'
release_status: 'GA'
-- name: 'Claude Sonnet 4.5'
+- name: 'Claude Sonnet 4.6'
provider: 'Anthropic'
release_status: 'GA'
-- name: 'Claude Sonnet 4.6'
+- name: 'Claude Sonnet 5'
provider: 'Anthropic'
release_status: 'GA'
diff --git a/data/tables/copilot/model-supported-clients.yml b/data/tables/copilot/model-supported-clients.yml
index f5f9fbe6c10b..0a45d99d394c 100644
--- a/data/tables/copilot/model-supported-clients.yml
+++ b/data/tables/copilot/model-supported-clients.yml
@@ -95,6 +95,15 @@
xcode: false
jetbrains: false
+- name: Claude Sonnet 5
+ dotcom: true
+ cli: true
+ vscode: true
+ vs: true
+ eclipse: false
+ xcode: false
+ jetbrains: false
+
- name: Gemini 2.5 Pro
dotcom: false
cli: false
diff --git a/data/tables/copilot/model-supported-plans.yml b/data/tables/copilot/model-supported-plans.yml
index f3d3e95e7310..dd0c20882c86 100644
--- a/data/tables/copilot/model-supported-plans.yml
+++ b/data/tables/copilot/model-supported-plans.yml
@@ -75,6 +75,13 @@
business: true
enterprise: true
+- name: Claude Sonnet 5
+ pro: true
+ pro_plus: true
+ max: true
+ business: true
+ enterprise: true
+
- name: Gemini 2.5 Pro
pro: true
pro_plus: true
diff --git a/data/tables/copilot/models-and-pricing.yml b/data/tables/copilot/models-and-pricing.yml
index dd6592b523ea..81a37cde4281 100644
--- a/data/tables/copilot/models-and-pricing.yml
+++ b/data/tables/copilot/models-and-pricing.yml
@@ -171,6 +171,15 @@
output: $25.00
cache_write: $6.25
+- model: Claude Sonnet 5[^sonnet-5-promo]
+ provider: anthropic
+ release_status: GA
+ category: Versatile
+ input: $2.00
+ cached_input: $0.20
+ output: $10.00
+ cache_write: $2.50
+
- model: Claude Opus 4.8 (fast mode) (preview)
provider: anthropic
release_status: GA
diff --git a/data/ui.yml b/data/ui.yml
index 4eba450206e8..3c0042112d20 100644
--- a/data/ui.yml
+++ b/data/ui.yml
@@ -54,7 +54,7 @@ search:
search_docs_with_query: Search docs for "{{query}}"
privacy_disclaimer: For product and service improvement purposes, the GitHub Docs team will retain questions and answers generated in the Docs search function. Please see the GitHub Privacy Statement to review how GitHub collects and uses your data.
ai:
- disclaimer: Copilot uses AI. Check for mistakes.
+ disclaimer: Copilot uses AI. Check for mistakes.
references: Copilot Sources
loading_status_message: Loading Copilot response...
done_loading_status_message: Done loading Copilot response
diff --git a/data/variables/copilot.yml b/data/variables/copilot.yml
index 2e05c6c4ead3..ad3c0c7fbfff 100644
--- a/data/variables/copilot.yml
+++ b/data/variables/copilot.yml
@@ -202,6 +202,7 @@ copilot_claude_sonnet_37: 'Claude Sonnet 3.7'
copilot_claude_sonnet_40: 'Claude Sonnet 4'
copilot_claude_sonnet_45: 'Claude Sonnet 4.5'
copilot_claude_sonnet_46: 'Claude Sonnet 4.6'
+copilot_claude_sonnet_5: 'Claude Sonnet 5'
# Gemini:
copilot_gemini: 'Gemini'
copilot_gemini_flash: 'Gemini 2.0 Flash'
diff --git a/eslint.config.ts b/eslint.config.ts
index 0be658ea7314..4b004441f956 100644
--- a/eslint.config.ts
+++ b/eslint.config.ts
@@ -103,6 +103,10 @@ export default [
// Custom rules
'custom-rules/use-custom-logger': 'error',
+ // Disallow dangerouslySetInnerHTML; render trusted HTML via RenderedHTML /
+ // renderHTMLString or a hast tree instead (github/docs-engineering#6619).
+ 'custom-rules/no-dangerously-set-inner-html': 'error',
+
// Prevent direct res.redirect() usage — use res.safeRedirect() instead
// to avoid open redirect vulnerabilities via protocol-relative URLs.
'no-restricted-syntax': [
@@ -232,9 +236,10 @@ export default [
},
// Allow role="list" on list-style:none
elements in these components.
- // Chromium drops the implicit `list` role from the accessibility tree when
- // list-style:none is set, so NVDA/JAWS lose list semantics; role="list" restores
- // them and is not actually redundant here. See github/accessibility-audits#16815.
+ // Chromium drops the implicit `list`/`listitem` roles from the accessibility tree
+ // when list-style:none is set, so NVDA/JAWS lose list semantics and the item count;
+ // role="list" on the
diff --git a/src/rest/components/RestAuth.tsx b/src/rest/components/RestAuth.tsx
index cb3c49875e40..0f15102cf675 100644
--- a/src/rest/components/RestAuth.tsx
+++ b/src/rest/components/RestAuth.tsx
@@ -4,6 +4,7 @@ import { useTranslation } from '@/languages/components/useTranslation'
import { DEFAULT_VERSION, useVersion } from '@/versions/components/useVersion'
import { Link } from '@/frame/components/Link'
import { ProgAccessT } from './types'
+import { RenderedHTML } from '@/frame/components/ui/RenderedHTML/RenderedHTML'
// Documentation paths may be moved around by content team in the future
const USER_TOKEN_PATH =
@@ -69,7 +70,7 @@ export function RestAuth({ progAccess, slug, operationTitle }: Props) {
function NoFineGrainedAccess({ basicAuth }: { basicAuth: boolean }) {
const { t } = useTranslation('rest_reference')
- if (basicAuth) return
+ if (basicAuth) return
return
The scope of the budget for this organization. Use 'organization' for org-level budgets or 'repository' for repo-specific budgets within the organization. user and multi_user_customer scopes are only supported when budget_product_sku is ai_credits or premium_requests.
",
+ "description": "
The scope of the budget for this organization.
\n
\n
organization: Apply the budget to the organization.
\n
repository: Apply the budget to a specific repository in the organization.
\n
multi_user_customer: Apply a universal budget to all users in the organization.
\n
user: Apply the budget to a single user in the organization.
\n
\n
user and multi_user_customer scopes are only supported when\nbudget_product_sku is ai_credits or premium_requests.
Usernames to assign to this Dependabot Alert.\nPass one or more user logins to replace the set of assignees on this alert.\nSend an empty array ([]) to clear all assignees from the alert.\nTo assign an AI agent, include the bot login (for example, copilot-swe-agent[bot]).
The authenticated user must have access to security alerts for the repository to use this endpoint. For more information, see \"Granting access to security alerts.\"
\n
OAuth app tokens and personal access tokens (classic) need the security_events scope to use this endpoint. If this endpoint is only used with public repositories, the token can use the public_repo scope instead.
The scope of the budget for this organization. Use 'organization' for org-level budgets or 'repository' for repo-specific budgets within the organization. user and multi_user_customer scopes are only supported when budget_product_sku is ai_credits or premium_requests.
",
+ "description": "
The scope of the budget for this organization.
\n
\n
organization: Apply the budget to the organization.
\n
repository: Apply the budget to a specific repository in the organization.
\n
multi_user_customer: Apply a universal budget to all users in the organization.
\n
user: Apply the budget to a single user in the organization.
\n
\n
user and multi_user_customer scopes are only supported when\nbudget_product_sku is ai_credits or premium_requests.
Usernames to assign to this Dependabot Alert.\nPass one or more user logins to replace the set of assignees on this alert.\nSend an empty array ([]) to clear all assignees from the alert.\nTo assign an AI agent, include the bot login (for example, copilot-swe-agent[bot]).
The authenticated user must have access to security alerts for the repository to use this endpoint. For more information, see \"Granting access to security alerts.\"
\n
OAuth app tokens and personal access tokens (classic) need the security_events scope to use this endpoint. If this endpoint is only used with public repositories, the token can use the public_repo scope instead.
Usernames to assign to this Dependabot Alert.\nPass one or more user logins to replace the set of assignees on this alert.\nSend an empty array ([]) to clear all assignees from the alert.\nTo assign an AI agent, include the bot login (for example, copilot-swe-agent[bot]).
The authenticated user must have access to security alerts for the repository to use this endpoint. For more information, see \"Granting access to security alerts.\"
\n
OAuth app tokens and personal access tokens (classic) need the security_events scope to use this endpoint. If this endpoint is only used with public repositories, the token can use the public_repo scope instead.
Usernames to assign to this Dependabot Alert.\nPass one or more user logins to replace the set of assignees on this alert.\nSend an empty array ([]) to clear all assignees from the alert.\nTo assign an AI agent, include the bot login (for example, copilot-swe-agent[bot]).
The authenticated user must have access to security alerts for the repository to use this endpoint. For more information, see \"Granting access to security alerts.\"
\n
OAuth app tokens and personal access tokens (classic) need the security_events scope to use this endpoint. If this endpoint is only used with public repositories, the token can use the public_repo scope instead.
Usernames to assign to this Dependabot Alert.\nPass one or more user logins to replace the set of assignees on this alert.\nSend an empty array ([]) to clear all assignees from the alert.\nTo assign an AI agent, include the bot login (for example, copilot-swe-agent[bot]).
The authenticated user must have access to security alerts for the repository to use this endpoint. For more information, see \"Granting access to security alerts.\"
\n
OAuth app tokens and personal access tokens (classic) need the security_events scope to use this endpoint. If this endpoint is only used with public repositories, the token can use the public_repo scope instead.
Usernames to assign to this Dependabot Alert.\nPass one or more user logins to replace the set of assignees on this alert.\nSend an empty array ([]) to clear all assignees from the alert.\nTo assign an AI agent, include the bot login (for example, copilot-swe-agent[bot]).
The authenticated user must have access to security alerts for the repository to use this endpoint. For more information, see \"Granting access to security alerts.\"
\n
OAuth app tokens and personal access tokens (classic) need the security_events scope to use this endpoint. If this endpoint is only used with public repositories, the token can use the public_repo scope instead.
Usernames to assign to this Dependabot Alert.\nPass one or more user logins to replace the set of assignees on this alert.\nSend an empty array ([]) to clear all assignees from the alert.\nTo assign an AI agent, include the bot login (for example, copilot-swe-agent[bot]).
The authenticated user must have access to security alerts for the repository to use this endpoint. For more information, see \"Granting access to security alerts.\"
\n
OAuth app tokens and personal access tokens (classic) need the security_events scope to use this endpoint. If this endpoint is only used with public repositories, the token can use the public_repo scope instead.
Usernames to assign to this Dependabot Alert.\nPass one or more user logins to replace the set of assignees on this alert.\nSend an empty array ([]) to clear all assignees from the alert.\nTo assign an AI agent, include the bot login (for example, copilot-swe-agent[bot]).
The authenticated user must have access to security alerts for the repository to use this endpoint. For more information, see \"Granting access to security alerts.\"
\n
OAuth app tokens and personal access tokens (classic) need the security_events scope to use this endpoint. If this endpoint is only used with public repositories, the token can use the public_repo scope instead.
Usernames to assign to this Dependabot Alert.\nPass one or more user logins to replace the set of assignees on this alert.\nSend an empty array ([]) to clear all assignees from the alert.\nTo assign an AI agent, include the bot login (for example, copilot-swe-agent[bot]).
The authenticated user must have access to security alerts for the repository to use this endpoint. For more information, see \"Granting access to security alerts.\"
\n
OAuth app tokens and personal access tokens (classic) need the security_events scope to use this endpoint. If this endpoint is only used with public repositories, the token can use the public_repo scope instead.
Usernames to assign to this Dependabot Alert.\nPass one or more user logins to replace the set of assignees on this alert.\nSend an empty array ([]) to clear all assignees from the alert.\nTo assign an AI agent, include the bot login (for example, copilot-swe-agent[bot]).
The authenticated user must have access to security alerts for the repository to use this endpoint. For more information, see \"Granting access to security alerts.\"
\n
OAuth app tokens and personal access tokens (classic) need the security_events scope to use this endpoint. If this endpoint is only used with public repositories, the token can use the public_repo scope instead.
",
diff --git a/src/rest/lib/config.json b/src/rest/lib/config.json
index 6eaaa278c3fe..193cae090c80 100644
--- a/src/rest/lib/config.json
+++ b/src/rest/lib/config.json
@@ -47,5 +47,5 @@
]
}
},
- "sha": "3c63b49e41d98449d7b0f7ed12ea40dc9839edd4"
+ "sha": "8d5eb57de9ea64dbcfeca8442934eaecb3817f12"
}
\ No newline at end of file
diff --git a/src/search/components/input/AskAIResults.tsx b/src/search/components/input/AskAIResults.tsx
index 754a6d298534..eefd71c2d6be 100644
--- a/src/search/components/input/AskAIResults.tsx
+++ b/src/search/components/input/AskAIResults.tsx
@@ -24,6 +24,7 @@ import { generateAISearchLinksJson } from '../helpers/ai-search-links-json'
import { ASK_AI_EVENT_GROUP } from '@/events/components/event-groups'
import type { AIReference } from '../types'
+import { RenderedHTML } from '@/frame/components/ui/RenderedHTML/RenderedHTML'
type AIQueryResultsProps = {
query: string
@@ -398,7 +399,7 @@ export function AskAIResults({
{!aiCouldNotAnswer && message !== '' ? (
-
+
) : null}
-
diff --git a/src/search/components/results/SearchResults.tsx b/src/search/components/results/SearchResults.tsx
index 828bee21b853..4e8e2803cf2a 100644
--- a/src/search/components/results/SearchResults.tsx
+++ b/src/search/components/results/SearchResults.tsx
@@ -15,6 +15,9 @@ import type { SearchQueryContentT } from '@/search/components/types'
import type { GeneralSearchHitWithoutIncludes, GeneralSearchResponse } from '@/search/types'
import type { estypes } from '@elastic/elasticsearch'
import { GENERAL_SEARCH_RESULTS } from '@/events/components/event-groups'
+import { RenderedHTML } from '@/frame/components/ui/RenderedHTML/RenderedHTML'
+import { renderHTMLString } from '@/frame/components/ui/RenderedHTML/render-html-string'
+import { markdownComponents } from '@/frame/components/ui/MarkdownContent/markdownComponents'
type Props = {
results: GeneralSearchResponse
@@ -118,7 +121,6 @@ function SearchResultHit({
{
sendEvent({
@@ -132,9 +134,11 @@ function SearchResultHit({
eventGroupId: eventGroupId.current,
})
}}
- >
+ >
+ {renderHTMLString(title, markdownComponents)}
+
- {content && }
+ {content && }
{debug && (
score: {hit.score} popularity:{' '}
diff --git a/src/tools/components/PlatformPicker.tsx b/src/tools/components/PlatformPicker.tsx
index 4c95b7681711..b0ab8782712b 100644
--- a/src/tools/components/PlatformPicker.tsx
+++ b/src/tools/components/PlatformPicker.tsx
@@ -15,40 +15,8 @@ const platforms = [
// Nota bene: platform === os
-// Imperatively modify article content to show only the selected platform
-// find all platform-specific *block* elements and hide or show as appropriate
-// example: {% mac %} block content {% endmac %}
-function showPlatformSpecificContent(platform: string) {
- const markdowns = Array.from(document.querySelectorAll('.ghd-tool'))
- const platformMarkdowns = markdowns.filter((xel) =>
- platforms.some((platformValue) => xel.classList.contains(platformValue.value)),
- )
- for (const el of platformMarkdowns) {
- el.style.display = el.classList.contains(platform) ? '' : 'none'
-
- // hack: special handling for minitoc links -- we can't pass the tool classes
- // directly to the Primer NavList.Item generated