diff --git a/.gitignore b/.gitignore
index 0fe46b6eadc4ccc819fbf342ee1071bb657792b3..e545e004cef31fa5f40ba8df6a2317ea5b69ddb5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,7 +25,6 @@ out-vscode-reh-web-pkg/
 out-vscode-web/
 out-vscode-web-min/
 out-vscode-web-pkg/
-src/vs/server
 resources/server
 build/node_modules
 coverage/
diff --git a/.yarnrc b/.yarnrc
deleted file mode 100644
index 3c6eccfb102f2084d16395d70d65f05a91b6d47b..0000000000000000000000000000000000000000
--- a/.yarnrc
+++ /dev/null
@@ -1,3 +0,0 @@
-disturl "https://atom.io/download/electron"
-target "9.2.1"
-runtime "electron"
diff --git a/build/gulpfile.reh.js b/build/gulpfile.reh.js
index f2ea1bd37010b1eb8a43ce9beaae4a88810f6e2d..3f660f9981921ec465d2b8809a1a5ea5663f4c1f 100644
--- a/build/gulpfile.reh.js
+++ b/build/gulpfile.reh.js
@@ -52,6 +52,7 @@ gulp.task('vscode-reh-web-linux-x64-min', noop);
 gulp.task('vscode-reh-web-linux-alpine-min', noop);
 
 function getNodeVersion() {
+	return process.versions.node;
 	const yarnrc = fs.readFileSync(path.join(REPO_ROOT, 'remote', '.yarnrc'), 'utf8');
 	const target = /^target "(.*)"$/m.exec(yarnrc)[1];
 	return target;
diff --git a/build/lib/node.js b/build/lib/node.js
index 403ae3d9657f823019542e739fc39292db20e4fe..738ee8cee0e79aa239af10e1abefc9e836b8ce33 100644
--- a/build/lib/node.js
+++ b/build/lib/node.js
@@ -5,11 +5,8 @@
  *--------------------------------------------------------------------------------------------*/
 Object.defineProperty(exports, "__esModule", { value: true });
 const path = require("path");
-const fs = require("fs");
 const root = path.dirname(path.dirname(__dirname));
-const yarnrcPath = path.join(root, 'remote', '.yarnrc');
-const yarnrc = fs.readFileSync(yarnrcPath, 'utf8');
-const version = /^target\s+"([^"]+)"$/m.exec(yarnrc)[1];
+const version = process.versions.node;
 const node = process.platform === 'win32' ? 'node.exe' : 'node';
 const nodePath = path.join(root, '.build', 'node', `v${version}`, `${process.platform}-${process.arch}`, node);
 console.log(nodePath);
diff --git a/build/lib/node.ts b/build/lib/node.ts
index 64397034461b1661f82007c141cbf4c039a3b722..c53dccf4dc0a99122ed96cf10c2eb632bb25059e 100644
--- a/build/lib/node.ts
+++ b/build/lib/node.ts
@@ -4,13 +4,10 @@
  *--------------------------------------------------------------------------------------------*/
 
 import * as path from 'path';
-import * as fs from 'fs';
 
 const root = path.dirname(path.dirname(__dirname));
-const yarnrcPath = path.join(root, 'remote', '.yarnrc');
-const yarnrc = fs.readFileSync(yarnrcPath, 'utf8');
-const version = /^target\s+"([^"]+)"$/m.exec(yarnrc)![1];
+const version = process.versions.node;
 const node = process.platform === 'win32' ? 'node.exe' : 'node';
 const nodePath = path.join(root, '.build', 'node', `v${version}`, `${process.platform}-${process.arch}`, node);
 
-console.log(nodePath);
\ No newline at end of file
+console.log(nodePath);
diff --git a/build/lib/util.js b/build/lib/util.js
index e552a036f89bd581644459fd5c27fe4ae1379f62..169e8614b9f6a2bd68446144ab7e1ce5c6d49b64 100644
--- a/build/lib/util.js
+++ b/build/lib/util.js
@@ -257,6 +257,7 @@ function streamToPromise(stream) {
 }
 exports.streamToPromise = streamToPromise;
 function getElectronVersion() {
+    return process.versions.node;
     const yarnrc = fs.readFileSync(path.join(root, '.yarnrc'), 'utf8');
     const target = /^target "(.*)"$/m.exec(yarnrc)[1];
     return target;
diff --git a/build/lib/util.ts b/build/lib/util.ts
index 035c7e95ea3006bb3dabd68bbf54db80de4aaaf2..4ff8dcfe6b21a0ec8064ebc7bb05506b8f1faa91 100644
--- a/build/lib/util.ts
+++ b/build/lib/util.ts
@@ -322,6 +322,7 @@ export function streamToPromise(stream: NodeJS.ReadWriteStream): Promise<void> {
 }
 
 export function getElectronVersion(): string {
+	return process.versions.node;
 	const yarnrc = fs.readFileSync(path.join(root, '.yarnrc'), 'utf8');
 	const target = /^target "(.*)"$/m.exec(yarnrc)![1];
 	return target;
diff --git a/build/npm/postinstall.js b/build/npm/postinstall.js
index 8f8b0019a7792a993fbd6bf95b013b596aa2935a..ea054c725bea2eec342e12b07314241aa18a4951 100644
--- a/build/npm/postinstall.js
+++ b/build/npm/postinstall.js
@@ -33,10 +33,11 @@ function yarnInstall(location, opts) {
 
 yarnInstall('extensions'); // node modules shared by all extensions
 
-if (!(process.platform === 'win32' && (process.arch === 'arm64' || process.env['npm_config_arch'] === 'arm64'))) {
-	yarnInstall('remote'); // node modules used by vscode server
-	yarnInstall('remote/web'); // node modules used by vscode web
-}
+// NOTE@coder: Skip these dependencies since we don't use them.
+// if (!(process.platform === 'win32' && (process.arch === 'arm64' || process.env['npm_config_arch'] === 'arm64'))) {
+// 	yarnInstall('remote'); // node modules used by vscode server
+// 	yarnInstall('remote/web'); // node modules used by vscode web
+// }
 
 const allExtensionFolders = fs.readdirSync('extensions');
 const extensions = allExtensionFolders.filter(e => {
@@ -69,9 +70,9 @@ runtime "${runtime}"`;
 }
 
 yarnInstall(`build`); // node modules required for build
-yarnInstall('test/automation'); // node modules required for smoketest
-yarnInstall('test/smoke'); // node modules required for smoketest
-yarnInstall('test/integration/browser'); // node modules required for integration
+// yarnInstall('test/automation'); // node modules required for smoketest
+// yarnInstall('test/smoke'); // node modules required for smoketest
+// yarnInstall('test/integration/browser'); // node modules required for integration
 yarnInstallBuildDependencies(); // node modules for watching, specific to host node version, not electron
 
 cp.execSync('git config pull.rebase true');
diff --git a/build/npm/preinstall.js b/build/npm/preinstall.js
index cb88d37adefd4882f61a2711fdd7f72b89e1a6e3..6b3253af0a3a0aa4d75456379ef1c00f4cb98d13 100644
--- a/build/npm/preinstall.js
+++ b/build/npm/preinstall.js
@@ -8,8 +8,9 @@ let err = false;
 const majorNodeVersion = parseInt(/^(\d+)\./.exec(process.versions.node)[1]);
 
 if (majorNodeVersion < 10 || majorNodeVersion >= 13) {
-	console.error('\033[1;31m*** Please use node >=10 and <=12.\033[0;0m');
-	err = true;
+	// We are ok building above Node 12.
+	// console.error('\033[1;31m*** Please use node >=10 and <=12.\033[0;0m');
+	// err = true;
 }
 
 const cp = require('child_process');
diff --git a/coder.js b/coder.js
new file mode 100644
index 0000000000000000000000000000000000000000..df5b42cba463b6c0043aebbc835f852f1284aa36
--- /dev/null
+++ b/coder.js
@@ -0,0 +1,64 @@
+// This must be ran from VS Code's root.
+const gulp = require("gulp");
+const path = require("path");
+const _ = require("underscore");
+const buildfile = require("./src/buildfile");
+const common = require("./build/lib/optimize");
+const util = require("./build/lib/util");
+const deps = require("./build/dependencies");
+
+const vscodeEntryPoints = _.flatten([
+	buildfile.entrypoint("vs/workbench/workbench.web.api"),
+	buildfile.entrypoint("vs/server/entry"),
+	buildfile.base,
+	buildfile.workbenchWeb,
+	buildfile.workerExtensionHost,
+	buildfile.workerNotebook,
+	buildfile.keyboardMaps,
+	buildfile.entrypoint("vs/platform/files/node/watcher/unix/watcherApp", ["vs/css", "vs/nls"]),
+	buildfile.entrypoint("vs/platform/files/node/watcher/nsfw/watcherApp", ["vs/css", "vs/nls"]),
+	buildfile.entrypoint("vs/workbench/services/extensions/node/extensionHostProcess", ["vs/css", "vs/nls"]),
+]);
+
+const vscodeResources = [
+	"out-build/vs/server/fork.js",
+	"!out-build/vs/server/doc/**",
+	"out-build/vs/workbench/services/extensions/worker/extensionHostWorkerMain.js",
+	"out-build/bootstrap.js",
+	"out-build/bootstrap-fork.js",
+	"out-build/bootstrap-amd.js",
+	'out-build/bootstrap-node.js',
+	"out-build/paths.js",
+	'out-build/vs/**/*.{svg,png,html,ttf}',
+	"!out-build/vs/code/browser/workbench/*.html",
+	'!out-build/vs/code/electron-browser/**',
+	"out-build/vs/base/common/performance.js",
+	"out-build/vs/base/node/languagePacks.js",
+	'out-build/vs/base/browser/ui/codicons/codicon/**',
+	"out-build/vs/workbench/browser/media/*-theme.css",
+	"out-build/vs/workbench/contrib/debug/**/*.json",
+	"out-build/vs/workbench/contrib/externalTerminal/**/*.scpt",
+	"out-build/vs/workbench/contrib/webview/browser/pre/*.js",
+	"out-build/vs/**/markdown.css",
+	"out-build/vs/workbench/contrib/tasks/**/*.json",
+	"out-build/vs/platform/files/**/*.md",
+	"!**/test/**"
+];
+
+gulp.task("optimize", gulp.series(
+	util.rimraf("out-vscode"),
+	common.optimizeTask({
+		src: "out-build",
+		entryPoints: vscodeEntryPoints,
+		resources: vscodeResources,
+		loaderConfig: common.loaderConfig(),
+		out: "out-vscode",
+		inlineAmdImages: true,
+		bundleInfo: undefined
+	}),
+));
+
+gulp.task("minify", gulp.series(
+	util.rimraf("out-vscode-min"),
+	common.minifyTask("out-vscode")
+));
diff --git a/extensions/postinstall.js b/extensions/postinstall.js
index da4fa3e9d0443d679dfbab1000b434af2ae01afd..50f3e1144f8057883dea8b91ec2f7073458dbd94 100644
--- a/extensions/postinstall.js
+++ b/extensions/postinstall.js
@@ -24,6 +24,9 @@ function processRoot() {
 			rimraf.sync(filePath);
 		}
 	}
+
+	// Delete .bin so it doesn't contain broken symlinks that trip up nfpm.
+	rimraf.sync(path.join(__dirname, 'node_modules', '.bin'));
 }
 
 function processLib() {
diff --git a/package.json b/package.json
index 9b5ee0f876303283eb766fd2bb3ed818c50b1d3e..30ef9fa81b1cd844138388d794d4d6d9db5c7fba 100644
--- a/package.json
+++ b/package.json
@@ -46,7 +46,11 @@
     "watch-web": "gulp watch-web --max_old_space_size=4095",
     "eslint": "eslint -c .eslintrc.json --rulesdir ./build/lib/eslint --ext .ts --ext .js ./src/vs ./extensions"
   },
+  "dependencies_comment": "Move rimraf to dependencies because it is used in the postinstall script.",
   "dependencies": {
+    "@coder/logger": "^1.1.12",
+    "@coder/node-browser": "^1.0.8",
+    "@coder/requirefs": "^1.1.5",
     "applicationinsights": "1.0.8",
     "chokidar": "3.2.3",
     "graceful-fs": "4.2.3",
@@ -60,6 +64,7 @@
     "native-keymap": "2.2.0",
     "native-watchdog": "1.3.0",
     "node-pty": "0.10.0-beta8",
+    "rimraf": "^2.2.8",
     "semver-umd": "^5.5.7",
     "spdlog": "^0.11.1",
     "sudo-prompt": "9.1.1",
@@ -160,7 +165,6 @@
     "pump": "^1.0.1",
     "queue": "3.0.6",
     "rcedit": "^1.1.0",
-    "rimraf": "^2.2.8",
     "sinon": "^1.17.2",
     "source-map": "^0.4.4",
     "style-loader": "^1.0.0",
@@ -192,5 +196,8 @@
     "windows-foreground-love": "0.2.0",
     "windows-mutex": "0.3.0",
     "windows-process-tree": "0.2.4"
+  },
+  "resolutions": {
+    "minimist": "^1.2.5"
   }
 }
diff --git a/product.json b/product.json
index b9349015e3475bff07104ca2fa859954a37f962a..4c32260abc42efe17ee7d717e4dcebf182044e8c 100644
--- a/product.json
+++ b/product.json
@@ -20,7 +20,7 @@
 	"darwinBundleIdentifier": "com.visualstudio.code.oss",
 	"linuxIconName": "com.visualstudio.code.oss",
 	"licenseFileName": "LICENSE.txt",
-	"reportIssueUrl": "https://github.com/Microsoft/vscode/issues/new",
+	"reportIssueUrl": "https://github.com/cdr/code-server/issues/new",
 	"urlProtocol": "code-oss",
 	"extensionAllowedProposedApi": [
 		"ms-vscode.vscode-js-profile-flame",
diff --git a/remote/.yarnrc b/remote/.yarnrc
deleted file mode 100644
index c1a32ce532afa501fb19bdbcf6bcb0ec151ecd99..0000000000000000000000000000000000000000
--- a/remote/.yarnrc
+++ /dev/null
@@ -1,3 +0,0 @@
-disturl "http://nodejs.org/dist"
-target "12.14.1"
-runtime "node"
diff --git a/src/vs/base/common/network.ts b/src/vs/base/common/network.ts
index 4b6aebc16466dff58a9dfab4a680d230fa1f71a5..dd72e179ec0fa9a0b3e16e497225cb6da6218af3 100644
--- a/src/vs/base/common/network.ts
+++ b/src/vs/base/common/network.ts
@@ -113,16 +113,17 @@ class RemoteAuthoritiesImpl {
 		if (host && host.indexOf(':') !== -1) {
 			host = `[${host}]`;
 		}
-		const port = this._ports[authority];
+		// const port = this._ports[authority];
 		const connectionToken = this._connectionTokens[authority];
 		let query = `path=${encodeURIComponent(uri.path)}`;
 		if (typeof connectionToken === 'string') {
 			query += `&tkn=${encodeURIComponent(connectionToken)}`;
 		}
+		// NOTE@coder: Changed this to work against the current path.
 		return URI.from({
 			scheme: platform.isWeb ? this._preferredWebSchema : Schemas.vscodeRemoteResource,
-			authority: `${host}:${port}`,
-			path: `/vscode-remote-resource`,
+			authority: window.location.host,
+			path: `${window.location.pathname.replace(/\/+$/, '')}/vscode-remote-resource`,
 			query
 		});
 	}
diff --git a/src/vs/base/common/platform.ts b/src/vs/base/common/platform.ts
index 0bbc5d6ef911b1e98d26ad796873a9b6b7fb04ec..61f139b9c557b9c46e5a9640ab0e37a6fb7692ee 100644
--- a/src/vs/base/common/platform.ts
+++ b/src/vs/base/common/platform.ts
@@ -59,6 +59,17 @@ if (typeof navigator === 'object' && !isElectronRenderer) {
 	_isWeb = true;
 	_locale = navigator.language;
 	_language = _locale;
+	// NOTE@coder: Make languages work.
+	const el = typeof document !== 'undefined' && document.getElementById('vscode-remote-nls-configuration');
+	const rawNlsConfig = el && el.getAttribute('data-settings');
+	if (rawNlsConfig) {
+		try {
+			const nlsConfig: NLSConfig = JSON.parse(rawNlsConfig);
+			_locale = nlsConfig.locale;
+			_translationsConfigFile = nlsConfig._translationsConfigFile;
+			_language = nlsConfig.availableLanguages['*'] || LANGUAGE_DEFAULT;
+		} catch (error) { /* Oh well. */ }
+	}
 } else if (typeof process === 'object') {
 	_isWindows = (process.platform === 'win32');
 	_isMacintosh = (process.platform === 'darwin');
diff --git a/src/vs/base/common/processes.ts b/src/vs/base/common/processes.ts
index c52f7b3774f399d3fa161682316b20d807072806..08a87fa970f159f84691c5068cf5e38f0926015c 100644
--- a/src/vs/base/common/processes.ts
+++ b/src/vs/base/common/processes.ts
@@ -110,7 +110,8 @@ export function sanitizeProcessEnvironment(env: IProcessEnvironment, ...preserve
 		/^ELECTRON_.+$/,
 		/^GOOGLE_API_KEY$/,
 		/^VSCODE_.+$/,
-		/^SNAP(|_.*)$/
+		/^SNAP(|_.*)$/,
+		/^CODE_SERVER_.+$/,
 	];
 	const envKeys = Object.keys(env);
 	envKeys
diff --git a/src/vs/base/common/uriIpc.ts b/src/vs/base/common/uriIpc.ts
index ef2291d49b13c9c995afc90eab9c92afabc2b3b4..29b2f9dfc2b7fa998ac1188db06dee95419fcd5b 100644
--- a/src/vs/base/common/uriIpc.ts
+++ b/src/vs/base/common/uriIpc.ts
@@ -5,6 +5,7 @@
 
 import { URI, UriComponents } from 'vs/base/common/uri';
 import { MarshalledObject } from 'vs/base/common/marshalling';
+import { Schemas } from './network';
 
 export interface IURITransformer {
 	transformIncoming(uri: UriComponents): UriComponents;
@@ -31,29 +32,35 @@ function toJSON(uri: URI): UriComponents {
 
 export class URITransformer implements IURITransformer {
 
-	private readonly _uriTransformer: IRawURITransformer;
-
-	constructor(uriTransformer: IRawURITransformer) {
-		this._uriTransformer = uriTransformer;
+	constructor(private readonly remoteAuthority: string) {
 	}
 
+	// NOTE@coder: Coming in from the browser it'll be vscode-remote so it needs
+	// to be transformed into file.
 	public transformIncoming(uri: UriComponents): UriComponents {
-		const result = this._uriTransformer.transformIncoming(uri);
-		return (result === uri ? uri : toJSON(URI.from(result)));
+		return uri.scheme === Schemas.vscodeRemote
+			? toJSON(URI.file(uri.path))
+			: uri;
 	}
 
+	// NOTE@coder: Going out to the browser it'll be file so it needs to be
+	// transformed into vscode-remote.
 	public transformOutgoing(uri: UriComponents): UriComponents {
-		const result = this._uriTransformer.transformOutgoing(uri);
-		return (result === uri ? uri : toJSON(URI.from(result)));
+		return uri.scheme === Schemas.file
+			? toJSON(URI.from({ authority: this.remoteAuthority, scheme: Schemas.vscodeRemote, path: uri.path }))
+			: uri;
 	}
 
 	public transformOutgoingURI(uri: URI): URI {
-		const result = this._uriTransformer.transformOutgoing(uri);
-		return (result === uri ? uri : URI.from(result));
+		return uri.scheme === Schemas.file
+			? URI.from({ authority: this.remoteAuthority, scheme: Schemas.vscodeRemote, path:uri.path })
+			: uri;
 	}
 
 	public transformOutgoingScheme(scheme: string): string {
-		return this._uriTransformer.transformOutgoingScheme(scheme);
+		return scheme === Schemas.file
+			? Schemas.vscodeRemote
+			: scheme;
 	}
 }
 
@@ -152,4 +159,4 @@ export function transformAndReviveIncomingURIs<T>(obj: T, transformer: IURITrans
 		return obj;
 	}
 	return result;
-}
\ No newline at end of file
+}
diff --git a/src/vs/base/node/languagePacks.js b/src/vs/base/node/languagePacks.js
index 2c64061da7b01aef0bfe3cec851da232ca9461c8..c0ef8faedd406c38bf9c55bbbdbbb060046492d9 100644
--- a/src/vs/base/node/languagePacks.js
+++ b/src/vs/base/node/languagePacks.js
@@ -128,7 +128,10 @@ function factory(nodeRequire, path, fs, perf) {
 	function getLanguagePackConfigurations(userDataPath) {
 		const configFile = path.join(userDataPath, 'languagepacks.json');
 		try {
-			return nodeRequire(configFile);
+			// NOTE@coder: Swapped require with readFile since require is cached and
+			// we don't restart the server-side portion of code-server when the
+			// language changes.
+			return JSON.parse(fs.readFileSync(configFile, "utf8"));
 		} catch (err) {
 			// Do nothing. If we can't read the file we have no
 			// language pack config.
diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts
index ad5272b22320a361cec0eed40d57629b06147c01..c9280b14472507ebb9a277f554485f08b090cb69 100644
--- a/src/vs/code/browser/workbench/workbench.ts
+++ b/src/vs/code/browser/workbench/workbench.ts
@@ -16,6 +16,7 @@ import { isEqual } from 'vs/base/common/resources';
 import { isStandalone } from 'vs/base/browser/browser';
 import { localize } from 'vs/nls';
 import { Schemas } from 'vs/base/common/network';
+import { encodePath } from 'vs/server/node/util';
 
 interface ICredential {
 	service: string;
@@ -253,12 +254,18 @@ class WorkspaceProvider implements IWorkspaceProvider {
 
 		// Folder
 		else if (isFolderToOpen(workspace)) {
-			targetHref = `${document.location.origin}${document.location.pathname}?${WorkspaceProvider.QUERY_PARAM_FOLDER}=${encodeURIComponent(workspace.folderUri.toString())}`;
+			const target = workspace.folderUri.scheme === Schemas.vscodeRemote
+				? encodePath(workspace.folderUri.path)
+				: encodeURIComponent(workspace.folderUri.toString());
+			targetHref = `${document.location.origin}${document.location.pathname}?${WorkspaceProvider.QUERY_PARAM_FOLDER}=${target}`;
 		}
 
 		// Workspace
 		else if (isWorkspaceToOpen(workspace)) {
-			targetHref = `${document.location.origin}${document.location.pathname}?${WorkspaceProvider.QUERY_PARAM_WORKSPACE}=${encodeURIComponent(workspace.workspaceUri.toString())}`;
+			const target = workspace.workspaceUri.scheme === Schemas.vscodeRemote
+				? encodePath(workspace.workspaceUri.path)
+				: encodeURIComponent(workspace.workspaceUri.toString());
+			targetHref = `${document.location.origin}${document.location.pathname}?${WorkspaceProvider.QUERY_PARAM_WORKSPACE}=${target}`;
 		}
 
 		// Append payload if any
@@ -348,7 +355,22 @@ class WindowIndicator implements IWindowIndicator {
 		throw new Error('Missing web configuration element');
 	}
 
-	const config: IWorkbenchConstructionOptions & { folderUri?: UriComponents, workspaceUri?: UriComponents } = JSON.parse(configElementAttribute);
+	const config: IWorkbenchConstructionOptions & { folderUri?: UriComponents, workspaceUri?: UriComponents } = {
+		webviewEndpoint: `${window.location.origin}${window.location.pathname.replace(/\/+$/, '')}/webview/`,
+		...JSON.parse(configElementAttribute),
+	};
+
+	// Strip the protocol from the authority if it exists.
+	const normalizeAuthority = (authority: string): string => authority.replace(/^https?:\/\//, "");
+	if (config.remoteAuthority) {
+		(config as any).remoteAuthority = normalizeAuthority(config.remoteAuthority);
+	}
+	if (config.workspaceUri && config.workspaceUri.authority) {
+		config.workspaceUri.authority = normalizeAuthority(config.workspaceUri.authority);
+	}
+	if (config.folderUri && config.folderUri.authority) {
+		config.folderUri.authority = normalizeAuthority(config.folderUri.authority);
+	}
 
 	// Revive static extension locations
 	if (Array.isArray(config.staticExtensions)) {
@@ -360,40 +382,7 @@ class WindowIndicator implements IWindowIndicator {
 	// Find workspace to open and payload
 	let foundWorkspace = false;
 	let workspace: IWorkspace;
-	let payload = Object.create(null);
-
-	const query = new URL(document.location.href).searchParams;
-	query.forEach((value, key) => {
-		switch (key) {
-
-			// Folder
-			case WorkspaceProvider.QUERY_PARAM_FOLDER:
-				workspace = { folderUri: URI.parse(value) };
-				foundWorkspace = true;
-				break;
-
-			// Workspace
-			case WorkspaceProvider.QUERY_PARAM_WORKSPACE:
-				workspace = { workspaceUri: URI.parse(value) };
-				foundWorkspace = true;
-				break;
-
-			// Empty
-			case WorkspaceProvider.QUERY_PARAM_EMPTY_WINDOW:
-				workspace = undefined;
-				foundWorkspace = true;
-				break;
-
-			// Payload
-			case WorkspaceProvider.QUERY_PARAM_PAYLOAD:
-				try {
-					payload = JSON.parse(value);
-				} catch (error) {
-					console.error(error); // possible invalid JSON
-				}
-				break;
-		}
-	});
+	let payload = config.workspaceProvider?.payload || Object.create(null);
 
 	// If no workspace is provided through the URL, check for config attribute from server
 	if (!foundWorkspace) {
diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts
index 92dd2bcf87dba5e5f07f2707a91b1a364ab1b05f..047522bd1a2c1edfda05c3739838fecbd70db6c5 100644
--- a/src/vs/platform/environment/node/argv.ts
+++ b/src/vs/platform/environment/node/argv.ts
@@ -8,6 +8,8 @@ import { localize } from 'vs/nls';
 import { isWindows } from 'vs/base/common/platform';
 
 export interface ParsedArgs {
+	'extra-extensions-dir'?: string[];
+	'extra-builtin-extensions-dir'?: string[];
 	_: string[];
 	'folder-uri'?: string[]; // undefined or array of 1 or more
 	'file-uri'?: string[]; // undefined or array of 1 or more
@@ -142,6 +144,8 @@ export const OPTIONS: OptionDescriptions<Required<ParsedArgs>> = {
 	'extensions-dir': { type: 'string', deprecates: 'extensionHomePath', cat: 'e', args: 'dir', description: localize('extensionHomePath', "Set the root path for extensions.") },
 	'extensions-download-dir': { type: 'string' },
 	'builtin-extensions-dir': { type: 'string' },
+	'extra-builtin-extensions-dir': { type: 'string[]', cat: 'o', description: 'Path to an extra builtin extension directory.' },
+	'extra-extensions-dir': { type: 'string[]', cat: 'o', description: 'Path to an extra user extension directory.' },
 	'list-extensions': { type: 'boolean', cat: 'e', description: localize('listExtensions', "List the installed extensions.") },
 	'show-versions': { type: 'boolean', cat: 'e', description: localize('showVersions', "Show versions of installed extensions, when using --list-extension.") },
 	'category': { type: 'string', cat: 'e', description: localize('category', "Filters installed extensions by provided category, when using --list-extension.") },
@@ -405,4 +409,3 @@ export function buildHelpMessage(productName: string, executableName: string, ve
 export function buildVersionMessage(version: string | undefined, commit: string | undefined): string {
 	return `${version || localize('unknownVersion', "Unknown version")}\n${commit || localize('unknownCommit', "Unknown commit")}\n${process.arch}`;
 }
-
diff --git a/src/vs/platform/environment/node/environmentService.ts b/src/vs/platform/environment/node/environmentService.ts
index 45d5ec2cc02707d91f19a66d408ae46a1201a9e8..4ed498c63ceb55d15bd104a92b701ead3dfa81f2 100644
--- a/src/vs/platform/environment/node/environmentService.ts
+++ b/src/vs/platform/environment/node/environmentService.ts
@@ -38,6 +38,8 @@ export interface INativeEnvironmentService extends IEnvironmentService {
 	extensionsPath?: string;
 	extensionsDownloadPath: string;
 	builtinExtensionsPath: string;
+	extraExtensionPaths: string[];
+	extraBuiltinExtensionPaths: string[];
 
 	driverHandle?: string;
 	driverVerbose: boolean;
@@ -180,6 +182,13 @@ export class EnvironmentService implements INativeEnvironmentService {
 		return resources.joinPath(this.userHome, product.dataFolderName, 'extensions').fsPath;
 	}
 
+	@memoize get extraExtensionPaths(): string[] {
+		return (this._args['extra-extensions-dir'] || []).map((p) => <string>parsePathArg(p, process));
+	}
+	@memoize get extraBuiltinExtensionPaths(): string[] {
+		return (this._args['extra-builtin-extensions-dir'] || []).map((p) => <string>parsePathArg(p, process));
+	}
+
 	@memoize
 	get extensionDevelopmentLocationURI(): URI[] | undefined {
 		const s = this._args.extensionDevelopmentPath;
diff --git a/src/vs/platform/extensionManagement/node/extensionsScanner.ts b/src/vs/platform/extensionManagement/node/extensionsScanner.ts
index 575b2aafc3802cd6f5f943930e30de9f2c2690de..873181f967856759e3dc001e5bbe06e2f9eb676a 100644
--- a/src/vs/platform/extensionManagement/node/extensionsScanner.ts
+++ b/src/vs/platform/extensionManagement/node/extensionsScanner.ts
@@ -85,7 +85,7 @@ export class ExtensionsScanner extends Disposable {
 	}
 
 	async scanAllUserExtensions(): Promise<ILocalExtension[]> {
-		return this.scanExtensionsInDir(this.extensionsPath, ExtensionType.User);
+		return this.scanExtensionsInDirs(this.extensionsPath, this.environmentService.extraExtensionPaths, ExtensionType.User);
 	}
 
 	async extractUserExtension(identifierWithVersion: ExtensionIdentifierWithVersion, zipPath: string, token: CancellationToken): Promise<ILocalExtension> {
@@ -211,7 +211,13 @@ export class ExtensionsScanner extends Disposable {
 
 	private async scanExtensionsInDir(dir: string, type: ExtensionType): Promise<ILocalExtension[]> {
 		const limiter = new Limiter<any>(10);
-		const extensionsFolders = await pfs.readdir(dir);
+		const extensionsFolders = await pfs.readdir(dir)
+			.catch((error) => {
+				if (error.code !== 'ENOENT') {
+					throw error;
+				}
+				return <string[]>[];
+			});
 		const extensions = await Promise.all<ILocalExtension>(extensionsFolders.map(extensionFolder => limiter.queue(() => this.scanExtension(extensionFolder, dir, type))));
 		return extensions.filter(e => e && e.identifier);
 	}
@@ -241,7 +247,7 @@ export class ExtensionsScanner extends Disposable {
 	}
 
 	private async scanDefaultSystemExtensions(): Promise<ILocalExtension[]> {
-		const result = await this.scanExtensionsInDir(this.systemExtensionsPath, ExtensionType.System);
+		const result = await this.scanExtensionsInDirs(this.systemExtensionsPath, this.environmentService.extraBuiltinExtensionPaths, ExtensionType.System);
 		this.logService.trace('Scanned system extensions:', result.length);
 		return result;
 	}
@@ -345,4 +351,9 @@ export class ExtensionsScanner extends Disposable {
 			}
 		});
 	}
+
+	private async scanExtensionsInDirs(dir: string, dirs: string[], type: ExtensionType): Promise<ILocalExtension[]>{
+		const results = await Promise.all([dir, ...dirs].map((path) => this.scanExtensionsInDir(path, type)));
+		return results.reduce((flat, current) => flat.concat(current), []);
+	}
 }
diff --git a/src/vs/platform/product/common/product.ts b/src/vs/platform/product/common/product.ts
index bb33203d1727b1c076efac9113afc3b2580cdbd9..c53cea338cdaa0f0ac15542c129e1572b3f13b80 100644
--- a/src/vs/platform/product/common/product.ts
+++ b/src/vs/platform/product/common/product.ts
@@ -30,6 +30,12 @@ if (isWeb) {
 			],
 		});
 	}
+	// NOTE@coder: Add the ability to inject settings from the server.
+	const el = document.getElementById('vscode-remote-product-configuration');
+	const rawProductConfiguration = el && el.getAttribute('data-settings');
+	if (rawProductConfiguration) {
+		Object.assign(product, JSON.parse(rawProductConfiguration));
+	}
 }
 
 // Node: AMD loader
diff --git a/src/vs/platform/product/common/productService.ts b/src/vs/platform/product/common/productService.ts
index d1cb00a6d63621a4873a6a5e815220d084ceac2a..1a69d6f63a7406d364aa3e2b32fb75309f212e98 100644
--- a/src/vs/platform/product/common/productService.ts
+++ b/src/vs/platform/product/common/productService.ts
@@ -30,6 +30,8 @@ export type ConfigurationSyncStore = {
 };
 
 export interface IProductConfiguration {
+	readonly codeServerVersion?: string;
+
 	readonly version: string;
 	readonly date?: string;
 	readonly quality?: string;
diff --git a/src/vs/platform/remote/browser/browserSocketFactory.ts b/src/vs/platform/remote/browser/browserSocketFactory.ts
index 3715cbb8e6ee41c3d9b5090918d243b723ae2d00..c65de8ad37e727d66da97a8f8b170cbcef87181b 100644
--- a/src/vs/platform/remote/browser/browserSocketFactory.ts
+++ b/src/vs/platform/remote/browser/browserSocketFactory.ts
@@ -208,7 +208,8 @@ export class BrowserSocketFactory implements ISocketFactory {
 	}
 
 	connect(host: string, port: number, query: string, callback: IConnectCallback): void {
-		const socket = this._webSocketFactory.create(`ws://${host}:${port}/?${query}&skipWebSocketFrames=false`);
+		// NOTE@coder: Modified to work against the current path.
+		const socket = this._webSocketFactory.create(`${window.location.protocol === 'https:' ? 'wss' : 'ws'}://${window.location.host}${window.location.pathname}?${query}&skipWebSocketFrames=false`);
 		const errorListener = socket.onError((err) => callback(err, undefined));
 		socket.onOpen(() => {
 			errorListener.dispose();
@@ -216,6 +217,3 @@ export class BrowserSocketFactory implements ISocketFactory {
 		});
 	}
 }
-
-
-
diff --git a/src/vs/platform/remote/common/remoteAgentConnection.ts b/src/vs/platform/remote/common/remoteAgentConnection.ts
index 18d3d04fd20335975293e37b3b641120dd92da20..4e49f9d63623da6c84624144765f76ec127ea526 100644
--- a/src/vs/platform/remote/common/remoteAgentConnection.ts
+++ b/src/vs/platform/remote/common/remoteAgentConnection.ts
@@ -92,7 +92,7 @@ async function connectToRemoteExtensionHostAgent(options: ISimpleConnectionOptio
 		options.socketFactory.connect(
 			options.host,
 			options.port,
-			`reconnectionToken=${options.reconnectionToken}&reconnection=${options.reconnectionProtocol ? 'true' : 'false'}`,
+			`type=${connectionTypeToString(connectionType)}&reconnectionToken=${options.reconnectionToken}&reconnection=${options.reconnectionProtocol ? 'true' : 'false'}`,
 			(err: any, socket: ISocket | undefined) => {
 				if (err || !socket) {
 					options.logService.error(`${logPrefix} socketFactory.connect() failed. Error:`);
diff --git a/src/vs/platform/storage/browser/storageService.ts b/src/vs/platform/storage/browser/storageService.ts
index ab3fd347b69f8a3d9b96e706cd87c911b8ffed6b..9d351037b577f9f1edfd18ae9b3c48a211f4467f 100644
--- a/src/vs/platform/storage/browser/storageService.ts
+++ b/src/vs/platform/storage/browser/storageService.ts
@@ -122,8 +122,8 @@ export class BrowserStorageService extends Disposable implements IStorageService
 		return this.getStorage(scope).getNumber(key, fallbackValue);
 	}
 
-	store(key: string, value: string | boolean | number | undefined | null, scope: StorageScope): void {
-		this.getStorage(scope).set(key, value);
+	store(key: string, value: string | boolean | number | undefined | null, scope: StorageScope): Promise<void> {
+		return this.getStorage(scope).set(key, value);
 	}
 
 	remove(key: string, scope: StorageScope): void {
diff --git a/src/vs/platform/storage/common/storage.ts b/src/vs/platform/storage/common/storage.ts
index 6611f1dae42055f69a55c1c154d9475f11cd4d0a..d598d4909d5ff6d1614e4a038b1865e1f9a4e963 100644
--- a/src/vs/platform/storage/common/storage.ts
+++ b/src/vs/platform/storage/common/storage.ts
@@ -85,7 +85,7 @@ export interface IStorageService {
 	 * The scope argument allows to define the scope of the storage
 	 * operation to either the current workspace only or all workspaces.
 	 */
-	store(key: string, value: string | boolean | number | undefined | null, scope: StorageScope): void;
+	store(key: string, value: string | boolean | number | undefined | null, scope: StorageScope): Promise<void> | void;
 
 	/**
 	 * Delete an element stored under the provided key from storage.
diff --git a/src/vs/platform/storage/node/storageService.ts b/src/vs/platform/storage/node/storageService.ts
index ac657056aa68549f0053cfb1ec68835ba4ce20f9..143f9b5681eb867c5e5c5437946ab785eb34e4b4 100644
--- a/src/vs/platform/storage/node/storageService.ts
+++ b/src/vs/platform/storage/node/storageService.ts
@@ -202,8 +202,8 @@ export class NativeStorageService extends Disposable implements IStorageService
 		return this.getStorage(scope).getNumber(key, fallbackValue);
 	}
 
-	store(key: string, value: string | boolean | number | undefined | null, scope: StorageScope): void {
-		this.getStorage(scope).set(key, value);
+	store(key: string, value: string | boolean | number | undefined | null, scope: StorageScope): Promise<void> {
+		return this.getStorage(scope).set(key, value);
 	}
 
 	remove(key: string, scope: StorageScope): void {
diff --git a/src/vs/server/browser/client.ts b/src/vs/server/browser/client.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3c0703b7174ad792a4b42841e96ee93765d71601
--- /dev/null
+++ b/src/vs/server/browser/client.ts
@@ -0,0 +1,189 @@
+import { Emitter } from 'vs/base/common/event';
+import { URI } from 'vs/base/common/uri';
+import { localize } from 'vs/nls';
+import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
+import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
+import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
+import { ILocalizationsService } from 'vs/platform/localizations/common/localizations';
+import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
+import { Registry } from 'vs/platform/registry/common/platform';
+import { PersistentConnectionEventType } from 'vs/platform/remote/common/remoteAgentConnection';
+import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
+import { INodeProxyService, NodeProxyChannelClient } from 'vs/server/common/nodeProxy';
+import { TelemetryChannelClient } from 'vs/server/common/telemetry';
+import 'vs/workbench/contrib/localizations/browser/localizations.contribution';
+import { LocalizationsService } from 'vs/workbench/services/localizations/electron-browser/localizationsService';
+import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
+import { Options } from 'vs/server/ipc.d';
+import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
+
+class TelemetryService extends TelemetryChannelClient {
+	public constructor(
+		@IRemoteAgentService remoteAgentService: IRemoteAgentService,
+	) {
+		super(remoteAgentService.getConnection()!.getChannel('telemetry'));
+	}
+}
+
+/**
+ * Remove extra slashes in a URL.
+ */
+export const normalize = (url: string, keepTrailing = false): string => {
+	return url.replace(/\/\/+/g, "/").replace(/\/+$/, keepTrailing ? "/" : "");
+};
+
+/**
+ * Get options embedded in the HTML.
+ */
+export const getOptions = <T extends Options>(): T => {
+	try {
+		return JSON.parse(document.getElementById("coder-options")!.getAttribute("data-settings")!);
+	} catch (error) {
+		return {} as T;
+	}
+};
+
+const options = getOptions();
+
+const TELEMETRY_SECTION_ID = 'telemetry';
+Registry.as<IConfigurationRegistry>(Extensions.Configuration).registerConfiguration({
+	'id': TELEMETRY_SECTION_ID,
+	'order': 110,
+	'type': 'object',
+	'title': localize('telemetryConfigurationTitle', 'Telemetry'),
+	'properties': {
+		'telemetry.enableTelemetry': {
+			'type': 'boolean',
+			'description': localize('telemetry.enableTelemetry', 'Enable usage data and errors to be sent to a Microsoft online service.'),
+			'default': !options.disableTelemetry,
+			'tags': ['usesOnlineServices']
+		}
+	}
+});
+
+class NodeProxyService extends NodeProxyChannelClient implements INodeProxyService {
+	private readonly _onClose = new Emitter<void>();
+	public readonly onClose = this._onClose.event;
+	private readonly _onDown = new Emitter<void>();
+	public readonly onDown = this._onDown.event;
+	private readonly _onUp = new Emitter<void>();
+	public readonly onUp = this._onUp.event;
+
+	public constructor(
+		@IRemoteAgentService remoteAgentService: IRemoteAgentService,
+	) {
+		super(remoteAgentService.getConnection()!.getChannel('nodeProxy'));
+		remoteAgentService.getConnection()!.onDidStateChange((state) => {
+			switch (state.type) {
+				case PersistentConnectionEventType.ConnectionGain:
+					return this._onUp.fire();
+				case PersistentConnectionEventType.ConnectionLost:
+					return this._onDown.fire();
+				case PersistentConnectionEventType.ReconnectionPermanentFailure:
+					return this._onClose.fire();
+			}
+		});
+	}
+}
+
+registerSingleton(ILocalizationsService, LocalizationsService);
+registerSingleton(INodeProxyService, NodeProxyService);
+registerSingleton(ITelemetryService, TelemetryService);
+
+/**
+ * This is called by vs/workbench/browser/web.main.ts after the workbench has
+ * been initialized so we can initialize our own client-side code.
+ */
+export const initialize = async (services: ServiceCollection): Promise<void> => {
+	const event = new CustomEvent('ide-ready');
+	window.dispatchEvent(event);
+
+	if (parent) {
+		// Tell the parent loading has completed.
+		parent.postMessage({ event: 'loaded' }, window.location.origin);
+
+		// Proxy or stop proxing events as requested by the parent.
+		const listeners = new Map<string, (event: Event) => void>();
+		window.addEventListener('message', (parentEvent) => {
+			const eventName = parentEvent.data.bind || parentEvent.data.unbind;
+			if (eventName) {
+				const oldListener = listeners.get(eventName);
+				if (oldListener) {
+					document.removeEventListener(eventName, oldListener);
+				}
+			}
+
+			if (parentEvent.data.bind && parentEvent.data.prop) {
+				const listener = (event: Event) => {
+					parent.postMessage({
+						event: parentEvent.data.event,
+						[parentEvent.data.prop]: event[parentEvent.data.prop as keyof Event]
+					}, window.location.origin);
+				};
+				listeners.set(parentEvent.data.bind, listener);
+				document.addEventListener(parentEvent.data.bind, listener);
+			}
+		});
+	}
+
+	if (!window.isSecureContext) {
+		(services.get(INotificationService) as INotificationService).notify({
+			severity: Severity.Warning,
+			message: 'code-server is being accessed over an insecure domain. Web views, the clipboard, and other functionality will not work as expected.',
+			actions: {
+				primary: [{
+					id: 'understand',
+					label: 'I understand',
+					tooltip: '',
+					class: undefined,
+					enabled: true,
+					checked: true,
+					dispose: () => undefined,
+					run: () => {
+						return Promise.resolve();
+					}
+				}],
+			}
+		});
+	}
+
+	// This will be used to set the background color while VS Code loads.
+	const theme = (services.get(IStorageService) as IStorageService).get("colorThemeData", StorageScope.GLOBAL);
+	if (theme) {
+		localStorage.setItem("colorThemeData", theme);
+	}
+};
+
+export interface Query {
+	[key: string]: string | undefined;
+}
+
+/**
+ * Split a string up to the delimiter. If the delimiter doesn't exist the first
+ * item will have all the text and the second item will be an empty string.
+ */
+export const split = (str: string, delimiter: string): [string, string] => {
+	const index = str.indexOf(delimiter);
+	return index !== -1 ? [str.substring(0, index).trim(), str.substring(index + 1)] : [str, ''];
+};
+
+/**
+ * Return the URL modified with the specified query variables. It's pretty
+ * stupid so it probably doesn't cover any edge cases. Undefined values will
+ * unset existing values. Doesn't allow duplicates.
+ */
+export const withQuery = (url: string, replace: Query): string => {
+	const uri = URI.parse(url);
+	const query = { ...replace };
+	uri.query.split('&').forEach((kv) => {
+		const [key, value] = split(kv, '=');
+		if (!(key in query)) {
+			query[key] = value;
+		}
+	});
+	return uri.with({
+		query: Object.keys(query)
+			.filter((k) => typeof query[k] !== 'undefined')
+			.map((k) => `${k}=${query[k]}`).join('&'),
+	}).toString(true);
+};
diff --git a/src/vs/server/browser/extHostNodeProxy.ts b/src/vs/server/browser/extHostNodeProxy.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ed7c078077b0c375758529959b280e091436113a
--- /dev/null
+++ b/src/vs/server/browser/extHostNodeProxy.ts
@@ -0,0 +1,46 @@
+import { Emitter } from 'vs/base/common/event';
+import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
+import { ExtHostNodeProxyShape, MainContext, MainThreadNodeProxyShape } from 'vs/workbench/api/common/extHost.protocol';
+import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
+
+export class ExtHostNodeProxy implements ExtHostNodeProxyShape {
+	_serviceBrand: any;
+
+	private readonly _onMessage = new Emitter<string>();
+	public readonly onMessage = this._onMessage.event;
+	private readonly _onClose = new Emitter<void>();
+	public readonly onClose = this._onClose.event;
+	private readonly _onDown = new Emitter<void>();
+	public readonly onDown = this._onDown.event;
+	private readonly _onUp = new Emitter<void>();
+	public readonly onUp = this._onUp.event;
+
+	private readonly proxy: MainThreadNodeProxyShape;
+
+	constructor(@IExtHostRpcService rpc: IExtHostRpcService) {
+		this.proxy = rpc.getProxy(MainContext.MainThreadNodeProxy);
+	}
+
+	public $onMessage(message: string): void {
+		this._onMessage.fire(message);
+	}
+
+	public $onClose(): void {
+		this._onClose.fire();
+	}
+
+	public $onUp(): void {
+		this._onUp.fire();
+	}
+
+	public $onDown(): void {
+		this._onDown.fire();
+	}
+
+	public send(message: string): void {
+		this.proxy.$send(message);
+	}
+}
+
+export interface IExtHostNodeProxy extends ExtHostNodeProxy { }
+export const IExtHostNodeProxy = createDecorator<IExtHostNodeProxy>('IExtHostNodeProxy');
diff --git a/src/vs/server/browser/mainThreadNodeProxy.ts b/src/vs/server/browser/mainThreadNodeProxy.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0d2e93edae2baf34d27b7b52be0bf4960f244531
--- /dev/null
+++ b/src/vs/server/browser/mainThreadNodeProxy.ts
@@ -0,0 +1,37 @@
+import { IDisposable } from 'vs/base/common/lifecycle';
+import { INodeProxyService } from 'vs/server/common/nodeProxy';
+import { ExtHostContext, IExtHostContext, MainContext, MainThreadNodeProxyShape } from 'vs/workbench/api/common/extHost.protocol';
+import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
+
+@extHostNamedCustomer(MainContext.MainThreadNodeProxy)
+export class MainThreadNodeProxy implements MainThreadNodeProxyShape {
+	private disposed = false;
+	private disposables = <IDisposable[]>[];
+
+	constructor(
+		extHostContext: IExtHostContext,
+		@INodeProxyService private readonly proxyService: INodeProxyService,
+	) {
+		if (!extHostContext.remoteAuthority) { // HACK: A terrible way to detect if running in the worker.
+			const proxy = extHostContext.getProxy(ExtHostContext.ExtHostNodeProxy);
+			this.disposables = [
+				this.proxyService.onMessage((message: string) => proxy.$onMessage(message)),
+				this.proxyService.onClose(() => proxy.$onClose()),
+				this.proxyService.onDown(() => proxy.$onDown()),
+				this.proxyService.onUp(() => proxy.$onUp()),
+			];
+		}
+	}
+
+	$send(message: string): void {
+		if (!this.disposed) {
+			this.proxyService.send(message);
+		}
+	}
+
+	dispose(): void {
+		this.disposables.forEach((d) => d.dispose());
+		this.disposables = [];
+		this.disposed = true;
+	}
+}
diff --git a/src/vs/server/browser/worker.ts b/src/vs/server/browser/worker.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5ae44cdc856bf81326a4c516b8be9afb2c746a67
--- /dev/null
+++ b/src/vs/server/browser/worker.ts
@@ -0,0 +1,56 @@
+import { Client } from '@coder/node-browser';
+import { fromTar } from '@coder/requirefs';
+import { URI } from 'vs/base/common/uri';
+import { ILogService } from 'vs/platform/log/common/log';
+import { ExtensionActivationTimesBuilder } from 'vs/workbench/api/common/extHostExtensionActivator';
+import { IExtHostNodeProxy } from './extHostNodeProxy';
+
+export const loadCommonJSModule = async <T>(
+	module: URI,
+	activationTimesBuilder: ExtensionActivationTimesBuilder,
+	nodeProxy: IExtHostNodeProxy,
+	logService: ILogService,
+	vscode: any,
+): Promise<T> => {
+	const fetchUri = URI.from({
+		scheme: self.location.protocol.replace(':', ''),
+		authority: self.location.host,
+		path: self.location.pathname.replace(/\/static\/([^\/]+)\/.*$/, '/static/$1\/'),
+		query: `tar=${encodeURIComponent(module.path)}`,
+	});
+	const response = await fetch(fetchUri.toString(true));
+	if (response.status !== 200) {
+		throw new Error(`Failed to download extension "${module}"`);
+	}
+	const client = new Client(nodeProxy, { logger: logService });
+	const init = await client.handshake();
+	const buffer = new Uint8Array(await response.arrayBuffer());
+	const rfs = fromTar(buffer);
+	(<any>self).global = self;
+	rfs.provide('vscode', vscode);
+	Object.keys(client.modules).forEach((key) => {
+		const mod = (client.modules as any)[key];
+		if (key === 'process') {
+			(<any>self).process = mod;
+			(<any>self).process.env = init.env;
+			return;
+		}
+
+		rfs.provide(key, mod);
+		switch (key) {
+			case 'buffer':
+				(<any>self).Buffer = mod.Buffer;
+				break;
+			case 'timers':
+				(<any>self).setImmediate = mod.setImmediate;
+				break;
+		}
+	});
+
+	try {
+		activationTimesBuilder.codeLoadingStart();
+		return rfs.require('.');
+	} finally {
+		activationTimesBuilder.codeLoadingStop();
+	}
+};
diff --git a/src/vs/server/common/nodeProxy.ts b/src/vs/server/common/nodeProxy.ts
new file mode 100644
index 0000000000000000000000000000000000000000..14b9de879ceab4c1976770fa7810d276c5aa3e36
--- /dev/null
+++ b/src/vs/server/common/nodeProxy.ts
@@ -0,0 +1,47 @@
+import { ReadWriteConnection } from '@coder/node-browser';
+import { Event } from 'vs/base/common/event';
+import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc';
+import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
+
+export const INodeProxyService = createDecorator<INodeProxyService>('nodeProxyService');
+
+export interface INodeProxyService extends ReadWriteConnection {
+	_serviceBrand: any;
+	send(message: string): void;
+	onMessage: Event<string>;
+	onUp: Event<void>;
+	onClose: Event<void>;
+	onDown: Event<void>;
+}
+
+export class NodeProxyChannel implements IServerChannel {
+	constructor(private service: INodeProxyService) {}
+
+	listen(_: unknown, event: string): Event<any> {
+		switch (event) {
+			case 'onMessage': return this.service.onMessage;
+		}
+		throw new Error(`Invalid listen ${event}`);
+	}
+
+	async call(_: unknown, command: string, args?: any): Promise<any> {
+		switch (command) {
+			case 'send': return this.service.send(args[0]);
+		}
+		throw new Error(`Invalid call ${command}`);
+	}
+}
+
+export class NodeProxyChannelClient {
+	_serviceBrand: any;
+
+	public readonly onMessage: Event<string>;
+
+	constructor(private readonly channel: IChannel) {
+		this.onMessage = this.channel.listen<string>('onMessage');
+	}
+
+	public send(data: string): void {
+		this.channel.call('send', [data]);
+	}
+}
diff --git a/src/vs/server/common/telemetry.ts b/src/vs/server/common/telemetry.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4ea6d95d36aaac07dbd4d0e16ab3c1bba255f683
--- /dev/null
+++ b/src/vs/server/common/telemetry.ts
@@ -0,0 +1,65 @@
+import { ITelemetryData } from 'vs/base/common/actions';
+import { Event } from 'vs/base/common/event';
+import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc';
+import { ClassifiedEvent, GDPRClassification, StrictPropertyCheck } from 'vs/platform/telemetry/common/gdprTypings';
+import { ITelemetryInfo, ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
+
+export class TelemetryChannel implements IServerChannel {
+	constructor(private service: ITelemetryService) {}
+
+	listen(_: unknown, event: string): Event<any> {
+		throw new Error(`Invalid listen ${event}`);
+	}
+
+	call(_: unknown, command: string, args?: any): Promise<any> {
+		switch (command) {
+			case 'publicLog': return this.service.publicLog(args[0], args[1], args[2]);
+			case 'publicLog2': return this.service.publicLog2(args[0], args[1], args[2]);
+			case 'publicLogError': return this.service.publicLogError(args[0], args[1]);
+			case 'publicLogError2': return this.service.publicLogError2(args[0], args[1]);
+			case 'setEnabled': return Promise.resolve(this.service.setEnabled(args[0]));
+			case 'getTelemetryInfo': return this.service.getTelemetryInfo();
+			case 'setExperimentProperty': return Promise.resolve(this.service.setExperimentProperty(args[0], args[1]));
+		}
+		throw new Error(`Invalid call ${command}`);
+	}
+}
+
+export class TelemetryChannelClient implements ITelemetryService {
+	_serviceBrand: any;
+
+	// These don't matter; telemetry is sent to the Node side which decides
+	// whether to send the telemetry event.
+	public isOptedIn = true;
+	public sendErrorTelemetry = true;
+
+	constructor(private readonly channel: IChannel) {}
+
+	public publicLog(eventName: string, data?: ITelemetryData, anonymizeFilePaths?: boolean): Promise<void> {
+		return this.channel.call('publicLog', [eventName, data, anonymizeFilePaths]);
+	}
+
+	public publicLog2<E extends ClassifiedEvent<T> = never, T extends GDPRClassification<T> = never>(eventName: string, data?: StrictPropertyCheck<T, E>, anonymizeFilePaths?: boolean): Promise<void> {
+		return this.channel.call('publicLog2', [eventName, data, anonymizeFilePaths]);
+	}
+
+	public publicLogError(errorEventName: string, data?: ITelemetryData): Promise<void> {
+		return this.channel.call('publicLogError', [errorEventName, data]);
+	}
+
+	public publicLogError2<E extends ClassifiedEvent<T> = never, T extends GDPRClassification<T> = never>(eventName: string, data?: StrictPropertyCheck<T, E>): Promise<void> {
+		return this.channel.call('publicLogError2', [eventName, data]);
+	}
+
+	public setEnabled(value: boolean): void {
+		this.channel.call('setEnable', [value]);
+	}
+
+	public getTelemetryInfo(): Promise<ITelemetryInfo> {
+		return this.channel.call('getTelemetryInfo');
+	}
+
+	public setExperimentProperty(name: string, value: string): void {
+		this.channel.call('setExperimentProperty', [name, value]);
+	}
+}
diff --git a/src/vs/server/entry.ts b/src/vs/server/entry.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ab020fbb4e4ab3748cc807765ff9c362389faafa
--- /dev/null
+++ b/src/vs/server/entry.ts
@@ -0,0 +1,78 @@
+import { field } from '@coder/logger';
+import { setUnexpectedErrorHandler } from 'vs/base/common/errors';
+import { CodeServerMessage, VscodeMessage } from 'vs/server/ipc';
+import { logger } from 'vs/server/node/logger';
+import { enableCustomMarketplace } from 'vs/server/node/marketplace';
+import { Vscode } from 'vs/server/node/server';
+
+setUnexpectedErrorHandler((error) => logger.warn(error instanceof Error ? error.message : error));
+enableCustomMarketplace();
+
+/**
+ * Ensure we control when the process exits.
+ */
+const exit = process.exit;
+process.exit = function(code?: number) {
+	logger.warn(`process.exit() was prevented: ${code || 'unknown code'}.`);
+} as (code?: number) => never;
+
+// Kill VS Code if the parent process dies.
+if (typeof process.env.CODE_SERVER_PARENT_PID !== 'undefined') {
+	const parentPid = parseInt(process.env.CODE_SERVER_PARENT_PID, 10);
+	setInterval(() => {
+		try {
+			process.kill(parentPid, 0); // Throws an exception if the process doesn't exist anymore.
+		} catch (e) {
+			exit();
+		}
+	}, 5000);
+} else {
+	logger.error('no parent process');
+	exit(1);
+}
+
+const vscode = new Vscode();
+const send = (message: VscodeMessage): void => {
+	if (!process.send) {
+		throw new Error('not spawned with IPC');
+	}
+	process.send(message);
+};
+
+// Wait for the init message then start up VS Code. Subsequent messages will
+// return new workbench options without starting a new instance.
+process.on('message', async (message: CodeServerMessage, socket) => {
+	logger.debug('got message from code-server', field('message', message));
+	switch (message.type) {
+		case 'init':
+			try {
+				const options = await vscode.initialize(message.options);
+				send({ type: 'options', id: message.id, options });
+			} catch (error) {
+				logger.error(error.message);
+				logger.error(error.stack);
+				exit(1);
+			}
+			break;
+		case 'cli':
+			try {
+				await vscode.cli(message.args);
+				exit(0);
+			} catch (error) {
+				logger.error(error.message);
+				logger.error(error.stack);
+				exit(1);
+			}
+			break;
+		case 'socket':
+			vscode.handleWebSocket(socket, message.query);
+			break;
+	}
+});
+if (!process.send) {
+	logger.error('not spawned with IPC');
+	exit(1);
+} else {
+	// This lets the parent know the child is ready to receive messages.
+	send({ type: 'ready' });
+}
diff --git a/src/vs/server/fork.js b/src/vs/server/fork.js
new file mode 100644
index 0000000000000000000000000000000000000000..56331ff1fc32bbd82e769aaecb551e427f798ec3
--- /dev/null
+++ b/src/vs/server/fork.js
@@ -0,0 +1,3 @@
+// This must be a JS file otherwise when it gets compiled it turns into AMD
+// syntax which will not work without the right loader.
+require('../../bootstrap-amd').load('vs/server/entry');
diff --git a/src/vs/server/ipc.d.ts b/src/vs/server/ipc.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..33b28cf2d53746ee9c50c056ac2e087dcee0a4e2
--- /dev/null
+++ b/src/vs/server/ipc.d.ts
@@ -0,0 +1,131 @@
+/**
+ * External interfaces for integration into code-server over IPC. No vs imports
+ * should be made in this file.
+ */
+export interface Options {
+	disableTelemetry: boolean
+}
+
+export interface InitMessage {
+	type: 'init';
+	id: string;
+	options: VscodeOptions;
+}
+
+export type Query = { [key: string]: string | string[] | undefined };
+
+export interface SocketMessage {
+	type: 'socket';
+	query: Query;
+}
+
+export interface CliMessage {
+	type: 'cli';
+	args: Args;
+}
+
+export interface OpenCommandPipeArgs {
+	type: 'open';
+	fileURIs?: string[];
+	folderURIs: string[];
+	forceNewWindow?: boolean;
+	diffMode?: boolean;
+	addMode?: boolean;
+	gotoLineMode?: boolean;
+	forceReuseWindow?: boolean;
+	waitMarkerFilePath?: string;
+}
+
+export type CodeServerMessage = InitMessage | SocketMessage | CliMessage;
+
+export interface ReadyMessage {
+	type: 'ready';
+}
+
+export interface OptionsMessage {
+	id: string;
+	type: 'options';
+	options: WorkbenchOptions;
+}
+
+export type VscodeMessage = ReadyMessage | OptionsMessage;
+
+export interface StartPath {
+	url: string;
+	workspace: boolean;
+}
+
+export interface Args {
+	'user-data-dir'?: string;
+
+	'enable-proposed-api'?: string[];
+	'extensions-dir'?: string;
+	'builtin-extensions-dir'?: string;
+	'extra-extensions-dir'?: string[];
+	'extra-builtin-extensions-dir'?: string[];
+
+	locale?: string
+
+	log?: string;
+	verbose?: boolean;
+
+	_: string[];
+}
+
+export interface VscodeOptions {
+	readonly args: Args;
+	readonly remoteAuthority: string;
+	readonly startPath?: StartPath;
+}
+
+export interface VscodeOptionsMessage extends VscodeOptions {
+	readonly id: string;
+}
+
+export interface UriComponents {
+	readonly scheme: string;
+	readonly authority: string;
+	readonly path: string;
+	readonly query: string;
+	readonly fragment: string;
+}
+
+export interface NLSConfiguration {
+	locale: string;
+	availableLanguages: {
+		[key: string]: string;
+	};
+	pseudo?: boolean;
+	_languagePackSupport?: boolean;
+}
+
+export interface WorkbenchOptions {
+	readonly workbenchWebConfiguration: {
+		readonly remoteAuthority?: string;
+		readonly folderUri?: UriComponents;
+		readonly workspaceUri?: UriComponents;
+		readonly logLevel?: number;
+		readonly workspaceProvider?: {
+			payload: [
+				["userDataPath", string],
+				["enableProposedApi", string],
+			];
+		};
+	};
+	readonly remoteUserDataUri: UriComponents;
+	readonly productConfiguration: {
+		codeServerVersion?: string;
+		readonly extensionsGallery?: {
+			readonly serviceUrl: string;
+			readonly itemUrl: string;
+			readonly controlUrl: string;
+			readonly recommendationsUrl: string;
+		};
+	};
+	readonly nlsConfiguration: NLSConfiguration;
+	readonly commit: string;
+}
+
+export interface WorkbenchOptionsMessage {
+	id: string;
+}
diff --git a/src/vs/server/node/channel.ts b/src/vs/server/node/channel.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e10cc9c218b27d859a523be3db5b8a30ef90d953
--- /dev/null
+++ b/src/vs/server/node/channel.ts
@@ -0,0 +1,360 @@
+import { Server } from '@coder/node-browser';
+import * as path from 'path';
+import { VSBuffer } from 'vs/base/common/buffer';
+import { CancellationTokenSource } from 'vs/base/common/cancellation';
+import { Emitter, Event } from 'vs/base/common/event';
+import { IDisposable } from 'vs/base/common/lifecycle';
+import { OS } from 'vs/base/common/platform';
+import { ReadableStreamEventPayload } from 'vs/base/common/stream';
+import { URI, UriComponents } from 'vs/base/common/uri';
+import { transformOutgoingURIs } from 'vs/base/common/uriIpc';
+import { IServerChannel } from 'vs/base/parts/ipc/common/ipc';
+import { IDiagnosticInfo } from 'vs/platform/diagnostics/common/diagnostics';
+import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService';
+import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
+import { FileDeleteOptions, FileOpenOptions, FileOverwriteOptions, FileReadStreamOptions, FileType, FileWriteOptions, IStat, IWatchOptions } from 'vs/platform/files/common/files';
+import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider';
+import { ILogService } from 'vs/platform/log/common/log';
+import product from 'vs/platform/product/common/product';
+import { IRemoteAgentEnvironment, RemoteAgentConnectionContext } from 'vs/platform/remote/common/remoteAgentEnvironment';
+import { ITelemetryData, ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
+import { INodeProxyService } from 'vs/server/common/nodeProxy';
+import { getTranslations } from 'vs/server/node/nls';
+import { getUriTransformer } from 'vs/server/node/util';
+import { IFileChangeDto } from 'vs/workbench/api/common/extHost.protocol';
+import { ExtensionScanner, ExtensionScannerInput } from 'vs/workbench/services/extensions/node/extensionPoints';
+
+/**
+ * Extend the file provider to allow unwatching.
+ */
+class Watcher extends DiskFileSystemProvider {
+	public readonly watches = new Map<number, IDisposable>();
+
+	public dispose(): void {
+		this.watches.forEach((w) => w.dispose());
+		this.watches.clear();
+		super.dispose();
+	}
+
+	public _watch(req: number, resource: URI, opts: IWatchOptions): void {
+		this.watches.set(req, this.watch(resource, opts));
+	}
+
+	public unwatch(req: number): void {
+		this.watches.get(req)!.dispose();
+		this.watches.delete(req);
+	}
+}
+
+export class FileProviderChannel implements IServerChannel<RemoteAgentConnectionContext>, IDisposable {
+	private readonly provider: DiskFileSystemProvider;
+	private readonly watchers = new Map<string, Watcher>();
+
+	public constructor(
+		private readonly environmentService: INativeEnvironmentService,
+		private readonly logService: ILogService,
+	) {
+		this.provider = new DiskFileSystemProvider(this.logService);
+	}
+
+	public listen(context: RemoteAgentConnectionContext, event: string, args?: any): Event<any> {
+		switch (event) {
+			case 'filechange': return this.filechange(context, args[0]);
+			case 'readFileStream': return this.readFileStream(args[0], args[1]);
+		}
+
+		throw new Error(`Invalid listen '${event}'`);
+	}
+
+	private filechange(context: RemoteAgentConnectionContext, session: string): Event<IFileChangeDto[]> {
+		const emitter = new Emitter<IFileChangeDto[]>({
+			onFirstListenerAdd: () => {
+				const provider = new Watcher(this.logService);
+				this.watchers.set(session, provider);
+				const transformer = getUriTransformer(context.remoteAuthority);
+				provider.onDidChangeFile((events) => {
+					emitter.fire(events.map((event) => ({
+						...event,
+						resource: transformer.transformOutgoing(event.resource),
+					})));
+				});
+				provider.onDidErrorOccur((event) => this.logService.error(event));
+			},
+			onLastListenerRemove: () => {
+				this.watchers.get(session)!.dispose();
+				this.watchers.delete(session);
+			},
+		});
+
+		return emitter.event;
+	}
+
+	private readFileStream(resource: UriComponents, opts: FileReadStreamOptions): Event<ReadableStreamEventPayload<VSBuffer>> {
+		const cts = new CancellationTokenSource();
+		const fileStream = this.provider.readFileStream(this.transform(resource), opts, cts.token);
+		const emitter = new Emitter<ReadableStreamEventPayload<VSBuffer>>({
+			onFirstListenerAdd: () => {
+				fileStream.on('data', (data) => emitter.fire(VSBuffer.wrap(data)));
+				fileStream.on('error', (error) => emitter.fire(error));
+				fileStream.on('end', () => emitter.fire('end'));
+			},
+			onLastListenerRemove: () => cts.cancel(),
+		});
+
+		return emitter.event;
+	}
+
+	public call(_: unknown, command: string, args?: any): Promise<any> {
+		switch (command) {
+			case 'stat': return this.stat(args[0]);
+			case 'open': return this.open(args[0], args[1]);
+			case 'close': return this.close(args[0]);
+			case 'read': return this.read(args[0], args[1], args[2]);
+			case 'readFile': return this.readFile(args[0]);
+			case 'write': return this.write(args[0], args[1], args[2], args[3], args[4]);
+			case 'writeFile': return this.writeFile(args[0], args[1], args[2]);
+			case 'delete': return this.delete(args[0], args[1]);
+			case 'mkdir': return this.mkdir(args[0]);
+			case 'readdir': return this.readdir(args[0]);
+			case 'rename': return this.rename(args[0], args[1], args[2]);
+			case 'copy': return this.copy(args[0], args[1], args[2]);
+			case 'watch': return this.watch(args[0], args[1], args[2], args[3]);
+			case 'unwatch': return this.unwatch(args[0], args[1]);
+		}
+
+		throw new Error(`Invalid call '${command}'`);
+	}
+
+	public dispose(): void {
+		this.watchers.forEach((w) => w.dispose());
+		this.watchers.clear();
+	}
+
+	private async stat(resource: UriComponents): Promise<IStat> {
+		return this.provider.stat(this.transform(resource));
+	}
+
+	private async open(resource: UriComponents, opts: FileOpenOptions): Promise<number> {
+		return this.provider.open(this.transform(resource), opts);
+	}
+
+	private async close(fd: number): Promise<void> {
+		return this.provider.close(fd);
+	}
+
+	private async read(fd: number, pos: number, length: number): Promise<[VSBuffer, number]> {
+		const buffer = VSBuffer.alloc(length);
+		const bytesRead = await this.provider.read(fd, pos, buffer.buffer, 0, length);
+		return [buffer, bytesRead];
+	}
+
+	private async readFile(resource: UriComponents): Promise<VSBuffer> {
+		return VSBuffer.wrap(await this.provider.readFile(this.transform(resource)));
+	}
+
+	private write(fd: number, pos: number, buffer: VSBuffer, offset: number, length: number): Promise<number> {
+		return this.provider.write(fd, pos, buffer.buffer, offset, length);
+	}
+
+	private writeFile(resource: UriComponents, buffer: VSBuffer, opts: FileWriteOptions): Promise<void> {
+		return this.provider.writeFile(this.transform(resource), buffer.buffer, opts);
+	}
+
+	private async delete(resource: UriComponents, opts: FileDeleteOptions): Promise<void> {
+		return this.provider.delete(this.transform(resource), opts);
+	}
+
+	private async mkdir(resource: UriComponents): Promise<void> {
+		return this.provider.mkdir(this.transform(resource));
+	}
+
+	private async readdir(resource: UriComponents): Promise<[string, FileType][]> {
+		return this.provider.readdir(this.transform(resource));
+	}
+
+	private async rename(resource: UriComponents, target: UriComponents, opts: FileOverwriteOptions): Promise<void> {
+		return this.provider.rename(this.transform(resource), URI.from(target), opts);
+	}
+
+	private copy(resource: UriComponents, target: UriComponents, opts: FileOverwriteOptions): Promise<void> {
+		return this.provider.copy(this.transform(resource), URI.from(target), opts);
+	}
+
+	private async watch(session: string, req: number, resource: UriComponents, opts: IWatchOptions): Promise<void> {
+		this.watchers.get(session)!._watch(req, this.transform(resource), opts);
+	}
+
+	private async unwatch(session: string, req: number): Promise<void> {
+		this.watchers.get(session)!.unwatch(req);
+	}
+
+	private transform(resource: UriComponents): URI {
+		// Used for walkthrough content.
+		if (/^\/static[^/]*\//.test(resource.path)) {
+			return URI.file(this.environmentService.appRoot + resource.path.replace(/^\/static[^/]*\//, '/'));
+		// Used by the webview service worker to load resources.
+		} else if (resource.path === '/vscode-resource' && resource.query) {
+			try {
+				const query = JSON.parse(resource.query);
+				if (query.requestResourcePath) {
+					return URI.file(query.requestResourcePath);
+				}
+			} catch (error) { /* Carry on. */ }
+		}
+		return URI.from(resource);
+	}
+}
+
+// See ../../workbench/services/remote/common/remoteAgentEnvironmentChannel.ts
+export class ExtensionEnvironmentChannel implements IServerChannel {
+	public constructor(
+		private readonly environment: INativeEnvironmentService,
+		private readonly log: ILogService,
+		private readonly telemetry: ITelemetryService,
+		private readonly connectionToken: string,
+	) {}
+
+	public listen(_: unknown, event: string): Event<any> {
+		throw new Error(`Invalid listen '${event}'`);
+	}
+
+	public async call(context: any, command: string, args: any): Promise<any> {
+		switch (command) {
+			case 'getEnvironmentData':
+				return transformOutgoingURIs(
+					await this.getEnvironmentData(),
+					getUriTransformer(context.remoteAuthority),
+				);
+			case 'scanExtensions':
+				return transformOutgoingURIs(
+					await this.scanExtensions(args.language),
+					getUriTransformer(context.remoteAuthority),
+				);
+			case 'getDiagnosticInfo': return this.getDiagnosticInfo();
+			case 'disableTelemetry': return this.disableTelemetry();
+			case 'logTelemetry': return this.logTelemetry(args[0], args[1]);
+			case 'flushTelemetry': return this.flushTelemetry();
+		}
+		throw new Error(`Invalid call '${command}'`);
+	}
+
+	private async getEnvironmentData(): Promise<IRemoteAgentEnvironment> {
+		return {
+			pid: process.pid,
+			connectionToken: this.connectionToken,
+			appRoot: URI.file(this.environment.appRoot),
+			settingsPath: this.environment.settingsResource,
+			logsPath: URI.file(this.environment.logsPath),
+			extensionsPath: URI.file(this.environment.extensionsPath!),
+			extensionHostLogsPath: URI.file(path.join(this.environment.logsPath, 'extension-host')),
+			globalStorageHome: this.environment.globalStorageHome,
+			workspaceStorageHome: this.environment.workspaceStorageHome,
+			userHome: this.environment.userHome,
+			os: OS,
+		};
+	}
+
+	private async scanExtensions(language: string): Promise<IExtensionDescription[]> {
+		const translations = await getTranslations(language, this.environment.userDataPath);
+
+		const scanMultiple = (isBuiltin: boolean, isUnderDevelopment: boolean, paths: string[]): Promise<IExtensionDescription[][]> => {
+			return Promise.all(paths.map((path) => {
+				return ExtensionScanner.scanExtensions(new ExtensionScannerInput(
+					product.version,
+					product.commit,
+					language,
+					!!process.env.VSCODE_DEV,
+					path,
+					isBuiltin,
+					isUnderDevelopment,
+					translations,
+				), this.log);
+			}));
+		};
+
+		const scanBuiltin = async (): Promise<IExtensionDescription[][]> => {
+			return scanMultiple(true, false, [this.environment.builtinExtensionsPath, ...this.environment.extraBuiltinExtensionPaths]);
+		};
+
+		const scanInstalled = async (): Promise<IExtensionDescription[][]> => {
+			return scanMultiple(false, true, [this.environment.extensionsPath!, ...this.environment.extraExtensionPaths]);
+		};
+
+		return Promise.all([scanBuiltin(), scanInstalled()]).then((allExtensions) => {
+			const uniqueExtensions = new Map<string, IExtensionDescription>();
+			allExtensions.forEach((multipleExtensions) => {
+				multipleExtensions.forEach((extensions) => {
+					extensions.forEach((extension) => {
+						const id = ExtensionIdentifier.toKey(extension.identifier);
+						if (uniqueExtensions.has(id)) {
+							const oldPath = uniqueExtensions.get(id)!.extensionLocation.fsPath;
+							const newPath = extension.extensionLocation.fsPath;
+							this.log.warn(`${oldPath} has been overridden ${newPath}`);
+						}
+						uniqueExtensions.set(id, {
+							...extension,
+							// Force extensions that should run on the client due to latency
+							// issues.
+							extensionKind: extension.identifier.value === 'vscodevim.vim'
+								? [ 'web' ]
+								: extension.extensionKind,
+						});
+					});
+				});
+			});
+			return Array.from(uniqueExtensions.values());
+		});
+	}
+
+	private getDiagnosticInfo(): Promise<IDiagnosticInfo> {
+		throw new Error('not implemented');
+	}
+
+	private async disableTelemetry(): Promise<void> {
+		this.telemetry.setEnabled(false);
+	}
+
+	private async logTelemetry(eventName: string, data: ITelemetryData): Promise<void> {
+		this.telemetry.publicLog(eventName, data);
+	}
+
+	private async flushTelemetry(): Promise<void> {
+		// We always send immediately at the moment.
+	}
+}
+
+export class NodeProxyService implements INodeProxyService {
+	public _serviceBrand = undefined;
+
+	public readonly server: Server;
+
+	private readonly _onMessage = new Emitter<string>();
+	public readonly onMessage = this._onMessage.event;
+	private readonly _$onMessage = new Emitter<string>();
+	public readonly $onMessage = this._$onMessage.event;
+	public readonly _onDown = new Emitter<void>();
+	public readonly onDown = this._onDown.event;
+	public readonly _onUp = new Emitter<void>();
+	public readonly onUp = this._onUp.event;
+
+	// Unused because the server connection will never permanently close.
+	private readonly _onClose = new Emitter<void>();
+	public readonly onClose = this._onClose.event;
+
+	public constructor() {
+		// TODO: down/up
+		this.server = new Server({
+			onMessage: this.$onMessage,
+			onClose: this.onClose,
+			onDown: this.onDown,
+			onUp: this.onUp,
+			send: (message: string): void => {
+				this._onMessage.fire(message);
+			}
+		});
+	}
+
+	public send(message: string): void {
+		this._$onMessage.fire(message);
+	}
+}
diff --git a/src/vs/server/node/connection.ts b/src/vs/server/node/connection.ts
new file mode 100644
index 0000000000000000000000000000000000000000..36e80fb6966ae2cb53c98f3d31e2193d00c509c3
--- /dev/null
+++ b/src/vs/server/node/connection.ts
@@ -0,0 +1,157 @@
+import * as cp from 'child_process';
+import { getPathFromAmdModule } from 'vs/base/common/amd';
+import { VSBuffer } from 'vs/base/common/buffer';
+import { Emitter } from 'vs/base/common/event';
+import { ISocket } from 'vs/base/parts/ipc/common/ipc.net';
+import { NodeSocket } from 'vs/base/parts/ipc/node/ipc.net';
+import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService';
+import { ILogService } from 'vs/platform/log/common/log';
+import { getNlsConfiguration } from 'vs/server/node/nls';
+import { Protocol } from 'vs/server/node/protocol';
+import { IExtHostReadyMessage } from 'vs/workbench/services/extensions/common/extensionHostProtocol';
+
+export abstract class Connection {
+	private readonly _onClose = new Emitter<void>();
+	public readonly onClose = this._onClose.event;
+	private disposed = false;
+	private _offline: number | undefined;
+
+	public constructor(protected protocol: Protocol, public readonly token: string) {}
+
+	public get offline(): number | undefined {
+		return this._offline;
+	}
+
+	public reconnect(socket: ISocket, buffer: VSBuffer): void {
+		this._offline = undefined;
+		this.doReconnect(socket, buffer);
+	}
+
+	public dispose(): void {
+		if (!this.disposed) {
+			this.disposed = true;
+			this.doDispose();
+			this._onClose.fire();
+		}
+	}
+
+	protected setOffline(): void {
+		if (!this._offline) {
+			this._offline = Date.now();
+		}
+	}
+
+	/**
+	 * Set up the connection on a new socket.
+	 */
+	protected abstract doReconnect(socket: ISocket, buffer: VSBuffer): void;
+	protected abstract doDispose(): void;
+}
+
+/**
+ * Used for all the IPC channels.
+ */
+export class ManagementConnection extends Connection {
+	public constructor(protected protocol: Protocol, token: string) {
+		super(protocol, token);
+		protocol.onClose(() => this.dispose()); // Explicit close.
+		protocol.onSocketClose(() => this.setOffline()); // Might reconnect.
+	}
+
+	protected doDispose(): void {
+		this.protocol.sendDisconnect();
+		this.protocol.dispose();
+		this.protocol.getUnderlyingSocket().destroy();
+	}
+
+	protected doReconnect(socket: ISocket, buffer: VSBuffer): void {
+		this.protocol.beginAcceptReconnection(socket, buffer);
+		this.protocol.endAcceptReconnection();
+	}
+}
+
+export class ExtensionHostConnection extends Connection {
+	private process?: cp.ChildProcess;
+
+	public constructor(
+		locale:string, protocol: Protocol, buffer: VSBuffer, token: string,
+		private readonly log: ILogService,
+		private readonly environment: INativeEnvironmentService,
+	) {
+		super(protocol, token);
+		this.protocol.dispose();
+		this.spawn(locale, buffer).then((p) => this.process = p);
+		this.protocol.getUnderlyingSocket().pause();
+	}
+
+	protected doDispose(): void {
+		if (this.process) {
+			this.process.kill();
+		}
+		this.protocol.getUnderlyingSocket().destroy();
+	}
+
+	protected doReconnect(socket: ISocket, buffer: VSBuffer): void {
+		// This is just to set the new socket.
+		this.protocol.beginAcceptReconnection(socket, null);
+		this.protocol.dispose();
+		this.sendInitMessage(buffer);
+	}
+
+	private sendInitMessage(buffer: VSBuffer): void {
+		const socket = this.protocol.getUnderlyingSocket();
+		socket.pause();
+		this.process!.send({ // Process must be set at this point.
+			type: 'VSCODE_EXTHOST_IPC_SOCKET',
+			initialDataChunk: (buffer.buffer as Buffer).toString('base64'),
+			skipWebSocketFrames: this.protocol.getSocket() instanceof NodeSocket,
+		}, socket);
+	}
+
+	private async spawn(locale: string, buffer: VSBuffer): Promise<cp.ChildProcess> {
+		const config = await getNlsConfiguration(locale, this.environment.userDataPath);
+		const proc = cp.fork(
+			getPathFromAmdModule(require, 'bootstrap-fork'),
+			[ '--type=extensionHost' ],
+			{
+				env: {
+					...process.env,
+					AMD_ENTRYPOINT: 'vs/workbench/services/extensions/node/extensionHostProcess',
+					PIPE_LOGGING: 'true',
+					VERBOSE_LOGGING: 'true',
+					VSCODE_EXTHOST_WILL_SEND_SOCKET: 'true',
+					VSCODE_HANDLES_UNCAUGHT_ERRORS: 'true',
+					VSCODE_LOG_STACK: 'false',
+					VSCODE_LOG_LEVEL: process.env.LOG_LEVEL,
+					VSCODE_NLS_CONFIG: JSON.stringify(config),
+				},
+				silent: true,
+			},
+		);
+
+		proc.on('error', () => this.dispose());
+		proc.on('exit', () => this.dispose());
+		if (proc.stdout && proc.stderr) {
+			proc.stdout.setEncoding('utf8').on('data', (d) => this.log.info('Extension host stdout', d));
+			proc.stderr.setEncoding('utf8').on('data', (d) => this.log.error('Extension host stderr', d));
+		}
+		proc.on('message', (event) => {
+			if (event && event.type === '__$console') {
+				const severity = (<any>this.log)[event.severity] ? event.severity : 'info';
+				(<any>this.log)[severity]('Extension host', event.arguments);
+			}
+			if (event && event.type === 'VSCODE_EXTHOST_DISCONNECTED') {
+				this.setOffline();
+			}
+		});
+
+		const listen = (message: IExtHostReadyMessage) => {
+			if (message.type === 'VSCODE_EXTHOST_IPC_READY') {
+				proc.removeListener('message', listen);
+				this.sendInitMessage(buffer);
+			}
+		};
+
+		return proc.on('message', listen);
+	}
+}
diff --git a/src/vs/server/node/insights.ts b/src/vs/server/node/insights.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a0ece345f28f06afb2af12fe4901ad228b2475a4
--- /dev/null
+++ b/src/vs/server/node/insights.ts
@@ -0,0 +1,124 @@
+import * as appInsights from 'applicationinsights';
+import * as https from 'https';
+import * as http from 'http';
+import * as os from 'os';
+
+class Channel {
+	public get _sender() {
+		throw new Error('unimplemented');
+	}
+	public get _buffer() {
+		throw new Error('unimplemented');
+	}
+
+	public setUseDiskRetryCaching(): void {
+		throw new Error('unimplemented');
+	}
+	public send(): void {
+		throw new Error('unimplemented');
+	}
+	public triggerSend(): void {
+		throw new Error('unimplemented');
+	}
+}
+
+export class TelemetryClient  {
+	public context: any = undefined;
+	public commonProperties: any = undefined;
+	public config: any = {};
+
+	public channel: any = new Channel();
+
+	public addTelemetryProcessor(): void {
+		throw new Error('unimplemented');
+	}
+
+	public clearTelemetryProcessors(): void {
+		throw new Error('unimplemented');
+	}
+
+	public runTelemetryProcessors(): void {
+		throw new Error('unimplemented');
+	}
+
+	public trackTrace(): void {
+		throw new Error('unimplemented');
+	}
+
+	public trackMetric(): void {
+		throw new Error('unimplemented');
+	}
+
+	public trackException(): void {
+		throw new Error('unimplemented');
+	}
+
+	public trackRequest(): void {
+		throw new Error('unimplemented');
+	}
+
+	public trackDependency(): void {
+		throw new Error('unimplemented');
+	}
+
+	public track(): void {
+		throw new Error('unimplemented');
+	}
+
+	public trackNodeHttpRequestSync(): void {
+		throw new Error('unimplemented');
+	}
+
+	public trackNodeHttpRequest(): void {
+		throw new Error('unimplemented');
+	}
+
+	public trackNodeHttpDependency(): void {
+		throw new Error('unimplemented');
+	}
+
+	public trackEvent(options: appInsights.Contracts.EventTelemetry): void {
+		if (!options.properties) {
+			options.properties = {};
+		}
+		if (!options.measurements) {
+			options.measurements = {};
+		}
+
+		try {
+			const cpus = os.cpus();
+			options.measurements.cores = cpus.length;
+			options.properties['common.cpuModel'] = cpus[0].model;
+		} catch (error) {}
+
+		try {
+			options.measurements.memoryFree = os.freemem();
+			options.measurements.memoryTotal = os.totalmem();
+		} catch (error) {}
+
+		try {
+			options.properties['common.shell'] = os.userInfo().shell;
+			options.properties['common.release'] = os.release();
+			options.properties['common.arch'] = os.arch();
+		} catch (error) {}
+
+		try {
+			const url = process.env.TELEMETRY_URL || 'https://v1.telemetry.coder.com/track';
+			const request = (/^http:/.test(url) ? http : https).request(url, {
+				method: 'POST',
+				headers: {
+					'Content-Type': 'application/json',
+				},
+			});
+			request.on('error', () => { /* We don't care. */ });
+			request.write(JSON.stringify(options));
+			request.end();
+		} catch (error) {}
+	}
+
+	public flush(options: { callback: (v: string) => void }): void {
+		if (options.callback) {
+			options.callback('');
+		}
+	}
+}
diff --git a/src/vs/server/node/ipc.ts b/src/vs/server/node/ipc.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5e560eb46e6a0a18c91e440c655ac0d44b09b6dd
--- /dev/null
+++ b/src/vs/server/node/ipc.ts
@@ -0,0 +1,61 @@
+import * as cp from 'child_process';
+import { Emitter } from 'vs/base/common/event';
+
+enum ControlMessage {
+	okToChild = 'ok>',
+	okFromChild = 'ok<',
+}
+
+interface RelaunchMessage {
+	type: 'relaunch';
+	version: string;
+}
+
+export type Message = RelaunchMessage;
+
+class IpcMain {
+	protected readonly _onMessage = new Emitter<Message>();
+	public readonly onMessage = this._onMessage.event;
+
+	public handshake(child?: cp.ChildProcess): Promise<void> {
+		return new Promise((resolve, reject) => {
+			const target = child || process;
+			if (!target.send) {
+				throw new Error('Not spawned with IPC enabled');
+			}
+			target.on('message', (message) => {
+				if (message === child ? ControlMessage.okFromChild : ControlMessage.okToChild) {
+					target.removeAllListeners();
+					target.on('message', (msg) => this._onMessage.fire(msg));
+					if (child) {
+						target.send!(ControlMessage.okToChild);
+					}
+					resolve();
+				}
+			});
+			if (child) {
+				child.once('error', reject);
+				child.once('exit', (code) => {
+					const error = new Error(`Unexpected exit with code ${code}`);
+					(error as any).code = code;
+					reject(error);
+				});
+			} else {
+				target.send(ControlMessage.okFromChild);
+			}
+		});
+	}
+
+	public relaunch(version: string): void {
+		this.send({ type: 'relaunch', version });
+	}
+
+	private send(message: Message): void {
+		if (!process.send) {
+			throw new Error('Not a child process with IPC enabled');
+		}
+		process.send(message);
+	}
+}
+
+export const ipcMain = new IpcMain();
diff --git a/src/vs/server/node/logger.ts b/src/vs/server/node/logger.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2a39c524aaa1b4031e04a631842f30b6fec3d98a
--- /dev/null
+++ b/src/vs/server/node/logger.ts
@@ -0,0 +1,2 @@
+import { logger as baseLogger } from '@coder/logger';
+export const logger = baseLogger.named('vscode');
diff --git a/src/vs/server/node/marketplace.ts b/src/vs/server/node/marketplace.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8956fc40d48448b9932036c4c286464881807338
--- /dev/null
+++ b/src/vs/server/node/marketplace.ts
@@ -0,0 +1,174 @@
+import * as fs from 'fs';
+import * as path from 'path';
+import * as tarStream from 'tar-stream';
+import * as util from 'util';
+import { CancellationToken } from 'vs/base/common/cancellation';
+import { mkdirp } from 'vs/base/node/pfs';
+import * as vszip from 'vs/base/node/zip';
+import * as nls from 'vs/nls';
+import product from 'vs/platform/product/common/product';
+
+// We will be overriding these, so keep a reference to the original.
+const vszipExtract = vszip.extract;
+const vszipBuffer = vszip.buffer;
+
+export interface IExtractOptions {
+	overwrite?: boolean;
+	/**
+	 * Source path within the TAR/ZIP archive. Only the files
+	 * contained in this path will be extracted.
+	 */
+	sourcePath?: string;
+}
+
+export interface IFile {
+	path: string;
+	contents?: Buffer | string;
+	localPath?: string;
+}
+
+export const tar = async (tarPath: string, files: IFile[]): Promise<string> => {
+	const pack = tarStream.pack();
+	const chunks: Buffer[] = [];
+	const ended = new Promise<Buffer>((resolve) => {
+		pack.on('end', () => resolve(Buffer.concat(chunks)));
+	});
+	pack.on('data', (chunk: Buffer) => chunks.push(chunk));
+	for (let i = 0; i < files.length; i++) {
+		const file = files[i];
+		pack.entry({ name: file.path }, file.contents);
+	}
+	pack.finalize();
+	await util.promisify(fs.writeFile)(tarPath, await ended);
+	return tarPath;
+};
+
+export const extract = async (archivePath: string, extractPath: string, options: IExtractOptions = {}, token: CancellationToken): Promise<void> => {
+	try {
+		await extractTar(archivePath, extractPath, options, token);
+	} catch (error) {
+		if (error.toString().includes('Invalid tar header')) {
+			await vszipExtract(archivePath, extractPath, options, token);
+		}
+	}
+};
+
+export const buffer = (targetPath: string, filePath: string): Promise<Buffer> => {
+	return new Promise<Buffer>(async (resolve, reject) => {
+		try {
+			let done: boolean = false;
+			await extractAssets(targetPath, new RegExp(filePath), (assetPath: string, data: Buffer) => {
+				if (path.normalize(assetPath) === path.normalize(filePath)) {
+					done = true;
+					resolve(data);
+				}
+			});
+			if (!done) {
+				throw new Error('couldn\'t find asset ' + filePath);
+			}
+		} catch (error) {
+			if (error.toString().includes('Invalid tar header')) {
+				vszipBuffer(targetPath, filePath).then(resolve).catch(reject);
+			} else {
+				reject(error);
+			}
+		}
+	});
+};
+
+const extractAssets = async (tarPath: string, match: RegExp, callback: (path: string, data: Buffer) => void): Promise<void> => {
+	return new Promise<void>((resolve, reject): void => {
+		const extractor = tarStream.extract();
+		const fail = (error: Error) => {
+			extractor.destroy();
+			reject(error);
+		};
+		extractor.once('error', fail);
+		extractor.on('entry', async (header, stream, next) => {
+			const name = header.name;
+			if (match.test(name)) {
+				extractData(stream).then((data) => {
+					callback(name, data);
+					next();
+				}).catch(fail);
+			} else {
+				stream.on('end', () => next());
+				stream.resume(); // Just drain it.
+			}
+		});
+		extractor.on('finish', resolve);
+		fs.createReadStream(tarPath).pipe(extractor);
+	});
+};
+
+const extractData = (stream: NodeJS.ReadableStream): Promise<Buffer> => {
+	return new Promise((resolve, reject): void => {
+		const fileData: Buffer[] = [];
+		stream.on('error', reject);
+		stream.on('end', () => resolve(Buffer.concat(fileData)));
+		stream.on('data', (data) => fileData.push(data));
+	});
+};
+
+const extractTar = async (tarPath: string, targetPath: string, options: IExtractOptions = {}, token: CancellationToken): Promise<void> => {
+	return new Promise<void>((resolve, reject): void => {
+		const sourcePathRegex = new RegExp(options.sourcePath ? `^${options.sourcePath}` : '');
+		const extractor = tarStream.extract();
+		const fail = (error: Error) => {
+			extractor.destroy();
+			reject(error);
+		};
+		extractor.once('error', fail);
+		extractor.on('entry', async (header, stream, next) => {
+			const nextEntry = (): void => {
+				stream.on('end', () => next());
+				stream.resume();
+			};
+
+			const rawName = path.normalize(header.name);
+			if (token.isCancellationRequested || !sourcePathRegex.test(rawName)) {
+				return nextEntry();
+			}
+
+			const fileName = rawName.replace(sourcePathRegex, '');
+			const targetFileName = path.join(targetPath, fileName);
+			if (/\/$/.test(fileName)) {
+				return mkdirp(targetFileName).then(nextEntry);
+			}
+
+			const dirName = path.dirname(fileName);
+			const targetDirName = path.join(targetPath, dirName);
+			if (targetDirName.indexOf(targetPath) !== 0) {
+				return fail(new Error(nls.localize('invalid file', 'Error extracting {0}. Invalid file.', fileName)));
+			}
+
+			await mkdirp(targetDirName, undefined);
+
+			const fstream = fs.createWriteStream(targetFileName, { mode: header.mode });
+			fstream.once('close', () => next());
+			fstream.once('error', fail);
+			stream.pipe(fstream);
+		});
+		extractor.once('finish', resolve);
+		fs.createReadStream(tarPath).pipe(extractor);
+	});
+};
+
+/**
+ * Override original functionality so we can use a custom marketplace with
+ * either tars or zips.
+ */
+export const enableCustomMarketplace = (): void => {
+	(<any>product).extensionsGallery = { // Use `any` to override readonly.
+		serviceUrl: process.env.SERVICE_URL || 'https://extensions.coder.com/api',
+		itemUrl: process.env.ITEM_URL || '',
+		controlUrl: '',
+		recommendationsUrl: '',
+		...(product.extensionsGallery || {}),
+	};
+
+	const target = vszip as typeof vszip;
+	target.zip = tar;
+	target.extract = extract;
+	target.buffer = buffer;
+};
diff --git a/src/vs/server/node/nls.ts b/src/vs/server/node/nls.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3d428a57d31f29c40f9c3ce45f715b443badf4e9
--- /dev/null
+++ b/src/vs/server/node/nls.ts
@@ -0,0 +1,88 @@
+import * as fs from 'fs';
+import * as path from 'path';
+import * as util from 'util';
+import { getPathFromAmdModule } from 'vs/base/common/amd';
+import * as lp from 'vs/base/node/languagePacks';
+import product from 'vs/platform/product/common/product';
+import { Translations } from 'vs/workbench/services/extensions/common/extensionPoints';
+
+const configurations = new Map<string, Promise<lp.NLSConfiguration>>();
+const metadataPath = path.join(getPathFromAmdModule(require, ''), 'nls.metadata.json');
+
+export const isInternalConfiguration = (config: lp.NLSConfiguration): config is lp.InternalNLSConfiguration => {
+	return config && !!(<lp.InternalNLSConfiguration>config)._languagePackId;
+};
+
+const DefaultConfiguration = {
+	locale: 'en',
+	availableLanguages: {},
+};
+
+export const getNlsConfiguration = async (locale: string, userDataPath: string): Promise<lp.NLSConfiguration> => {
+	const id = `${locale}: ${userDataPath}`;
+	if (!configurations.has(id)) {
+		configurations.set(id, new Promise(async (resolve) =>  {
+			const config = product.commit && await util.promisify(fs.exists)(metadataPath)
+				? await lp.getNLSConfiguration(product.commit, userDataPath, metadataPath, locale)
+				: DefaultConfiguration;
+			if (isInternalConfiguration(config)) {
+				config._languagePackSupport = true;
+			}
+			// If the configuration has no results keep trying since code-server
+			// doesn't restart when a language is installed so this result would
+			// persist (the plugin might not be installed yet or something).
+			if (config.locale !== 'en' && config.locale !== 'en-us' && Object.keys(config.availableLanguages).length === 0) {
+				configurations.delete(id);
+			}
+			resolve(config);
+		}));
+	}
+	return configurations.get(id)!;
+};
+
+export const getTranslations = async (locale: string, userDataPath: string): Promise<Translations> => {
+	const config = await getNlsConfiguration(locale, userDataPath);
+	if (isInternalConfiguration(config)) {
+		try {
+			return JSON.parse(await util.promisify(fs.readFile)(config._translationsConfigFile, 'utf8'));
+		} catch (error) { /* Nothing yet. */}
+	}
+	return {};
+};
+
+export const getLocaleFromConfig = async (userDataPath: string): Promise<string> => {
+	const files = ['locale.json', 'argv.json'];
+	for (let i = 0; i < files.length; ++i) {
+		try {
+			const localeConfigUri = path.join(userDataPath, 'User', files[i]);
+			const content = stripComments(await util.promisify(fs.readFile)(localeConfigUri, 'utf8'));
+			return JSON.parse(content).locale;
+		} catch (error) { /* Ignore. */ }
+	}
+	return 'en';
+};
+
+// Taken from src/main.js in the main VS Code source.
+const stripComments = (content: string): string => {
+	const regexp = /('(?:[^\\']*(?:\\.)?)*')|('(?:[^\\']*(?:\\.)?)*')|(\/\*(?:\r?\n|.)*?\*\/)|(\/{2,}.*?(?:(?:\r?\n)|$))/g;
+
+	return content.replace(regexp, (match, _m1, _m2, m3, m4) => {
+		// Only one of m1, m2, m3, m4 matches
+		if (m3) {
+			// A block comment. Replace with nothing
+			return '';
+		} else if (m4) {
+			// A line comment. If it ends in \r?\n then keep it.
+			const length_1 = m4.length;
+			if (length_1 > 2 && m4[length_1 - 1] === '\n') {
+				return m4[length_1 - 2] === '\r' ? '\r\n' : '\n';
+			}
+			else {
+				return '';
+			}
+		} else {
+			// We match a string
+			return match;
+		}
+	});
+};
diff --git a/src/vs/server/node/protocol.ts b/src/vs/server/node/protocol.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3c74512192aec6220216bc8563b3127b9cfd5fbf
--- /dev/null
+++ b/src/vs/server/node/protocol.ts
@@ -0,0 +1,73 @@
+import * as net from 'net';
+import { VSBuffer } from 'vs/base/common/buffer';
+import { PersistentProtocol } from 'vs/base/parts/ipc/common/ipc.net';
+import { NodeSocket, WebSocketNodeSocket } from 'vs/base/parts/ipc/node/ipc.net';
+import { AuthRequest, ConnectionTypeRequest, HandshakeMessage } from 'vs/platform/remote/common/remoteAgentConnection';
+
+export interface SocketOptions {
+	readonly reconnectionToken: string;
+	readonly reconnection: boolean;
+	readonly skipWebSocketFrames: boolean;
+}
+
+export class Protocol extends PersistentProtocol {
+	public constructor(socket: net.Socket, public readonly options: SocketOptions) {
+		super(
+			options.skipWebSocketFrames
+				? new NodeSocket(socket)
+				: new WebSocketNodeSocket(new NodeSocket(socket)),
+		);
+	}
+
+	public getUnderlyingSocket(): net.Socket {
+		const socket = this.getSocket();
+		return socket instanceof NodeSocket
+			? socket.socket
+			: (socket as WebSocketNodeSocket).socket.socket;
+	}
+
+	/**
+	 * Perform a handshake to get a connection request.
+	 */
+	public handshake(): Promise<ConnectionTypeRequest> {
+		return new Promise((resolve, reject) => {
+			const handler = this.onControlMessage((rawMessage) => {
+				try {
+					const message = JSON.parse(rawMessage.toString());
+					switch (message.type) {
+						case 'auth': return this.authenticate(message);
+						case 'connectionType':
+							handler.dispose();
+							return resolve(message);
+						default: throw new Error('Unrecognized message type');
+					}
+				} catch (error) {
+					handler.dispose();
+					reject(error);
+				}
+			});
+		});
+	}
+
+	/**
+	 * TODO: This ignores the authentication process entirely for now.
+	 */
+	private authenticate(_message: AuthRequest): void {
+		this.sendMessage({ type: 'sign', data: '' });
+	}
+
+	/**
+	 * TODO: implement.
+	 */
+	public tunnel(): void {
+		throw new Error('Tunnel is not implemented yet');
+	}
+
+	/**
+	 * Send a handshake message. In the case of the extension host, it just sends
+	 * back a debug port.
+	 */
+	public sendMessage(message: HandshakeMessage | { debugPort?: number } ): void {
+		this.sendControl(VSBuffer.fromString(JSON.stringify(message)));
+	}
+}
diff --git a/src/vs/server/node/server.ts b/src/vs/server/node/server.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4b88fedb2f05d300fb50978e63721d4d04b7fb5f
--- /dev/null
+++ b/src/vs/server/node/server.ts
@@ -0,0 +1,285 @@
+import * as fs from 'fs';
+import * as net from 'net';
+import * as path from 'path';
+import { Emitter } from 'vs/base/common/event';
+import { Schemas } from 'vs/base/common/network';
+import { URI } from 'vs/base/common/uri';
+import { getMachineId } from 'vs/base/node/id';
+import { ClientConnectionEvent, createChannelReceiver, IPCServer, IServerChannel } from 'vs/base/parts/ipc/common/ipc';
+import { LogsDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/logsDataCleaner';
+import { main } from "vs/code/node/cliProcessMain";
+import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
+import { ConfigurationService } from 'vs/platform/configuration/common/configurationService';
+import { ExtensionHostDebugBroadcastChannel } from 'vs/platform/debug/common/extensionHostDebugIpc';
+import { IEnvironmentService } from 'vs/platform/environment/common/environment';
+import { ParsedArgs } from 'vs/platform/environment/node/argv';
+import { EnvironmentService, INativeEnvironmentService } from 'vs/platform/environment/node/environmentService';
+import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService';
+import { IExtensionGalleryService, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
+import { ExtensionManagementChannel } from 'vs/platform/extensionManagement/common/extensionManagementIpc';
+import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService';
+import { IFileService } from 'vs/platform/files/common/files';
+import { FileService } from 'vs/platform/files/common/fileService';
+import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider';
+import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
+import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
+import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
+import { ILocalizationsService } from 'vs/platform/localizations/common/localizations';
+import { LocalizationsService } from 'vs/platform/localizations/node/localizations';
+import { getLogLevel, ILogService } from 'vs/platform/log/common/log';
+import { LoggerChannel } from 'vs/platform/log/common/logIpc';
+import { SpdLogService } from 'vs/platform/log/node/spdlogService';
+import product from 'vs/platform/product/common/product';
+import { IProductService } from 'vs/platform/product/common/productService';
+import { ConnectionType, ConnectionTypeRequest } from 'vs/platform/remote/common/remoteAgentConnection';
+import { RemoteAgentConnectionContext } from 'vs/platform/remote/common/remoteAgentEnvironment';
+import { IRequestService } from 'vs/platform/request/common/request';
+import { RequestChannel } from 'vs/platform/request/common/requestIpc';
+import { RequestService } from 'vs/platform/request/node/requestService';
+import ErrorTelemetry from 'vs/platform/telemetry/browser/errorTelemetry';
+import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
+import { TelemetryService } from 'vs/platform/telemetry/common/telemetryService';
+import { combinedAppender, LogAppender, NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
+import { AppInsightsAppender } from 'vs/platform/telemetry/node/appInsightsAppender';
+import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProperties';
+import { INodeProxyService, NodeProxyChannel } from 'vs/server/common/nodeProxy';
+import { TelemetryChannel } from 'vs/server/common/telemetry';
+import { Query, VscodeOptions, WorkbenchOptions } from 'vs/server/ipc';
+import { ExtensionEnvironmentChannel, FileProviderChannel, NodeProxyService } from 'vs/server/node/channel';
+import { Connection, ExtensionHostConnection, ManagementConnection } from 'vs/server/node/connection';
+import { TelemetryClient } from 'vs/server/node/insights';
+import { logger } from 'vs/server/node/logger';
+import { getLocaleFromConfig, getNlsConfiguration } from 'vs/server/node/nls';
+import { Protocol } from 'vs/server/node/protocol';
+import { getUriTransformer } from 'vs/server/node/util';
+import { REMOTE_FILE_SYSTEM_CHANNEL_NAME } from "vs/workbench/services/remote/common/remoteAgentFileSystemChannel";
+import { RemoteExtensionLogFileName } from 'vs/workbench/services/remote/common/remoteAgentService';
+
+export class Vscode {
+	public readonly _onDidClientConnect = new Emitter<ClientConnectionEvent>();
+	public readonly onDidClientConnect = this._onDidClientConnect.event;
+	private readonly ipc = new IPCServer<RemoteAgentConnectionContext>(this.onDidClientConnect);
+
+	private readonly maxExtraOfflineConnections = 0;
+	private readonly connections = new Map<ConnectionType, Map<string, Connection>>();
+
+	private readonly services = new ServiceCollection();
+	private servicesPromise?: Promise<void>;
+
+	public async cli(args: ParsedArgs): Promise<void> {
+		return main(args);
+	}
+
+	public async initialize(options: VscodeOptions): Promise<WorkbenchOptions> {
+		const transformer = getUriTransformer(options.remoteAuthority);
+		if (!this.servicesPromise) {
+			this.servicesPromise = this.initializeServices(options.args);
+		}
+		await this.servicesPromise;
+		const environment = this.services.get(IEnvironmentService) as INativeEnvironmentService;
+		const startPath = options.startPath;
+		const parseUrl = (url: string): URI => {
+			// This might be a fully-specified URL or just a path.
+			try {
+				return URI.parse(url, true);
+			} catch (error) {
+				return URI.from({
+					scheme: Schemas.vscodeRemote,
+					authority: options.remoteAuthority,
+					path: url,
+				});
+			}
+		};
+		return {
+			workbenchWebConfiguration: {
+				workspaceUri: startPath && startPath.workspace ? parseUrl(startPath.url) : undefined,
+				folderUri: startPath && !startPath.workspace ? parseUrl(startPath.url) : undefined,
+				remoteAuthority: options.remoteAuthority,
+				logLevel: getLogLevel(environment),
+				workspaceProvider: {
+					payload: [
+						["userDataPath", environment.userDataPath],
+						["enableProposedApi", JSON.stringify(options.args["enable-proposed-api"] || [])]
+					],
+				},
+			},
+			remoteUserDataUri: transformer.transformOutgoing(URI.file(environment.userDataPath)),
+			productConfiguration: product,
+			nlsConfiguration: await getNlsConfiguration(environment.args.locale || await getLocaleFromConfig(environment.userDataPath), environment.userDataPath),
+			commit: product.commit || 'development',
+		};
+	}
+
+	public async handleWebSocket(socket: net.Socket, query: Query): Promise<true> {
+		if (!query.reconnectionToken) {
+			throw new Error('Reconnection token is missing from query parameters');
+		}
+		const protocol = new Protocol(socket, {
+			reconnectionToken: <string>query.reconnectionToken,
+			reconnection: query.reconnection === 'true',
+			skipWebSocketFrames: query.skipWebSocketFrames === 'true',
+		});
+		try {
+			await this.connect(await protocol.handshake(), protocol);
+		} catch (error) {
+			protocol.sendMessage({ type: 'error', reason: error.message });
+			protocol.dispose();
+			protocol.getSocket().dispose();
+		}
+		return true;
+	}
+
+	private async connect(message: ConnectionTypeRequest, protocol: Protocol): Promise<void> {
+		if (product.commit && message.commit !== product.commit) {
+			logger.warn(`Version mismatch (${message.commit} instead of ${product.commit})`);
+		}
+
+		switch (message.desiredConnectionType) {
+			case ConnectionType.ExtensionHost:
+			case ConnectionType.Management:
+				if (!this.connections.has(message.desiredConnectionType)) {
+					this.connections.set(message.desiredConnectionType, new Map());
+				}
+				const connections = this.connections.get(message.desiredConnectionType)!;
+
+				const ok = async () => {
+					return message.desiredConnectionType === ConnectionType.ExtensionHost
+						? { debugPort: await this.getDebugPort() }
+						: { type: 'ok' };
+				};
+
+				const token = protocol.options.reconnectionToken;
+				if (protocol.options.reconnection && connections.has(token)) {
+					protocol.sendMessage(await ok());
+					const buffer = protocol.readEntireBuffer();
+					protocol.dispose();
+					return connections.get(token)!.reconnect(protocol.getSocket(), buffer);
+				} else if (protocol.options.reconnection || connections.has(token)) {
+					throw new Error(protocol.options.reconnection
+						? 'Unrecognized reconnection token'
+						: 'Duplicate reconnection token'
+					);
+				}
+
+				protocol.sendMessage(await ok());
+
+				let connection: Connection;
+				if (message.desiredConnectionType === ConnectionType.Management) {
+					connection = new ManagementConnection(protocol, token);
+					this._onDidClientConnect.fire({
+						protocol, onDidClientDisconnect: connection.onClose,
+					});
+					// TODO: Need a way to match clients with a connection. For now
+					// dispose everything which only works because no extensions currently
+					// utilize long-running proxies.
+					(this.services.get(INodeProxyService) as NodeProxyService)._onUp.fire();
+					connection.onClose(() => (this.services.get(INodeProxyService) as NodeProxyService)._onDown.fire());
+				} else {
+					const buffer = protocol.readEntireBuffer();
+					connection = new ExtensionHostConnection(
+						message.args ? message.args.language : 'en',
+						protocol, buffer, token,
+						this.services.get(ILogService) as ILogService,
+						this.services.get(IEnvironmentService) as INativeEnvironmentService,
+					);
+				}
+				connections.set(token, connection);
+				connection.onClose(() => connections.delete(token));
+				this.disposeOldOfflineConnections(connections);
+				break;
+			case ConnectionType.Tunnel: return protocol.tunnel();
+			default: throw new Error('Unrecognized connection type');
+		}
+	}
+
+	private disposeOldOfflineConnections(connections: Map<string, Connection>): void {
+		const offline = Array.from(connections.values())
+			.filter((connection) => typeof connection.offline !== 'undefined');
+		for (let i = 0, max = offline.length - this.maxExtraOfflineConnections; i < max; ++i) {
+			offline[i].dispose();
+		}
+	}
+
+	private async initializeServices(args: ParsedArgs): Promise<void> {
+		const environmentService = new EnvironmentService(args, process.execPath);
+		// https://github.com/cdr/code-server/issues/1693
+		fs.mkdirSync(environmentService.globalStorageHome.fsPath, { recursive: true });
+
+		const logService = new SpdLogService(RemoteExtensionLogFileName, environmentService.logsPath, getLogLevel(environmentService));
+		const fileService = new FileService(logService);
+		fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(logService));
+
+		const piiPaths = [
+			path.join(environmentService.userDataPath, 'clp'), // Language packs.
+			environmentService.appRoot,
+			environmentService.extensionsPath,
+			environmentService.builtinExtensionsPath,
+			...environmentService.extraExtensionPaths,
+			...environmentService.extraBuiltinExtensionPaths,
+		];
+
+		this.ipc.registerChannel('logger', new LoggerChannel(logService));
+		this.ipc.registerChannel(ExtensionHostDebugBroadcastChannel.ChannelName, new ExtensionHostDebugBroadcastChannel());
+
+		this.services.set(ILogService, logService);
+		this.services.set(IEnvironmentService, environmentService);
+
+		const configurationService = new ConfigurationService(environmentService.settingsResource, fileService);
+		await configurationService.initialize();
+		this.services.set(IConfigurationService, configurationService);
+
+		this.services.set(IRequestService, new SyncDescriptor(RequestService));
+		this.services.set(IFileService, fileService);
+		this.services.set(IProductService, { _serviceBrand: undefined, ...product });
+		this.services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService));
+		this.services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService));
+
+		if (!environmentService.disableTelemetry) {
+			this.services.set(ITelemetryService, new TelemetryService({
+				appender: combinedAppender(
+					new AppInsightsAppender('code-server', null, () => new TelemetryClient() as any, logService),
+					new LogAppender(logService),
+				),
+				sendErrorTelemetry: true,
+				commonProperties: resolveCommonProperties(
+					product.commit, product.version, await getMachineId(),
+					[], environmentService.installSourcePath, 'code-server',
+				),
+				piiPaths,
+			}, configurationService));
+		} else {
+			this.services.set(ITelemetryService, NullTelemetryService);
+		}
+
+		await new Promise((resolve) => {
+			const instantiationService = new InstantiationService(this.services);
+			this.services.set(ILocalizationsService, instantiationService.createInstance(LocalizationsService));
+			this.services.set(INodeProxyService, instantiationService.createInstance(NodeProxyService));
+
+			instantiationService.invokeFunction(() => {
+				instantiationService.createInstance(LogsDataCleaner);
+				const telemetryService = this.services.get(ITelemetryService) as ITelemetryService;
+				this.ipc.registerChannel('extensions', new ExtensionManagementChannel(
+					this.services.get(IExtensionManagementService) as IExtensionManagementService,
+					(context) => getUriTransformer(context.remoteAuthority),
+				));
+				this.ipc.registerChannel('remoteextensionsenvironment', new ExtensionEnvironmentChannel(
+					environmentService, logService, telemetryService, '',
+				));
+				this.ipc.registerChannel('request', new RequestChannel(this.services.get(IRequestService) as IRequestService));
+				this.ipc.registerChannel('telemetry', new TelemetryChannel(telemetryService));
+				this.ipc.registerChannel('nodeProxy', new NodeProxyChannel(this.services.get(INodeProxyService) as INodeProxyService));
+				this.ipc.registerChannel('localizations', <IServerChannel<any>>createChannelReceiver(this.services.get(ILocalizationsService) as ILocalizationsService));
+				this.ipc.registerChannel(REMOTE_FILE_SYSTEM_CHANNEL_NAME, new FileProviderChannel(environmentService, logService));
+				resolve(new ErrorTelemetry(telemetryService));
+			});
+		});
+	}
+
+	/**
+	 * TODO: implement.
+	 */
+	private async getDebugPort(): Promise<number | undefined> {
+		return undefined;
+	}
+}
diff --git a/src/vs/server/node/util.ts b/src/vs/server/node/util.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fa47e993b46802f1a26457649e9e8bc467a73bf2
--- /dev/null
+++ b/src/vs/server/node/util.ts
@@ -0,0 +1,13 @@
+import { URITransformer } from 'vs/base/common/uriIpc';
+
+export const getUriTransformer = (remoteAuthority: string): URITransformer => {
+	return new URITransformer(remoteAuthority);
+};
+
+/**
+ * Encode a path for opening via the folder or workspace query parameter. This
+ * preserves slashes so it can be edited by hand more easily.
+ */
+export const encodePath = (path: string): string => {
+	return path.split("/").map((p) => encodeURIComponent(p)).join("/");
+};
diff --git a/src/vs/workbench/api/browser/extensionHost.contribution.ts b/src/vs/workbench/api/browser/extensionHost.contribution.ts
index bfabf0008910c87146df53a2e10fe63bae517a86..32b3b1cf84c8d280fd7f03d541b867691d51c2fb 100644
--- a/src/vs/workbench/api/browser/extensionHost.contribution.ts
+++ b/src/vs/workbench/api/browser/extensionHost.contribution.ts
@@ -60,6 +60,7 @@ import './mainThreadComments';
 import './mainThreadNotebook';
 import './mainThreadTask';
 import './mainThreadLabelService';
+import 'vs/server/browser/mainThreadNodeProxy';
 import './mainThreadTunnelService';
 import './mainThreadAuthentication';
 import './mainThreadTimeline';
diff --git a/src/vs/workbench/api/browser/mainThreadStorage.ts b/src/vs/workbench/api/browser/mainThreadStorage.ts
index 7bc3904963bed2925f3640b6bd929347159dd3cf..c6db2368ae9eaca61889efcf3c49763c01ff7459 100644
--- a/src/vs/workbench/api/browser/mainThreadStorage.ts
+++ b/src/vs/workbench/api/browser/mainThreadStorage.ts
@@ -58,11 +58,11 @@ export class MainThreadStorage implements MainThreadStorageShape {
 		return JSON.parse(jsonValue);
 	}
 
-	$setValue(shared: boolean, key: string, value: object): Promise<void> {
+	async $setValue(shared: boolean, key: string, value: object): Promise<void> {
 		let jsonValue: string;
 		try {
 			jsonValue = JSON.stringify(value);
-			this._storageService.store(key, jsonValue, shared ? StorageScope.GLOBAL : StorageScope.WORKSPACE);
+			await this._storageService.store(key, jsonValue, shared ? StorageScope.GLOBAL : StorageScope.WORKSPACE);
 		} catch (err) {
 			return Promise.reject(err);
 		}
diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts
index 3595cd3e38136222044a13050b15105bbe539068..989caefff7c4b8203c03cec8fa451f5e70ea8964 100644
--- a/src/vs/workbench/api/common/extHost.api.impl.ts
+++ b/src/vs/workbench/api/common/extHost.api.impl.ts
@@ -68,6 +68,7 @@ import { IURITransformerService } from 'vs/workbench/api/common/extHostUriTransf
 import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
 import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
 import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook';
+import { IExtHostNodeProxy } from 'vs/server/browser/extHostNodeProxy';
 import { ExtHostTheming } from 'vs/workbench/api/common/extHostTheming';
 import { IExtHostTunnelService } from 'vs/workbench/api/common/extHostTunnelService';
 import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService';
@@ -100,6 +101,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
 	const extHostStorage = accessor.get(IExtHostStorage);
 	const extensionStoragePaths = accessor.get(IExtensionStoragePaths);
 	const extHostLogService = accessor.get(ILogService);
+	const extHostNodeProxy = accessor.get(IExtHostNodeProxy);
 	const extHostTunnelService = accessor.get(IExtHostTunnelService);
 	const extHostApiDeprecation = accessor.get(IExtHostApiDeprecationService);
 	const extHostWindow = accessor.get(IExtHostWindow);
@@ -110,6 +112,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
 	rpcProtocol.set(ExtHostContext.ExtHostConfiguration, extHostConfiguration);
 	rpcProtocol.set(ExtHostContext.ExtHostExtensionService, extensionService);
 	rpcProtocol.set(ExtHostContext.ExtHostStorage, extHostStorage);
+	rpcProtocol.set(ExtHostContext.ExtHostNodeProxy, extHostNodeProxy);
 	rpcProtocol.set(ExtHostContext.ExtHostTunnelService, extHostTunnelService);
 	rpcProtocol.set(ExtHostContext.ExtHostWindow, extHostWindow);
 
diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts
index 4b7946662950f18179a5b6e3552abd39e68ca80e..ca1352d311a94b42e18d0d9e4859b18ec2bb271d 100644
--- a/src/vs/workbench/api/common/extHost.protocol.ts
+++ b/src/vs/workbench/api/common/extHost.protocol.ts
@@ -795,6 +795,16 @@ export interface MainThreadLabelServiceShape extends IDisposable {
 	$unregisterResourceLabelFormatter(handle: number): void;
 }
 
+export interface MainThreadNodeProxyShape extends IDisposable {
+	$send(message: string): void;
+}
+export interface ExtHostNodeProxyShape {
+	$onMessage(message: string): void;
+	$onClose(): void;
+	$onDown(): void;
+	$onUp(): void;
+}
+
 export interface MainThreadSearchShape extends IDisposable {
 	$registerFileSearchProvider(handle: number, scheme: string): void;
 	$registerTextSearchProvider(handle: number, scheme: string): void;
@@ -1765,6 +1775,7 @@ export const MainContext = {
 	MainThreadWindow: createMainId<MainThreadWindowShape>('MainThreadWindow'),
 	MainThreadLabelService: createMainId<MainThreadLabelServiceShape>('MainThreadLabelService'),
 	MainThreadNotebook: createMainId<MainThreadNotebookShape>('MainThreadNotebook'),
+	MainThreadNodeProxy: createMainId<MainThreadNodeProxyShape>('MainThreadNodeProxy'),
 	MainThreadTheming: createMainId<MainThreadThemingShape>('MainThreadTheming'),
 	MainThreadTunnelService: createMainId<MainThreadTunnelServiceShape>('MainThreadTunnelService'),
 	MainThreadTimeline: createMainId<MainThreadTimelineShape>('MainThreadTimeline')
@@ -1806,6 +1817,7 @@ export const ExtHostContext = {
 	ExtHostOutputService: createMainId<ExtHostOutputServiceShape>('ExtHostOutputService'),
 	ExtHosLabelService: createMainId<ExtHostLabelServiceShape>('ExtHostLabelService'),
 	ExtHostNotebook: createMainId<ExtHostNotebookShape>('ExtHostNotebook'),
+	ExtHostNodeProxy: createMainId<ExtHostNodeProxyShape>('ExtHostNodeProxy'),
 	ExtHostTheming: createMainId<ExtHostThemingShape>('ExtHostTheming'),
 	ExtHostTunnelService: createMainId<ExtHostTunnelServiceShape>('ExtHostTunnelService'),
 	ExtHostAuthentication: createMainId<ExtHostAuthenticationShape>('ExtHostAuthentication'),
diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts
index 0bb5188614bcbf98b85c9208edc2b173f70b1670..38ff3e2e05645be8df619ed2b47fa2984b918741 100644
--- a/src/vs/workbench/api/common/extHostExtensionService.ts
+++ b/src/vs/workbench/api/common/extHostExtensionService.ts
@@ -31,6 +31,7 @@ import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitData
 import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths';
 import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
 import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
+import { IExtHostNodeProxy } from 'vs/server/browser/extHostNodeProxy';
 import { IExtHostTunnelService } from 'vs/workbench/api/common/extHostTunnelService';
 import { IExtHostTerminalService } from 'vs/workbench/api/common/extHostTerminalService';
 import { Emitter, Event } from 'vs/base/common/event';
@@ -82,6 +83,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
 	protected readonly _extHostWorkspace: ExtHostWorkspace;
 	protected readonly _extHostConfiguration: ExtHostConfiguration;
 	protected readonly _logService: ILogService;
+	protected readonly _nodeProxy: IExtHostNodeProxy;
 	protected readonly _extHostTunnelService: IExtHostTunnelService;
 	protected readonly _extHostTerminalService: IExtHostTerminalService;
 
@@ -114,6 +116,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
 		@ILogService logService: ILogService,
 		@IExtHostInitDataService initData: IExtHostInitDataService,
 		@IExtensionStoragePaths storagePath: IExtensionStoragePaths,
+		@IExtHostNodeProxy nodeProxy: IExtHostNodeProxy,
 		@IExtHostTunnelService extHostTunnelService: IExtHostTunnelService,
 		@IExtHostTerminalService extHostTerminalService: IExtHostTerminalService
 	) {
@@ -125,6 +128,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
 		this._extHostWorkspace = extHostWorkspace;
 		this._extHostConfiguration = extHostConfiguration;
 		this._logService = logService;
+		this._nodeProxy = nodeProxy;
 		this._extHostTunnelService = extHostTunnelService;
 		this._extHostTerminalService = extHostTerminalService;
 		this._disposables = new DisposableStore();
@@ -355,7 +359,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
 
 		const activationTimesBuilder = new ExtensionActivationTimesBuilder(reason.startup);
 		return Promise.all([
-			this._loadCommonJSModule<IExtensionModule>(joinPath(extensionDescription.extensionLocation, entryPoint), activationTimesBuilder),
+			this._loadCommonJSModule<IExtensionModule>(joinPath(extensionDescription.extensionLocation, entryPoint), activationTimesBuilder, !extensionDescription.browser),
 			this._loadExtensionContext(extensionDescription)
 		]).then(values => {
 			return AbstractExtHostExtensionService._callActivate(this._logService, extensionDescription.identifier, values[0], values[1], activationTimesBuilder);
@@ -746,7 +750,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
 
 	protected abstract _beforeAlmostReadyToRunExtensions(): Promise<void>;
 	protected abstract _getEntryPoint(extensionDescription: IExtensionDescription): string | undefined;
-	protected abstract _loadCommonJSModule<T>(module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<T>;
+	protected abstract _loadCommonJSModule<T>(module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder, isRemote?: boolean): Promise<T>;
 	public abstract async $setRemoteEnvironment(env: { [key: string]: string | null }): Promise<void>;
 }
 
diff --git a/src/vs/workbench/api/node/extHost.node.services.ts b/src/vs/workbench/api/node/extHost.node.services.ts
index b3c89e51cfc25a53293a352a2a8ad50d5f26d595..e21abe4e13bc25a5b72f556bbfb61085842faeb7 100644
--- a/src/vs/workbench/api/node/extHost.node.services.ts
+++ b/src/vs/workbench/api/node/extHost.node.services.ts
@@ -3,6 +3,8 @@
  *  Licensed under the MIT License. See License.txt in the project root for license information.
  *--------------------------------------------------------------------------------------------*/
 
+import { IExtHostNodeProxy } from 'vs/server/browser/extHostNodeProxy';
+import { NotImplementedProxy } from 'vs/base/common/types';
 import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
 import { ExtHostOutputService2 } from 'vs/workbench/api/node/extHostOutputService';
 import { ExtHostTerminalService } from 'vs/workbench/api/node/extHostTerminalService';
@@ -36,3 +38,4 @@ registerSingleton(IExtHostSearch, NativeExtHostSearch);
 registerSingleton(IExtHostTask, ExtHostTask);
 registerSingleton(IExtHostTerminalService, ExtHostTerminalService);
 registerSingleton(IExtHostTunnelService, ExtHostTunnelService);
+registerSingleton(IExtHostNodeProxy, class extends NotImplementedProxy<IExtHostNodeProxy>(String(IExtHostNodeProxy)) { whenReady = Promise.resolve(); });
diff --git a/src/vs/workbench/api/worker/extHost.worker.services.ts b/src/vs/workbench/api/worker/extHost.worker.services.ts
index 3843fdec386edc09a1d361b63de892a04e0070ed..8aac4df527857e964798362a69f5591bef07c165 100644
--- a/src/vs/workbench/api/worker/extHost.worker.services.ts
+++ b/src/vs/workbench/api/worker/extHost.worker.services.ts
@@ -8,6 +8,7 @@ import { ILogService } from 'vs/platform/log/common/log';
 import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService';
 import { ExtHostExtensionService } from 'vs/workbench/api/worker/extHostExtensionService';
 import { ExtHostLogService } from 'vs/workbench/api/worker/extHostLogService';
+import { ExtHostNodeProxy, IExtHostNodeProxy } from 'vs/server/browser/extHostNodeProxy';
 
 // #########################################################################
 // ###                                                                   ###
@@ -17,3 +18,4 @@ import { ExtHostLogService } from 'vs/workbench/api/worker/extHostLogService';
 
 registerSingleton(IExtHostExtensionService, ExtHostExtensionService);
 registerSingleton(ILogService, ExtHostLogService);
+registerSingleton(IExtHostNodeProxy, ExtHostNodeProxy);
diff --git a/src/vs/workbench/api/worker/extHostExtensionService.ts b/src/vs/workbench/api/worker/extHostExtensionService.ts
index a6a149083719d7479268e24eb5339f6cbf93e655..360888dc7dff9437f6c85f7a2043ad9e7c4daf21 100644
--- a/src/vs/workbench/api/worker/extHostExtensionService.ts
+++ b/src/vs/workbench/api/worker/extHostExtensionService.ts
@@ -10,6 +10,7 @@ import { URI } from 'vs/base/common/uri';
 import { RequireInterceptor } from 'vs/workbench/api/common/extHostRequireInterceptor';
 import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
 import { ExtensionRuntime } from 'vs/workbench/api/common/extHostTypes';
+import { loadCommonJSModule } from 'vs/server/browser/worker';
 
 class WorkerRequireInterceptor extends RequireInterceptor {
 
@@ -44,10 +45,15 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService {
 	}
 
 	protected _getEntryPoint(extensionDescription: IExtensionDescription): string | undefined {
-		return extensionDescription.browser;
+		// NOTE@coder: We can support regular Node modules as well. These will just
+		// require the root of the extension.
+		return extensionDescription.browser || ".";
 	}
 
-	protected async _loadCommonJSModule<T>(module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<T> {
+	protected async _loadCommonJSModule<T>(module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder, isRemote?: boolean): Promise<T> {
+		if (isRemote) {
+			return loadCommonJSModule(module, activationTimesBuilder, this._nodeProxy, this._logService, this._fakeModules!.getModule('vscode', module));
+		}
 
 		module = module.with({ path: ensureSuffix(module.path, '.js') });
 		const response = await fetch(module.toString(true));
diff --git a/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css b/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css
index ced2d815834e40a1543e80516472799075980733..dfcae73e8a042307600c67f163aa00ba9e0762f4 100644
--- a/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css
+++ b/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css
@@ -55,6 +55,10 @@
 	align-items: center;
 	justify-content: center;
 	order: -1;
+
+	/* NOTE@coder: Hide since it doesn't seem to do anything when used with
+	code-server except open the VS Code repository. */
+	display: none !important;
 }
 
 .monaco-workbench .activitybar > .content > .home-bar > .home-bar-icon-badge {
diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts
index 511d7376a2bfebde59b4c67fed54c39e9dd534c9..c7c45f8e4e4ffe56a8782f58af75c6a7835142cf 100644
--- a/src/vs/workbench/browser/web.main.ts
+++ b/src/vs/workbench/browser/web.main.ts
@@ -45,6 +45,7 @@ import { FileLogService } from 'vs/platform/log/common/fileLogService';
 import { toLocalISOString } from 'vs/base/common/date';
 import { isWorkspaceToOpen, isFolderToOpen } from 'vs/platform/windows/common/windows';
 import { getWorkspaceIdentifier } from 'vs/workbench/services/workspaces/browser/workspaces';
+import { initialize } from 'vs/server/browser/client';
 import { coalesce } from 'vs/base/common/arrays';
 import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider';
 import { WebResourceIdentityService, IResourceIdentityService } from 'vs/platform/resource/common/resourceIdentityService';
@@ -87,6 +88,8 @@ class BrowserMain extends Disposable {
 		// Startup
 		const instantiationService = workbench.startup();
 
+		await initialize(services.serviceCollection);
+
 		// Return API Facade
 		return instantiationService.invokeFunction(accessor => {
 			const commandService = accessor.get(ICommandService);
diff --git a/src/vs/workbench/common/resources.ts b/src/vs/workbench/common/resources.ts
index 18ea0bfedb4492327429a38237b05915b29f6dd0..d59a17c17f4fffa23d786ce36b4ff624d5688a58 100644
--- a/src/vs/workbench/common/resources.ts
+++ b/src/vs/workbench/common/resources.ts
@@ -15,6 +15,7 @@ import { ParsedExpression, IExpression, parse } from 'vs/base/common/glob';
 import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
 import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
 import { withNullAsUndefined } from 'vs/base/common/types';
+import { Schemas } from 'vs/base/common/network';
 
 export class ResourceContextKey extends Disposable implements IContextKey<URI> {
 
@@ -68,7 +69,8 @@ export class ResourceContextKey extends Disposable implements IContextKey<URI> {
 		if (!ResourceContextKey._uriEquals(this._resourceKey.get(), value)) {
 			this._contextKeyService.bufferChangeEvents(() => {
 				this._resourceKey.set(value);
-				this._schemeKey.set(value ? value.scheme : null);
+				// NOTE@coder: Fixes source control context menus (#1104).
+				this._schemeKey.set(value ? (value.scheme === Schemas.vscodeRemote ? Schemas.file : value.scheme) : null);
 				this._filenameKey.set(value ? basename(value) : null);
 				this._langIdKey.set(value ? this._modeService.getModeIdByFilepathOrFirstLine(value) : null);
 				this._extensionKey.set(value ? extname(value) : null);
diff --git a/src/vs/workbench/contrib/scm/browser/media/scm.css b/src/vs/workbench/contrib/scm/browser/media/scm.css
index b1838de8f21c60141d01cc424a5e000a32f1c828..0a480032e0cc8d5219cd240f8807aa317718659d 100644
--- a/src/vs/workbench/contrib/scm/browser/media/scm.css
+++ b/src/vs/workbench/contrib/scm/browser/media/scm.css
@@ -138,9 +138,11 @@
 	margin-right: 8px;
 }
 
-.scm-view .monaco-list .monaco-list-row .resource > .name > .monaco-icon-label > .actions {
-	flex-grow: 100;
-}
+/* NOTE@coder: Causes the label to shrink to zero width in Firefox due to
+ * overflow:hidden. This isn't right anyway, as far as I can tell. */
+/* .scm-view .monaco-list .monaco-list-row .resource > .name > .monaco-icon-label > .actions { */
+/* 	flex-grow: 100; */
+/* } */
 
 .scm-view .monaco-list .monaco-list-row .resource-group > .actions,
 .scm-view .monaco-list .monaco-list-row .resource > .name > .monaco-icon-label > .actions {
diff --git a/src/vs/workbench/services/dialogs/browser/dialogService.ts b/src/vs/workbench/services/dialogs/browser/dialogService.ts
index 1360c248eb7ff937c92d08bbf30d2b76ea606dc0..adccf8b88d62381c3ec484df40c6d63142ec9ef5 100644
--- a/src/vs/workbench/services/dialogs/browser/dialogService.ts
+++ b/src/vs/workbench/services/dialogs/browser/dialogService.ts
@@ -124,11 +124,12 @@ export class DialogService implements IDialogService {
 	async about(): Promise<void> {
 		const detailString = (useAgo: boolean): string => {
 			return nls.localize('aboutDetail',
-				"Version: {0}\nCommit: {1}\nDate: {2}\nBrowser: {3}",
+				"code-server: v{4}\n VS Code: v{0}\nCommit: {1}\nDate: {2}\nBrowser: {3}",
 				this.productService.version || 'Unknown',
 				this.productService.commit || 'Unknown',
 				this.productService.date ? `${this.productService.date}${useAgo ? ' (' + fromNow(new Date(this.productService.date), true) + ')' : ''}` : 'Unknown',
-				navigator.userAgent
+				navigator.userAgent,
+				this.productService.codeServerVersion || 'Unknown',
 			);
 		};
 
diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts
index 819607be0c13fed28eb7fbe6d4a62c0b860b1aa9..b046943311b713a579cc3a94983ea1b7fca7b9b1 100644
--- a/src/vs/workbench/services/environment/browser/environmentService.ts
+++ b/src/vs/workbench/services/environment/browser/environmentService.ts
@@ -116,8 +116,18 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment
 	@memoize
 	get logFile(): URI { return joinPath(this.options.logsPath, 'window.log'); }
 
+	// NOTE@coder: Use the regular path for extensions that write directly to disk
+	// instead of using the VS Code API.
 	@memoize
-	get userRoamingDataHome(): URI { return URI.file('/User').with({ scheme: Schemas.userData }); }
+	get userRoamingDataHome(): URI { return URI.file(this.userDataPath).with({ scheme: Schemas.userData }); }
+	@memoize
+	get userDataPath(): string {
+		const dataPath = this.payload?.get("userDataPath");
+		if (!dataPath) {
+			throw new Error("userDataPath was not provided to environment service");
+		}
+		return dataPath;
+	}
 
 	@memoize
 	get settingsResource(): URI { return joinPath(this.userRoamingDataHome, 'settings.json'); }
@@ -279,7 +289,12 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment
 						extensionHostDebugEnvironment.params.port = parseInt(value);
 						break;
 					case 'enableProposedApi':
-						extensionHostDebugEnvironment.extensionEnabledProposedApi = [];
+						try {
+							extensionHostDebugEnvironment.extensionEnabledProposedApi = JSON.parse(value);
+						} catch (error) {
+							console.error(error);
+							extensionHostDebugEnvironment.extensionEnabledProposedApi = [];
+						}
 						break;
 				}
 			}
diff --git a/src/vs/workbench/services/extensionManagement/common/extensionEnablementService.ts b/src/vs/workbench/services/extensionManagement/common/extensionEnablementService.ts
index 32f3dc52c1ff645df6471a03542d6ec3eb73a277..c2f4497d2eba13a771b2665ad58f12ecdfa7606a 100644
--- a/src/vs/workbench/services/extensionManagement/common/extensionEnablementService.ts
+++ b/src/vs/workbench/services/extensionManagement/common/extensionEnablementService.ts
@@ -205,7 +205,7 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench
 					}
 				}
 			}
-			return true;
+			return false; // NOTE@coder: Don't disable anything by extensionKind.
 		}
 		return false;
 	}
diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts
index a982b3ecc58c5a2f3a92be7b8cca3a1cacbb7d47..97f9bfcf0e679be683b1b09cd569149e7962f5ad 100644
--- a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts
+++ b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts
@@ -211,8 +211,11 @@ export class ExtensionManagementService extends Disposable implements IExtension
 		}
 
 		// Install Language pack on all servers
+		// NOTE@coder: It does not appear language packs can be installed on the web
+		// extension management server at this time. Filter out the web to fix this.
 		if (isLanguagePackExtension(manifest)) {
-			return Promise.all(this.servers.map(server => server.extensionManagementService.installFromGallery(gallery))).then(([local]) => local);
+			const servers = this.servers.filter(s => s !== this.extensionManagementServerService.webExtensionManagementServer);
+			return Promise.all(servers.map(server => server.extensionManagementService.installFromGallery(gallery))).then(([local]) => local);
 		}
 
 		// 1. Install on preferred location
@@ -245,6 +248,11 @@ export class ExtensionManagementService extends Disposable implements IExtension
 			return this.extensionManagementServerService.webExtensionManagementServer.extensionManagementService.installFromGallery(gallery);
 		}
 
+		// NOTE@coder: Fall back to installing on the remote server.
+		if (this.extensionManagementServerService.remoteExtensionManagementServer) {
+			return this.extensionManagementServerService.remoteExtensionManagementServer.extensionManagementService.installFromGallery(gallery);
+		}
+
 		if (this.extensionManagementServerService.remoteExtensionManagementServer) {
 			const error = new Error(localize('cannot be installed', "Cannot install '{0}' because this extension has defined that it cannot run on the remote server.", gallery.displayName || gallery.name));
 			error.name = INSTALL_ERROR_NOT_SUPPORTED;
diff --git a/src/vs/workbench/services/extensions/browser/extensionService.ts b/src/vs/workbench/services/extensions/browser/extensionService.ts
index 9e979d28691d0b0b26fde5e46b606731e31f3da5..dd31879c7dd899c73c4a1371996912f4513bfd0d 100644
--- a/src/vs/workbench/services/extensions/browser/extensionService.ts
+++ b/src/vs/workbench/services/extensions/browser/extensionService.ts
@@ -125,8 +125,10 @@ export class ExtensionService extends AbstractExtensionService implements IExten
 			this._remoteAgentService.getEnvironment(),
 			this._remoteAgentService.scanExtensions()
 		]);
-		localExtensions = this._checkEnabledAndProposedAPI(localExtensions);
 		remoteExtensions = this._checkEnabledAndProposedAPI(remoteExtensions);
+		// NOTE@coder: Include remotely hosted extensions that should run locally.
+		localExtensions = this._checkEnabledAndProposedAPI(localExtensions)
+			.concat(remoteExtensions.filter(ext => ext.extensionKind && (ext.extensionKind === "web" || ext.extensionKind.includes("web"))));
 
 		const remoteAgentConnection = this._remoteAgentService.getConnection();
 		this._runningLocation = _determineRunningLocation(this._productService, this._configService, localExtensions, remoteExtensions, Boolean(remoteEnv && remoteAgentConnection));
diff --git a/src/vs/workbench/services/extensions/common/extensionsUtil.ts b/src/vs/workbench/services/extensions/common/extensionsUtil.ts
index 65e532ee58dfc06ed944846d01b885cb8f260ebc..0b6282fde7ad03c7ea9872a777cbf487253abed1 100644
--- a/src/vs/workbench/services/extensions/common/extensionsUtil.ts
+++ b/src/vs/workbench/services/extensions/common/extensionsUtil.ts
@@ -37,7 +37,8 @@ export function canExecuteOnWorkspace(manifest: IExtensionManifest, productServi
 
 export function canExecuteOnWeb(manifest: IExtensionManifest, productService: IProductService, configurationService: IConfigurationService): boolean {
 	const extensionKind = getExtensionKind(manifest, productService, configurationService);
-	return extensionKind.some(kind => kind === 'web');
+	// NOTE@coder: Hardcode vim for now.
+	return extensionKind.some(kind => kind === 'web') || manifest.name === 'vim';
 }
 
 export function getExtensionKind(manifest: IExtensionManifest, productService: IProductService, configurationService: IConfigurationService): ExtensionKind[] {
diff --git a/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts b/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts
index 49542eda74c65e485272cd37d586911886aa3ad7..de0e2da0a4c2dca91dc7e0e48c28a8a75ca3e7d4 100644
--- a/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts
+++ b/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts
@@ -16,7 +16,7 @@ import { IInitData } from 'vs/workbench/api/common/extHost.protocol';
 import { MessageType, createMessageOfType, isMessageOfType, IExtHostSocketMessage, IExtHostReadyMessage, IExtHostReduceGraceTimeMessage } from 'vs/workbench/services/extensions/common/extensionHostProtocol';
 import { ExtensionHostMain, IExitFn } from 'vs/workbench/services/extensions/common/extensionHostMain';
 import { VSBuffer } from 'vs/base/common/buffer';
-import { IURITransformer, URITransformer, IRawURITransformer } from 'vs/base/common/uriIpc';
+import { IURITransformer, URITransformer } from 'vs/base/common/uriIpc';
 import { exists } from 'vs/base/node/pfs';
 import { realpath } from 'vs/base/node/extpath';
 import { IHostUtils } from 'vs/workbench/api/common/extHostExtensionService';
@@ -57,12 +57,13 @@ const args = minimist(process.argv.slice(2), {
 	const Module = require.__$__nodeRequire('module') as any;
 	const originalLoad = Module._load;
 
-	Module._load = function (request: string) {
+	Module._load = function (request: string, parent: object, isMain: boolean) {
 		if (request === 'natives') {
 			throw new Error('Either the extension or a NPM dependency is using the "natives" node module which is unsupported as it can cause a crash of the extension host. Click [here](https://go.microsoft.com/fwlink/?linkid=871887) to find out more');
 		}
 
-		return originalLoad.apply(this, arguments);
+		// NOTE@coder: Map node_module.asar requests to regular node_modules.
+		return originalLoad.apply(this, [request.replace(/node_modules\.asar(\.unpacked)?/, 'node_modules'), parent, isMain]);
 	};
 })();
 
@@ -135,8 +136,11 @@ function _createExtHostProtocol(): Promise<PersistentProtocol> {
 
 						// Wait for rich client to reconnect
 						protocol.onSocketClose(() => {
-							// The socket has closed, let's give the renderer a certain amount of time to reconnect
-							disconnectRunner1.schedule();
+							// NOTE@coder: Inform the server so we can manage offline
+							// connections there instead. Our goal is to persist connections
+							// forever (to a reasonable point) to account for things like
+							// hibernating overnight.
+							process.send!({ type: 'VSCODE_EXTHOST_DISCONNECTED' });
 						});
 					}
 				}
@@ -313,11 +317,9 @@ export async function startExtensionHostProcess(): Promise<void> {
 
 	// Attempt to load uri transformer
 	let uriTransformer: IURITransformer | null = null;
-	if (initData.remote.authority && args.uriTransformerPath) {
+	if (initData.remote.authority) {
 		try {
-			const rawURITransformerFactory = <any>require.__$__nodeRequire(args.uriTransformerPath);
-			const rawURITransformer = <IRawURITransformer>rawURITransformerFactory(initData.remote.authority);
-			uriTransformer = new URITransformer(rawURITransformer);
+			uriTransformer = new URITransformer(initData.remote.authority);
 		} catch (e) {
 			console.error(e);
 		}
diff --git a/src/vs/workbench/services/extensions/worker/extensionHostWorkerMain.ts b/src/vs/workbench/services/extensions/worker/extensionHostWorkerMain.ts
index 79455414c06b95612a0dce2cad01f2bb2f40ef49..a407593b4dc6053309ed560898918cf67470e836 100644
--- a/src/vs/workbench/services/extensions/worker/extensionHostWorkerMain.ts
+++ b/src/vs/workbench/services/extensions/worker/extensionHostWorkerMain.ts
@@ -14,7 +14,11 @@
 
 	require.config({
 		baseUrl: monacoBaseUrl,
-		catchError: true
+		catchError: true,
+		paths: {
+			'@coder/node-browser': `../node_modules/@coder/node-browser/out/client/client.js`,
+			'@coder/requirefs': `../node_modules/@coder/requirefs/out/requirefs.js`,
+		}
 	});
 
 	require(['vs/workbench/services/extensions/worker/extensionHostWorker'], () => { }, err => console.error(err));
diff --git a/src/vs/workbench/services/localizations/electron-browser/localizationsService.ts b/src/vs/workbench/services/localizations/electron-browser/localizationsService.ts
index 44999bd842eae12b752b2e7e8c4904272b111dc1..601b1c5408835c743fe07e34da4d4534873bf832 100644
--- a/src/vs/workbench/services/localizations/electron-browser/localizationsService.ts
+++ b/src/vs/workbench/services/localizations/electron-browser/localizationsService.ts
@@ -5,17 +5,17 @@
 
 import { createChannelSender } from 'vs/base/parts/ipc/common/ipc';
 import { ILocalizationsService } from 'vs/platform/localizations/common/localizations';
-import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService';
 import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
+import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
 
 export class LocalizationsService {
 
 	declare readonly _serviceBrand: undefined;
 
 	constructor(
-		@ISharedProcessService sharedProcessService: ISharedProcessService,
+		@IRemoteAgentService remoteAgentService: IRemoteAgentService,
 	) {
-		return createChannelSender<ILocalizationsService>(sharedProcessService.getChannel('localizations'));
+		return createChannelSender<ILocalizationsService>(remoteAgentService.getConnection()!.getChannel('localizations'));
 	}
 }
 
diff --git a/src/vs/workbench/workbench.web.main.ts b/src/vs/workbench/workbench.web.main.ts
index f02bbbf874b5b18ac8d077ad56a8a4a57e77a4a6..86271940724aaf28e4eda93e59920820a7d93987 100644
--- a/src/vs/workbench/workbench.web.main.ts
+++ b/src/vs/workbench/workbench.web.main.ts
@@ -35,7 +35,8 @@ import 'vs/workbench/services/textfile/browser/browserTextFileService';
 import 'vs/workbench/services/keybinding/browser/keymapService';
 import 'vs/workbench/services/extensions/browser/extensionService';
 import 'vs/workbench/services/extensionManagement/common/extensionManagementServerService';
-import 'vs/workbench/services/telemetry/browser/telemetryService';
+// NOTE@coder: We send it all to the server side to be processed there instead.
+// import 'vs/workbench/services/telemetry/browser/telemetryService';
 import 'vs/workbench/services/configurationResolver/browser/configurationResolverService';
 import 'vs/workbench/services/credentials/browser/credentialsService';
 import 'vs/workbench/services/url/browser/urlService';
diff --git a/yarn.lock b/yarn.lock
index 140ed883c1a92ebcd7a284b98ca71261fa9cb631..b363d7de5000fd370bb4221f48e193382648a185 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -140,6 +140,23 @@
     lodash "^4.17.13"
     to-fast-properties "^2.0.0"
 
+"@coder/logger@^1.1.12":
+  version "1.1.12"
+  resolved "https://registry.yarnpkg.com/@coder/logger/-/logger-1.1.12.tgz#def113b7183abc35a8da2b57f0929f7e9626f4e0"
+  integrity sha512-oM0j3lTVPqApUm3e0bKKcXpfAiJEys31fgEfQlHmvEA13ujsC4zDuXnt0uzDtph48eMoNRLOF/EE4mNShVJKVw==
+
+"@coder/node-browser@^1.0.8":
+  version "1.0.8"
+  resolved "https://registry.yarnpkg.com/@coder/node-browser/-/node-browser-1.0.8.tgz#c22f581b089ad7d95ad1362fd351c57b7fbc6e70"
+  integrity sha512-NLF9sYMRCN9WK1C224pHax1Cay3qKypg25BhVg7VfNbo3Cpa3daata8RF/rT8JK3lPsu8PmFgDRQjzGC9X1Lrw==
+
+"@coder/requirefs@^1.1.5":
+  version "1.1.5"
+  resolved "https://registry.yarnpkg.com/@coder/requirefs/-/requirefs-1.1.5.tgz#259db370d563a79a96fb150bc9d69c7db6edc9fb"
+  integrity sha512-3jB47OFCql9+9FI6Vc4YX0cfFnG5rxBfrZUH45S4XYtYGOz+/Xl4h4d2iMk50b7veHkeSWGlB4VHC3UZ16zuYQ==
+  optionalDependencies:
+    jszip "2.6.0"
+
 "@electron/get@^1.0.1":
   version "1.7.2"
   resolved "https://registry.yarnpkg.com/@electron/get/-/get-1.7.2.tgz#286436a9fb56ff1a1fcdf0e80131fd65f4d1e0fd"
@@ -5375,6 +5392,13 @@ jsprim@^1.2.2:
     json-schema "0.2.3"
     verror "1.10.0"
 
+jszip@2.6.0:
+  version "2.6.0"
+  resolved "https://registry.yarnpkg.com/jszip/-/jszip-2.6.0.tgz#7fb3e9c2f11c8a9840612db5dabbc8cf3a7534b7"
+  integrity sha1-f7PpwvEciphAYS212rvIzzp1NLc=
+  dependencies:
+    pako "~1.0.0"
+
 just-debounce@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/just-debounce/-/just-debounce-1.0.0.tgz#87fccfaeffc0b68cd19d55f6722943f929ea35ea"
@@ -5955,26 +5979,11 @@ minimatch@0.3:
   dependencies:
     brace-expansion "^1.1.7"
 
-minimist@0.0.8:
-  version "0.0.8"
-  resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
-  integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=
-
-minimist@^1.2.0:
-  version "1.2.0"
-  resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
-  integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=
-
-minimist@^1.2.5:
+minimist@0.0.8, minimist@^1.2.0, minimist@^1.2.5, minimist@~0.0.1:
   version "1.2.5"
   resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
   integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
 
-minimist@~0.0.1:
-  version "0.0.10"
-  resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf"
-  integrity sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=
-
 minipass@^2.2.1, minipass@^2.3.3:
   version "2.3.3"
   resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.3.tgz#a7dcc8b7b833f5d368759cce544dccb55f50f233"
@@ -6716,6 +6725,11 @@ p-try@^2.0.0:
   resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.0.0.tgz#85080bb87c64688fa47996fe8f7dfbe8211760b1"
   integrity sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==
 
+pako@~1.0.0:
+  version "1.0.11"
+  resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf"
+  integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==
+
 pako@~1.0.5:
   version "1.0.6"
   resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.6.tgz#0101211baa70c4bca4a0f63f2206e97b7dfaf258"