Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions .github/workflows/connector-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,17 @@ jobs:
npm run typecheck
npm run test -- --ci --runInBand tests/bridge/bridge-service.test.ts tests/auth/computer-bridge.test.ts tests/lib/connector-version.test.ts

- name: Build unsigned Windows installer verification artifacts
run: npm run connector:build:windows
- name: Build Windows verification artifacts and Microsoft Store MSIX
run: npm run connector:build:windows:store

- name: Verify installer lifecycle, package, pinned runtime, and DPAPI
run: npm run connector:verify:windows

- name: Verify Microsoft Store identity and MSIX contents
env:
DEVINX_REQUIRE_MSIX_ARTIFACT: '1'
run: npm run connector:verify:windows:store

- name: Audit JavaScript dependencies
run: npm run audit

Expand All @@ -51,3 +56,12 @@ jobs:
artifacts/connector/windows/DevinX-Connector-Setup-0.1.0-windows-x64.exe.sha256
artifacts/connector/windows/verification-audit.json
if-no-files-found: error

- name: Upload Microsoft Store submission package
uses: actions/upload-artifact@v6
with:
name: devinx-connector-windows-x64-MICROSOFT-STORE-UPLOAD
path: |
artifacts/connector/windows/DevinX-Connector-*-windows-x64.msix
artifacts/connector/windows/DevinX-Connector-*-windows-x64.msix.sha256
if-no-files-found: error
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,9 +148,12 @@ controls. Cognition's separate
[Windows cloud-session environments](https://docs.devin.ai/onboard-devin/environment/windows-support)
are not the Connector runtime.

The unsigned artifact produced by ordinary CI is deliberately marked **NOT FOR RELEASE**. DevinX
will offer Windows as a supported download only after an Authenticode-signed candidate passes the
clean Windows 11 x64 physical matrix in [spec 037](specs/037-windows-connector.md), including
The unsigned EXE and ZIP artifacts produced by ordinary CI are deliberately marked **NOT FOR
RELEASE**. The public Windows distribution is the reserved
[Microsoft Store listing](https://apps.microsoft.com/detail/9N52Z3FVMFH8), whose accepted MSIX is
signed and delivered by Microsoft. DevinX will offer it as a supported download only after the
Store package passes certification and the clean Windows 11 x64 physical matrix in
[spec 037](specs/037-windows-connector.md), including
official Devin ACP discovery, Tailscale routing, pairing, read/send/create grants,
AskUserQuestion, sleep/reconnect, update, and uninstall. This prevents an unfinished or unsigned
package from being presented as trustworthy.
Expand Down Expand Up @@ -267,6 +270,8 @@ or TestFlight build. Expo Go is not the release test environment.
| `npm run connector:verify:macos` | Verify the packaged Connector artifact |
| `npm run connector:build:windows` | Build the Windows x64 app, installer, and checksums |
| `npm run connector:verify:windows` | Verify installer lifecycle, package, and DPAPI storage |
| `npm run connector:build:windows:store` | Build the exact-identity Microsoft Store MSIX |
| `npm run connector:verify:windows:store` | Verify Store identity, manifest, assets, and MSIX |

No package may be added until it is verified in the official registry, including its publication
history, download history, and source repository. Dependencies are lockfile-enforced; never use a
Expand Down
56 changes: 56 additions & 0 deletions connector/windows-msix/AppxManifest.xml.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<Package
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:uap10="http://schemas.microsoft.com/appx/manifest/uap/windows10/10"
xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
IgnorableNamespaces="uap uap10 desktop rescap">
<Identity
Name="{{IDENTITY_NAME}}"
Publisher="{{PUBLISHER}}"
Version="{{VERSION}}"
ProcessorArchitecture="{{ARCHITECTURE}}" />
<Properties>
<DisplayName>DevinX Connector</DisplayName>
<PublisherDisplayName>{{PUBLISHER_DISPLAY_NAME}}</PublisherDisplayName>
<Logo>Assets\StoreLogo.png</Logo>
</Properties>
<Resources>
<Resource Language="{{DEFAULT_LANGUAGE}}" />
</Resources>
<Dependencies>
<TargetDeviceFamily
Name="Windows.Desktop"
MinVersion="{{MINIMUM_WINDOWS_VERSION}}"
MaxVersionTested="{{MAXIMUM_WINDOWS_VERSION_TESTED}}" />
</Dependencies>
<Applications>
<Application
Id="DevinXConnector"
Executable="DevinXConnector.exe"
uap10:RuntimeBehavior="win32App"
uap10:TrustLevel="mediumIL">
<uap:VisualElements
DisplayName="DevinX Connector"
Description="Securely connect DevinX on iPhone to Devin running on this Windows PC."
BackgroundColor="transparent"
Square150x150Logo="Assets\Square150x150Logo.png"
Square44x44Logo="Assets\Square44x44Logo.png" />
<Extensions>
<desktop:Extension
Category="windows.startupTask"
Executable="DevinXConnector.exe"
EntryPoint="Windows.FullTrustApplication">
<desktop:StartupTask
TaskId="DevinXConnectorStartup"
Enabled="false"
DisplayName="DevinX Connector" />
</desktop:Extension>
</Extensions>
</Application>
</Applications>
<Capabilities>
<rescap:Capability Name="runFullTrust" />
Comment on lines +32 to +54

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 AppxManifest declares runFullTrust alongside mediumIL trust level

connector/windows-msix/AppxManifest.xml.template:32-33 sets uap10:RuntimeBehavior="win32App" and uap10:TrustLevel="mediumIL" on the Application while :54 declares the runFullTrust restricted capability, and the startup-task extension uses EntryPoint="Windows.FullTrustApplication". The app must launch a Node child process and use current-user DPAPI, so it genuinely needs full trust. This particular attribute combination is intentional per the verify script's assertions, and MakeAppx pack + Store certification would reject a truly invalid manifest, so I did not flag it — but the mediumIL + runFullTrust pairing is unusual enough to be worth confirming against a successful real MSIX build/cert run on Windows.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

</Capabilities>
</Package>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added connector/windows-msix/Assets/Square44x44Logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added connector/windows-msix/Assets/StoreLogo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions connector/windows-msix/store-identity.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"storeId": "9N52Z3FVMFH8",
"identityName": "DevinXTools.DevinXConnector",
"publisher": "CN=43D84E24-857C-4C40-9DAA-1A6983913CD9",
"publisherDisplayName": "DevinX Tools",
"packageFamilyName": "DevinXTools.DevinXConnector_ydtgrt4yd5wrc",
"msaAppId": "7d1237e7-6265-48ab-adf5-2a12f5d81101",
"version": "0.1.1.0",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 MSIX package version (0.1.1.0) diverges from the actual runtime version (0.1.4) and the wrapped 0.1.0 build folder

The Store package is built by wrapping the DevinX-Connector-0.1.0-windows-x64 payload folder (scripts/connector/build-windows-msix.mjs:47-52, matching connectorVersion = '0.1.0' in scripts/connector/build-windows.mjs:22) and is stamped with MSIX version 0.1.1.0 from connector/windows-msix/store-identity.json. Meanwhile the bundled runtime reports CONNECTOR_VERSION = '0.1.4' (bridge/src/version.ts:1), which is what the mobile compatibility handshake actually checks against MINIMUM_SUPPORTED_CONNECTOR_VERSION = '0.1.2'. Functionally this is fine (the handshake uses the runtime version, not the MSIX version), but the three different version numbers for the same artifact are confusing and worth confirming intent, especially since the PR title says "0.1.1".

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

"architecture": "x64",
"defaultLanguage": "en-US",
"minimumWindowsVersion": "10.0.22000.0",
"maximumWindowsVersionTested": "10.0.26100.0"
}
2 changes: 1 addition & 1 deletion connector/windows/DevinXConnector.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net10.0-windows</TargetFramework>
<TargetFramework>net10.0-windows10.0.22000.0</TargetFramework>
<UseWindowsForms>true</UseWindowsForms>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
Expand Down
109 changes: 102 additions & 7 deletions connector/windows/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ private static void Main()
internal sealed class ConnectorForm : Form
{
private const int MaximumIpcLineCharacters = 16_384;
private const string StartupTaskId = "DevinXConnectorStartup";
private const string StartupValueName = "DevinX Connector";
private const string ReleasePage = "https://github.com/fenner888/Devinx/releases/latest";
private const string ReleasePage = "https://apps.microsoft.com/detail/9N52Z3FVMFH8";
private const string SetupGuide =
"https://github.com/fenner888/Devinx/blob/main/docs/devinx-connector.md";
private readonly Label statusLabel = new() { AutoSize = true, Font = new Font("Segoe UI", 18, FontStyle.Bold) };
Expand All @@ -42,12 +43,13 @@ internal sealed class ConnectorForm : Form
};
private readonly Button savePermissionsButton = new() { Text = "Save permissions", AutoSize = true };
private readonly Button revokeButton = new() { Text = "Revoke selected iPhone", AutoSize = true };
private readonly Button releasesButton = new() { Text = "Check official releases", AutoSize = true };
private readonly Button releasesButton = new() { Text = "Open Microsoft Store", AutoSize = true };
private readonly Button helpButton = new() { Text = "Setup and uninstall help", AutoSize = true };
private readonly NotifyIcon trayIcon = new() { Text = "DevinX Connector", Visible = true };
private readonly SemaphoreSlim writeLock = new(1, 1);
private Process? runtime;
private bool exiting;
private bool launchAtLoginInitializing = true;

public ConnectorForm()
{
Expand Down Expand Up @@ -124,17 +126,20 @@ public ConnectorForm()
revokeButton.Click += async (_, _) => await RevokeSelectedDeviceAsync();
releasesButton.Click += (_, _) => OpenOfficialPage(ReleasePage);
helpButton.Click += (_, _) => OpenOfficialPage(SetupGuide);
launchAtLogin.CheckedChanged += (_, _) => SetLaunchAtLogin(launchAtLogin.Checked);
launchAtLogin.CheckedChanged += async (_, _) => await HandleLaunchAtLoginChangedAsync();
FormClosing += HandleFormClosing;
Shown += async (_, _) => await StartRuntimeAsync();
Shown += async (_, _) =>
{
await InitializeLaunchAtLoginAsync();
await StartRuntimeAsync();
};

var menu = new ContextMenuStrip();
menu.Items.Add("Open DevinX Connector", null, (_, _) => RestoreWindow());
menu.Items.Add("Quit DevinX Connector", null, async (_, _) => await ExitAsync());
trayIcon.ContextMenuStrip = menu;
trayIcon.Icon = SystemIcons.Application;
trayIcon.DoubleClick += (_, _) => RestoreWindow();
launchAtLogin.Checked = IsLaunchAtLoginEnabled();
SetStatus("Starting…", "Checking Tailscale and Devin for Terminal");
}

Expand Down Expand Up @@ -372,13 +377,103 @@ private async Task ExitAsync()
Application.Exit();
}

private static bool IsLaunchAtLoginEnabled()
private async Task InitializeLaunchAtLoginAsync()
{
launchAtLoginInitializing = true;
try
{
if (!IsPackaged())
{
launchAtLogin.Checked = IsRegistryLaunchAtLoginEnabled();
return;
}

var startupTask = await global::Windows.ApplicationModel.StartupTask.GetAsync(StartupTaskId);
launchAtLogin.Checked =
startupTask.State == global::Windows.ApplicationModel.StartupTaskState.Enabled;
}
catch
{
launchAtLogin.Checked = false;
launchAtLogin.Enabled = false;
launchAtLogin.Text = "Open at sign in is unavailable for this installation";
}
finally
{
launchAtLoginInitializing = false;
}
}

private async Task HandleLaunchAtLoginChangedAsync()
{
if (launchAtLoginInitializing) return;
if (!IsPackaged())
{
SetRegistryLaunchAtLogin(launchAtLogin.Checked);
return;
}

launchAtLoginInitializing = true;
try
{
var startupTask = await global::Windows.ApplicationModel.StartupTask.GetAsync(StartupTaskId);
if (!launchAtLogin.Checked)
{
startupTask.Disable();
return;
}

var state = await startupTask.RequestEnableAsync();
if (state == global::Windows.ApplicationModel.StartupTaskState.Enabled) return;

launchAtLogin.Checked = false;
var detail =
state == global::Windows.ApplicationModel.StartupTaskState.DisabledByUser
? "Windows has disabled this startup task. Re-enable DevinX Connector in Settings > Apps > Startup."
: "Windows could not enable DevinX Connector at sign in on this PC.";
MessageBox.Show(
this,
detail,
"Open at sign in",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
catch
{
launchAtLogin.Checked = false;
MessageBox.Show(
this,
"Windows could not update the startup setting. Reopen DevinX Connector and try again.",
"Open at sign in",
MessageBoxButtons.OK,
MessageBoxIcon.Warning);
}
finally
{
launchAtLoginInitializing = false;
}
}

private static bool IsPackaged()
{
try
{
return !string.IsNullOrWhiteSpace(
global::Windows.ApplicationModel.Package.Current.Id.Name);
}
catch (InvalidOperationException)
{
return false;
}
}

private static bool IsRegistryLaunchAtLoginEnabled()
{
using var key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Run", writable: false);
return key?.GetValue(StartupValueName) is string;
}

private static void SetLaunchAtLogin(bool enabled)
private static void SetRegistryLaunchAtLogin(bool enabled)
{
using var key = Registry.CurrentUser.CreateSubKey(@"Software\Microsoft\Windows\CurrentVersion\Run", writable: true);
if (enabled) key.SetValue(StartupValueName, $"\"{Application.ExecutablePath}\"");
Expand Down
6 changes: 3 additions & 3 deletions connector/windows/packages.lock.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"version": 1,
"dependencies": {
"net10.0-windows7.0": {
"net10.0-windows10.0.22000": {
"Microsoft.NET.ILLink.Tasks": {
"type": "Direct",
"requested": "[10.0.10, )",
Expand All @@ -15,6 +15,6 @@
"contentHash": "RuvX3PEXU6pbY/I5ItAk800jm62r+YnoPLgyS2WTgwxkOnGkOfU9ORiipHUF0LkLyqM8rlroUCA319JjRYfRFQ=="
}
},
"net10.0-windows7.0/win-x64": {}
"net10.0-windows10.0.22000/win-x64": {}
}
}
}
19 changes: 10 additions & 9 deletions docs/connector-platform-roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,22 @@ Platform code may implement only secure storage, executable discovery, lifecycle
## Windows x64 implementation checkpoint

The shared Windows adapter, current-user DPAPI helper, native WinForms control surface,
notification-area lifecycle, pinned-runtime builder, per-user installer/uninstaller, Authenticode
signing hook, and Windows CI verifier are implemented under `bridge/`, `connector/windows/`,
`connector/windows-installer/`, and `scripts/connector/`. Ordinary CI remains explicitly unsigned
and not for release. A protected manual workflow produces a signed verification candidate once a
publisher-controlled Authenticode identity is configured. Windows becomes a supported download
only after that candidate passes the physical matrix in `specs/037-windows-connector.md`.
notification-area lifecycle, pinned-runtime builder, per-user installer/uninstaller, Microsoft
Store MSIX builder, and Windows CI verifier are implemented under `bridge/`, `connector/windows/`,
`connector/windows-installer/`, `connector/windows-msix/`, and `scripts/connector/`. Ordinary EXE
and ZIP CI artifacts remain explicitly unsigned and not for release. The public path is the exact
Partner Center identity `DevinXTools.DevinXConnector`; Microsoft signs and delivers the accepted
MSIX. Windows becomes a supported download only after Store certification and that exact package
passes the physical matrix in `specs/037-windows-connector.md`.

Remaining public-release gates:

1. Physically verify the exact ACP methods advertised by the installed official Windows Devin CLI.
Cognition now documents native Windows x64 and arm64 CLI installers plus `devin acp`; runtime
negotiation remains authoritative for the exact methods available on the test PC.
2. Configure a stable Authenticode code-signing identity, run the protected signed-candidate
workflow, and publish its checksum and provenance only after the physical matrix passes. Do not
distribute the unsigned CI artifact.
2. Upload the exact-identity MSIX to Partner Center, pass certification, and publish only through
Store ID `9N52Z3FVMFH8` after the physical matrix passes. Do not distribute the unsigned CI EXE,
ZIP, or pre-certification MSIX directly.
3. Validate Windows Firewall behavior while binding only to the active `100.64.0.0/10` Tailscale
interface.
4. Test pairing, denial, expiry, read/send/create permission separation, endpoint refresh,
Expand Down
Loading