Skip to content

Use secure websockets with mkcert#586

Merged
huntercaron merged 15 commits intomainfrom
huntercaron/mkcert-secure-ws
Mar 9, 2026
Merged

Use secure websockets with mkcert#586
huntercaron merged 15 commits intomainfrom
huntercaron/mkcert-secure-ws

Conversation

@huntercaron
Copy link
Collaborator

@huntercaron huntercaron commented Mar 6, 2026

Description

This PR adds TLS certificate generation and management to the Code Link CLI, enabling secure WebSocket (WSS) connections. Certificates are automatically generated on first run and cached locally in ~/.framer/code-link/. The CA is automatically installed to the system keychain on macOS with a one-time password prompt.

The certificate setup message has been improved for clarity and styled consistently with other status messages.

Changelog

  • Added mkcert integration for local certificate generation
  • Automatic CA installation to macOS system keychain
  • Certificate caching across CLI runs
  • Improved CLI messaging for certificate setup
  • Made TLS mandatory on the server (removed plain WS fallback that was incompatible with the WSS-only client)

Testing

  • Run the plugin in Safari and connect to the CLI — verify the connection works over WSS
  • Verify the CLI informs you why you are asked for your password (CA installation)

Safari silently blocks ws:// connections on HTTPS pages without firing
the close event, leaving the plugin stuck in "loading". Hardcode the
protocol to wss:// so failures increment failureCount normally and the
disconnected InfoPanel appears after 2 failures.
Generate self-signed certs via mkcert so the CLI serves WSS, matching
the plugin's wss:// protocol. Falls back to plain WS with a warning if
cert generation fails.
@github-actions github-actions bot added the Auto submit to Marketplace on merge Submits the plugin to the marketplace after merging label Mar 6, 2026
Remove ws:// fallback entirely — the CLI now requires TLS certs and
fails with a clear error message instead of silently downgrading. When
a new CA is generated (e.g. after the user deletes ca.key/ca.crt), the
old server cert and install marker are cleaned up so they get
regenerated against the new CA.
On Linux and Windows, tryInstallCA silently returned without warning,
leaving users with no explanation for why WSS connections fail. Now
shows platform-specific manual installation instructions.
@huntercaron huntercaron removed the Auto submit to Marketplace on merge Submits the plugin to the marketplace after merging label Mar 6, 2026
@huntercaron huntercaron changed the title Add mkcert TLS support to CLI for WSS connections Use secure websockets with mkcert Mar 6, 2026
Copy link

Copilot AI commented Mar 6, 2026

@Nick-Lucas I've opened a new pull request, #588, to work on those changes. Once the pull request is ready, I'll request review from you.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds TLS certificate generation/management to enable secure WebSocket (WSS) connections between the Framer plugin and the Code Link CLI, making TLS mandatory for local connections.

Changes:

  • Plugin switches client connections from ws:// to wss://.
  • CLI WebSocket server is now hosted over an HTTPS (TLS) server using locally generated certificates.
  • New CLI helper to download/run mkcert, cache certs, and (on macOS) install the CA into the system trust store.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 6 comments.

File Description
plugins/code-link/src/utils/sockets.ts Forces plugin WebSocket client to use wss:// and logs protocol on close.
packages/code-link-cli/src/helpers/connection.ts Wraps ws server with an HTTPS server to provide WSS-only connections.
packages/code-link-cli/src/helpers/certs.ts New mkcert-based certificate download/generation/cache helper for localhost TLS.
packages/code-link-cli/src/controller.ts Generates/loads certs before starting the WSS server; improves failure messaging.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@Nick-Lucas
Copy link
Contributor

@cursor review

- Verify mkcert binary integrity via hardcoded SHA-256 checksums before execution
- Sync root CA from mkcert default CAROOT and invalidate stale server certs on change
- Split cert generation into separate install + generate steps with better error messages
- Export CERT_DIR (overridable via FRAMER_CODE_LINK_CERT_DIR env var)
- Add WSS server error handler in connection helper
- Trim file header comment
@huntercaron
Copy link
Collaborator Author

@cursor review
@devin review
@copilot review

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Autofix Details

Bugbot Autofix prepared fixes for both issues found in the latest run.

  • ✅ Fixed: HTTPS runtime errors not forwarded to error handler
    • Added handlers.onError?.(err) to the HTTPS server runtime error path so connection-level error listeners now receive those errors.
  • ✅ Fixed: Test certificates already expired before PR merge
    • Replaced the embedded test key/certificate pair with a newly generated localhost certificate valid for ten years to avoid immediate expiration fragility.

Create PR

Or push these changes by commenting:

@cursor push 2987219ba8
Preview (2987219ba8)
diff --git a/packages/code-link-cli/src/helpers/connection.test.ts b/packages/code-link-cli/src/helpers/connection.test.ts
--- a/packages/code-link-cli/src/helpers/connection.test.ts
+++ b/packages/code-link-cli/src/helpers/connection.test.ts
@@ -4,53 +4,53 @@
 import { initConnection, sendMessage } from "./connection.ts"
 
 const TEST_KEY = `-----BEGIN PRIVATE KEY-----
-MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDrogGoQi38Qtul
-IzKaeMHoV9jJeyp3+hmQn7O5cQTxdjzYecqhMtUBvr3gklvSJgprDrGJ8tJSluHv
-g83lKVPTo0EVIHxvkxX886yQddyo4048tOBZCGkz2LXwZE3LKCRWQZeNUoSJn19L
-UPbavBoAM0/iosl+NAsAKL8dwT87i1m4jlojjRNLCzrpqt7VhKURaK85RJN8/Gv3
-CpZWhTIeSu8LyQNjA1PjxuntGTNLbnjoFLAzJ6pxX2MGuCppvuwzByY0XkChEaJ7
-P5nvWBVkr0wEVfYpXRgt4G9z2wuVAQsivjuo9jK6wtsJoOSp+05Js3cXdm5nJUij
-uo2vzUA9AgMBAAECggEADBjGlgFDxBoYlZs/e0+swMVVw0436W3lBxgzzVbghpbn
-28Mw5GKsLclBjThmT10VltZrxeW553SIh9fP565d99T/P9rpmH7IF7LYzpfGasM0
-nog4pkl4wSkkegFkPwRCDU2TvrUYScptRXwUGDmk6hK4TK3Hw1tfnzP4T8o+eUt+
-Za8MG8AeAMzBqumFQiAop6V0hdPhPLVeFGB2KMSXPzNt7HRgmIJtnLCi+lt0XaPR
-5j/fn6ZZKHsZBdsueNjqKrKBKgUyXdEPOruxHEfBnI7e9Uvmua0/IHUV51/41+D+
-Vp07I1YKsmhMvL5Id7GRoA54IPpnXy8amsmhZnkW0QKBgQD+Ezs9KYW7xJmT77IT
-2MIryNzepNZmQooyzNQ7+80eBGtL6INlnFqeexiP9jcSIqwomR/H95NAO68sSrxs
-yJnLMfHp5GN8X+htOZExlTHjFEEP8BIR4Pi1BhQR8oevFt3W/NqStx+obrkboxCk
-Yj+7uX3+Ss7i3ADt2op8GHlbCQKBgQDtawHKUmNthWWh+tUh6ExTLYa99GKur7DS
-gLjN1uBngo7cGVHJYiTp+iS7gqnE20ln7buVhc5os3F6fqPylb2Ig6NNsl9nAcjq
-GMonX+dug612il2IK+/pIcVp6R6xABv1cV0neZD+JU8q31pADcAdErGO6Dvt4Z5X
-N817Tu4klQKBgQD2gfAqwjuHVxLublPnX5ncY1CwD1wY8Rwmd4a+/+od4om7p0a8
-8jsVojbNjkQWK1+/L/meyPysCHxHy+cO4H4eoEGm/Tjs9hyKxJyzb55sRD1v2iud
-/xkugUw9sYKlhNkNelwSlut3Pp4IS2idJNnTKAAvFaOuhWe9XhiYmCI+CQKBgQDR
-odmL1uF6E/5gXwWQEfhKvXkrAr2btv/vbr8+6UttukcAKs8ffSxQ+JE0jDPw4RtY
-y/4FEYfmxQMfAPEsQnF/N5SbBzPb1SSdJ1RgHftQhq5Ea/oYQYttk2cnlDKIYStO
-tlFliJ6w+SqFFYAv7LREN3xWTdKUwdG4+0nRZik6XQKBgQDNFBL/NNy8f4jP2LNe
-Rr9yXnm7p7+ptHbIEaA6TmAqmBIgDWaxIyOgetPAs0VABtqeNk7CKSJyCDO3OGqe
-g2XehSba4rjQEs5W2z1/8zvVPn20856uVhFOMMV5a3Sr7tQ9iFQ19JiW8ACKll2L
-ThCKIGWM1NQFPq3sN0ooz4dKJA==
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDNdQNneJoOxRcf
+Pc8J1bv+S98RaiEtkMjvWYN8sn94sEvc03zZ1IGRryWAMfcirZPB8W3htHGAbrZW
+Bln/aFvrF/jBxzARPyejaB51EY1uDAjAlrz8aS6SsXs8xExVCMws8rWLxfAqp/+t
+qiPw9At7+iZs8uqKPuXF6qFCd0Am2CTtzjnQuvc9vI2RoaoHF/Hi0/VFqgUCWx8S
+HxO0VVHzPiVPqwWLlsLZnrU6swG1AVdIqxunbTY8GTyw2hUWwMNjkg/pxnsQWsbF
+QvOJ3SU0nW9ubKeDKjo3IARFGlFs+GstwZS9zfFGQ8Qq9w3NunTBghxpsVa8x3O4
+61r22r0zAgMBAAECggEAFKWfs/RAtx9DVZZmifx6qfGB90QNFYEpUDRUrGFbwgsl
+dImkUFe0tak+QYXD6i47XRAgFMCHb3qwxDBbNvmL0zJTg1W7FOCd4SQDe4xKBDcd
+Yg1D7LG9Rmyjj9XCPgFmU1YIvIUlv4OmhBNHDV3ZEZpv5h77Ru1PNfaAQietwLll
+0+wFA46bTEb4VrVyxCo5HL1h6ubjzOqhjRizFGVbVv5d5dz7rr7eWBRQ5vUeTb7v
+sWz8S6T1HA7IcKzsab3wt7CAu7NQ10Cy/iYw0GxPa1HuuBgKF/XXQfam23s0OI58
+17c4pkTjudrNl3BpqpwXP5z2OFL6QKoTmjisaYKbIQKBgQDnXQIGh0Dv+San14Mm
+sgPyTrdnfW3CFQbspAAGX1AK4+8ZczFp1+nsKVEolzOcfkDSusfuxCDmuw39uMqT
+7sQ9wSMj1n8MaK+8pVJ9lkIgHYSGArtaRmBOt0Qpo6CDk08UFhJTJbVxFE83Hv7J
+6EUzOTkom6c1rf7Y6RMUN04yWwKBgQDjVcxlre6vE12C7l6DlKGrgOPu3oP9H5w+
+kXvKMwfuWBXfTpBnZJdq5z1rPQjenHdiuRW3tU391kB6nfvfHmkbnFJz4V4cielM
+3vrXLmlZh+yZHRE59TEwnlYvmJxpB6gKvkfE6vw0Dt+pBiOZ/kRI7eJDcGSssaMz
+e6QWdtJoCQKBgQClowGbOOO1qfMRwInFfzayF8bYEYUtqK4fZr4Z0czVLx/zYXPj
+6c8v4tiWrcEmbYDtHQmcF8/SP+KhXdWyGQNnjskglCS69ouyC83F2XgX0+oXowcM
+d0vlrvDeuqVk7WZ04+RUoK+IeFjKBqe0V1SLepFAUEdid+H22s5s77dM9wKBgDhQ
+6diKdOVkC7dCq6E28bHVtSFYeCP9b6xESAE4EQ/nPEvcX4NtdOEjtaBaN1dGNYD8
+Trf6rv5h1AlMF7gXBXy0hex0+OZi96t2VXd25NwsAt2PSNQtvGjJ4Jnb2WtfXS6E
+Iq1SdPXEdX2eqrUcOvhCDdoc/qOVQdOgHm7+MnNZAoGAOwjsvqiOaJhP9bz/tC0b
+472giPzFxba1L19iSMfGeIJd/GDJKzW129sZEwUEfWkYVd0e1vhneKPBxtE1Z7+y
+35E71Q2ijmFPfd47y3pVgcWj1lPWCg9DAsrD/cqiJ/fIy0l90rSZYmUvG9G7qJQq
+/vDRn3clgb7L/ZEjeUeIEvY=
 -----END PRIVATE KEY-----
 `
 
 const TEST_CERT = `-----BEGIN CERTIFICATE-----
-MIIDCTCCAfGgAwIBAgIUZYxUt7lbGkNAA92GzPD0s5wFbvQwDQYJKoZIhvcNAQEL
-BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI2MDMwNjE0MTY1N1oXDTI2MDMw
-NzE0MTY1N1owFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF
-AAOCAQ8AMIIBCgKCAQEA66IBqEIt/ELbpSMymnjB6FfYyXsqd/oZkJ+zuXEE8XY8
-2HnKoTLVAb694JJb0iYKaw6xifLSUpbh74PN5SlT06NBFSB8b5MV/POskHXcqONO
-PLTgWQhpM9i18GRNyygkVkGXjVKEiZ9fS1D22rwaADNP4qLJfjQLACi/HcE/O4tZ
-uI5aI40TSws66are1YSlEWivOUSTfPxr9wqWVoUyHkrvC8kDYwNT48bp7RkzS254
-6BSwMyeqcV9jBrgqab7sMwcmNF5AoRGiez+Z71gVZK9MBFX2KV0YLeBvc9sLlQEL
-Ir47qPYyusLbCaDkqftOSbN3F3ZuZyVIo7qNr81APQIDAQABo1MwUTAdBgNVHQ4E
-FgQUOsJWop04UtmQdQVbThKWqiLU3eIwHwYDVR0jBBgwFoAUOsJWop04UtmQdQVb
-ThKWqiLU3eIwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAiphn
-7KISByG40QtIPSLltzQmkv2RPfpSq+LcNkdXmYEBbRh53a3L4JodHkl9Tmd98NI8
-wDPtqmA5A+L4Gc47v2O+b33IZ69g81JqYbjl2zNwYzCNEppirIfcyvalhFlly47A
-Z8HbtXQgF19BnBQ+DOTc0Xcbaxg7o5JoCSAs+t7e9kS9pdQ6Ak4vWZ6w75IgeVYp
-iaejAga8nqAw5JE4ORjnvJY5tNivOYRvslpYysLD4AW8twI52ZUqaRISZ4l+bYFI
-WvpV0rjmehRqHvyb06B91jDCy8oOb4WKnpBVHiSho5jLAEmuZn0GKrO7FpqQmUEW
-q3dVP+H9IROOHQsRwA==
+MIIDCTCCAfGgAwIBAgIUEm+yEgY9YJWp4K0j410Qi9Wyqe8wDQYJKoZIhvcNAQEL
+BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI2MDMwOTE0MjUzNVoXDTM2MDMw
+NjE0MjUzNVowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAzXUDZ3iaDsUXHz3PCdW7/kvfEWohLZDI71mDfLJ/eLBL
+3NN82dSBka8lgDH3Iq2TwfFt4bRxgG62VgZZ/2hb6xf4wccwET8no2gedRGNbgwI
+wJa8/GkukrF7PMRMVQjMLPK1i8XwKqf/raoj8PQLe/ombPLqij7lxeqhQndAJtgk
+7c450Lr3PbyNkaGqBxfx4tP1RaoFAlsfEh8TtFVR8z4lT6sFi5bC2Z61OrMBtQFX
+SKsbp202PBk8sNoVFsDDY5IP6cZ7EFrGxULzid0lNJ1vbmyngyo6NyAERRpRbPhr
+LcGUvc3xRkPEKvcNzbp0wYIcabFWvMdzuOta9tq9MwIDAQABo1MwUTAdBgNVHQ4E
+FgQULsidnLW2mOw3K4jNhopHvXW+6bQwHwYDVR0jBBgwFoAULsidnLW2mOw3K4jN
+hopHvXW+6bQwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAxmdG
+8RAHknfODzksEHK3iivJ/LqWMEh6bOsQqayD76ZaHSxRd56Y7kbxzPvjJkEkPDDM
+PcBpzr1IaEjd4Djq2AuwfCdelIqIWZNnkrwxf7dgxCCXczVuHQn3kjjNMo195wNH
+963DAtW++L5lpym27LSFP+Lh1YpR6Ft0ABUCO8j+hswnLG9kSm5WUyUkqbbVSlIS
+r42tr8yPxoypYLUz8MTLjphB+M3RAba/Wp4Qj/IaV25T3wM9yJAt5o/Dr/vnCDVx
+Ybg5PtiuknQYyq6Qn9nEAtewDC9CBV1BFbEOgR/sCsXWAm65cHbaBVdKSrH4rJOI
+AQ0TthT5VowpqXJdSw==
 -----END CERTIFICATE-----
 `
 

diff --git a/packages/code-link-cli/src/helpers/connection.ts b/packages/code-link-cli/src/helpers/connection.ts
--- a/packages/code-link-cli/src/helpers/connection.ts
+++ b/packages/code-link-cli/src/helpers/connection.ts
@@ -61,6 +61,7 @@
                 return
             }
             error(`WebSocket server error: ${err.message}`)
+            handlers.onError?.(err)
         }
 
         const handleListening = () => {
This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

mkcert creates rootCA-key.pem with 0o400 (read-only) permissions,
so fs.writeFile fails with EACCES when syncRootCA tries to update it.
Fix by removing the existing files before writing new ones.

Also switch help/recovery text from error() to info() and simplify
the port-in-use message.
@huntercaron huntercaron requested a review from Nick-Lucas March 9, 2026 16:45
Copy link
Contributor

@Nick-Lucas Nick-Lucas left a comment

Choose a reason for hiding this comment

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

Let's go!

@huntercaron huntercaron added this pull request to the merge queue Mar 9, 2026
Merged via the queue into main with commit 1a87ac0 Mar 9, 2026
7 of 8 checks passed
@huntercaron huntercaron deleted the huntercaron/mkcert-secure-ws branch March 9, 2026 17:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants