[feat] roadmap 13 batch M1 - open-core extension points (inert seams) - #38
Merged
Conversation
Add the seams a closed "cloud" plugin plugs into, so registration/rooms/roles can
live OUTSIDE this MIT repo. With no plugin configured every seam is INERT - the OSS
build behaves byte-for-byte as before.
- cloudHooks.js (store-only, no cycles): capability gate canApply(peerId, msgType)
with a pluggable provider (default allow-all) + an ALWAYS_ALLOWED floor of
connection/handshake types so a plugin can never brick the mesh; an auth provider
(authorize) accessor; connectSlot / usersSlot UI mount stores.
- peerHandler: ONE receive-side choke point at the top of conn.on('data')
(drop when !canApply); an auth hook in handleConnection that lets a provider
pre-approve known peers (default keeps the whitelist + approval flow).
- cloudPlugin.js: boot-time dynamic import of a plugin URL (VITE_CLOUD_PLUGIN or the
localStorage cloudPluginUrl dev override); hands it a small cloudApi
(setCapabilityProvider/setAuthProvider, appNotice, getPeers, mountConnect/
mountUsersSection, toast). Failure is caught + toasted; dynamic import only.
- CloudSlot.svelte: renders a plugin mount fn (el)=>cleanup into a DOM node, so a
separately-built plugin owns its own rendering. Mounted in Connect (login/browse)
and the Users popover (roles).
- static/cloud-plugin-example.js: dependency-free reference plugin (also the e2e
fixture) exercising every seam.
- OPEN-CORE.md: the plugin contract + rules.
- e2e open-core-m1: default path fully inert; the example plugin drives the gate,
auth, mount points and banner; unloading restores defaults.
svelte-check 499/77, build green. Two-peer replication regression is environment-
gated (hosts mapping for theprototype.app is disabled locally); default-path
inertness is proven single-page (canApply is unconditionally true with no provider).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A cloud plugin is a separate build with no access to the engine's stores, so it
needs its OWN way to replicate state across the mesh (roles map, room announces).
Add it to the cloudApi:
- sendCloud(payload) broadcasts { type:'cloud', payload } to peers.
- onCloudMessage(fn) receives them; core routes {type:'cloud'} in conn.on('data')
to cloudHooks.dispatchCloudMessage.
- 'cloud' joins the ALWAYS_ALLOWED floor so a plugin's own capability gate can
never drop its control channel.
Example plugin + OPEN-CORE.md + e2e updated (receives a dispatched cloud message).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Roadmap #13 Batch M1 — open-core extension points. The prerequisite for the closed cloud product (M2): the seams a cloud plugin plugs into, so registration / rooms / roles live outside this MIT repo. With no plugin configured, every seam is inert — the OSS build behaves byte-for-byte as before.
Seams
cloudHooks.canApply) — one receive-side choke point at the top ofconn.on('data')(peerHandler.svelte.js). Default allows everything; a plugin's provider drops a sender's disallowed message types (the receive-side layer roles enforcement needs). A smallALWAYS_ALLOWEDfloor (hosts / userdata / locked / theget*late-join requests) can't be gated, so a plugin can never brick the mesh; a throwing provider is treated as allow.authorize(peerId)) — inhandleConnection, lets a provider pre-approve known/authenticated peers, skipping the manual Approve. Default keeps the whitelist + approval flow byte-identical.import()of a plugin URL fromVITE_CLOUD_PLUGIN(prod) orlocalStorage.cloudPluginUrl(dev). Hands it a smallcloudApi. Load/throw is caught + toasted; dynamic import only (bundle + module-cycle rules).mountConnect/mountUsersSectionrender a plugin mount fn(el) => cleanupvia CloudSlot.svelte, decoupled from the app's framework version.appNotice(the first-run banner) is the shared-state seam.Included
tests/e2e/open-core-m1.test.cjs— default path fully inert; the example plugin drives the gate/auth/mounts/banner; unloading restores defaults. All checks PASS.Verification
svelte-check499/77 (baseline held) ·npm run buildgreen.canApplyis unconditionally true with no provider, so the gate early-return never fires). The two-peer replication regression is environment-gated locally (thetheprototype.apphosts mapping is currently disabled), not a code concern.Notes
tests/e2e/connect-overlay.test.cjsis a pre-existing stale test (queries.p-8, obsoleted by the roadmap Feature/properties #9 Connect rewrite) — untouched here.theprototype.app-cloudrepo — PocketBase (GitHub+Google OAuth, users/rooms/roles collections) + the client plugin (login, Browse Rooms, viewer/editor/admin with auto-promote).🤖 Generated with Claude Code