Skip to content

feat(plugins): let notarized third-party plugins install after you trust their developer - #2212

Merged
datlechin merged 2 commits into
mainfrom
feat/plugin-developer-trust
Aug 19, 2026
Merged

feat(plugins): let notarized third-party plugins install after you trust their developer#2212
datlechin merged 2 commits into
mainfrom
feat/plugin-developer-trust

Conversation

@datlechin

Copy link
Copy Markdown
Member

The problem

PluginCodeSignatureVerifier required every plugin bundle to be signed by TablePro's own Apple team:

anchor apple generic and certificate leaf[subject.OU] = "<TablePro's team id>"

with seven call sites and a DEBUG-only bypass. So a driver could only ever be published by the maintainers. It had to live under Plugins/ in this repo, be declared in project.yml, get a hand-written arm in build-plugin.yml, and be tagged here. Eight of the open issues are Database request: and none of them can be served by anyone else.

The registry README told contributors to "code-sign it with a valid Apple Developer certificate", which does not work: their Developer ID carries their team OU, not TablePro's, so the bundle is rejected at load time with "Bundle failed to load executable". Anyone who followed those instructions built something the app refuses to open. That README is fixed separately in TableProApp/plugins#5.

The evidence that the gate is the binding constraint, rather than a lack of interest, is a natural experiment this project already ran on itself. The linux branch has five Rust driver crates and no signing gate. Two outside contributors, with 4 and 2 lifetime commits between them, landed three drivers there in six weeks. The macOS registry, with the gate, has had zero third-party plugins ever.

What changes

A bundle now resolves to one of three outcomes instead of pass or fail:

Signature Result
TablePro's own team Loads, no prompt. Unchanged.
Developer ID, notarized Loads after the user trusts that developer by name
Unsigned, ad-hoc, tampered, expired, or any other certificate Refused, with no way to override

Trust is recorded per Apple Team ID rather than per plugin, so a developer's later plugins and updates install without asking again, and revoking one developer stops every plugin they signed. The team ID is assigned by Apple and cannot be chosen by the signer, so two developers can never collide and a rename cannot move trust between them.

The prompt says what is actually being granted: a driver runs as part of TablePro and can read the credentials of every connection you open.

Why this is safe

SecStaticCodeCheckValidity performs notarization checks by default. kSecCSNoNetworkAccess is the flag that turns them off, and a bundle whose notarization Apple revoked fails with errSecCSRevokedNotarization. So a Developer ID bundle that gets as far as the prompt is one Apple has seen and has not revoked.

The Developer ID requirement string uses 1.2.840.113635.100.6.1.13, Apple's Developer ID Application marker OID. That was verified rather than recalled: codesign -R against a real Developer ID signed app on disk matches it, and the same app fails TablePro's existing team-OU requirement.

Every call site is covered, and they are deliberately not all the same:

  • Load (PluginManager) and the validation helper enforce trust and never prompt, so revoking a developer stops their plugins loading on the next launch.
  • Committing a staged update enforces trust without prompting, since consent was taken when it was staged.
  • Registry install and both manual install paths prompt, because a manual install is the only place a user can grant trust. Declining aborts before anything is written to the plugins directory.

Notes

PluginDeveloperTrustStore is deliberately not @MainActor, unlike ExternalConnectionTrustStore which it otherwise mirrors. The plugin load path runs off the main actor and has to read trust before loading a bundle; the store wraps UserDefaults and holds no other state.

Themes are untouched. They are JSON with no executable, go through ThemeRegistryInstaller rather than this path, and are verified by SHA-256 against the manifest.

What this does not do

TableProPluginKit still ships inside this repository rather than as a versioned XCFramework, so a third-party driver still has to build against a checkout. That is the next step, and it is independent of this one.

Verification

  • build PASS
  • test PluginDeveloperTrustStoreTests, PluginSignatureTrustTests
  • lint TablePro
  • Settings > Plugins gained a Trusted Plugin Developers list with a per-developer Stop Trusting action, so the revoke path the docs describe exists rather than being implied.

@mintlify

mintlify Bot commented Aug 18, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
TablePro 🟢 Ready View Preview Aug 18, 2026, 8:26 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

Signed-off-by: Ngô Quốc Đạt <datlechin@gmail.com>
@datlechin
datlechin merged commit 702ba30 into main Aug 19, 2026
8 checks passed
@datlechin
datlechin deleted the feat/plugin-developer-trust branch August 19, 2026 02:07
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.

1 participant