From 549b37d6035250d387b1ecf641a95e2ee37472ed Mon Sep 17 00:00:00 2001 From: kasunben Date: Sun, 17 May 2026 22:57:45 +0200 Subject: [PATCH 1/3] refactor: update plugin names and manifest --- .../iframe/index.html | 0 .../iframe/runtime.js | 0 .../iframe/style.css | 0 .../manifest.json | 19 +++++++++---------- .../manifest.json | 14 +++++++------- .../manifest.json | 15 ++++++++------- .../manifest.json | 13 ++++++++----- .../src/index.tsx | 0 .../manifest.json | 14 ++++++++------ .../vite.config.ts | 3 +++ 10 files changed, 43 insertions(+), 35 deletions(-) rename plugins/{com.sovereign-demo.iframe-local => com.sovereign-demo.iframe-html}/iframe/index.html (100%) rename plugins/{com.sovereign-demo.iframe-local => com.sovereign-demo.iframe-html}/iframe/runtime.js (100%) rename plugins/{com.sovereign-demo.iframe-local => com.sovereign-demo.iframe-html}/iframe/style.css (100%) rename plugins/{com.sovereign-demo.external => com.sovereign-demo.iframe-html}/manifest.json (55%) rename plugins/{com.sovereign.launcher => com.sovereign-demo.internal-react}/manifest.json (53%) rename plugins/{com.sovereign.launcher => com.sovereign-demo.internal-react}/src/index.tsx (100%) diff --git a/plugins/com.sovereign-demo.iframe-local/iframe/index.html b/plugins/com.sovereign-demo.iframe-html/iframe/index.html similarity index 100% rename from plugins/com.sovereign-demo.iframe-local/iframe/index.html rename to plugins/com.sovereign-demo.iframe-html/iframe/index.html diff --git a/plugins/com.sovereign-demo.iframe-local/iframe/runtime.js b/plugins/com.sovereign-demo.iframe-html/iframe/runtime.js similarity index 100% rename from plugins/com.sovereign-demo.iframe-local/iframe/runtime.js rename to plugins/com.sovereign-demo.iframe-html/iframe/runtime.js diff --git a/plugins/com.sovereign-demo.iframe-local/iframe/style.css b/plugins/com.sovereign-demo.iframe-html/iframe/style.css similarity index 100% rename from plugins/com.sovereign-demo.iframe-local/iframe/style.css rename to plugins/com.sovereign-demo.iframe-html/iframe/style.css diff --git a/plugins/com.sovereign-demo.external/manifest.json b/plugins/com.sovereign-demo.iframe-html/manifest.json similarity index 55% rename from plugins/com.sovereign-demo.external/manifest.json rename to plugins/com.sovereign-demo.iframe-html/manifest.json index c68f772..10dd99f 100644 --- a/plugins/com.sovereign-demo.external/manifest.json +++ b/plugins/com.sovereign-demo.iframe-html/manifest.json @@ -1,9 +1,13 @@ { "schemaVersion": 1, - "id": "com.sovereign-demo.external", - "name": "External Demo", - "version": "0.0.0", - "runtime": "external", + "id": "com.sovereign-demo.iframe-html", + "name": "iframe Html Demo", + "version": "0.1.0", + "runtime": "iframe", + "runtimeConfig": { + "engine": "html", + "entrypoint": "iframe/index.html" + }, "permissions": [], "author": "Sovereign Core Team", "license": "AGPL-3.0", @@ -11,12 +15,7 @@ "minPlatformVersion": "0.1.0" }, "launch": { - "path": "/apps/com.sovereign.external-demo" - }, - "runtimeConfig": { - "external": { - "url": "https://example.com/" - } + "path": "/apps/com.sovereign-demo.iframe-html" }, "extensionPoints": { "launcher": true, diff --git a/plugins/com.sovereign-demo.iframe-local/manifest.json b/plugins/com.sovereign-demo.iframe-local/manifest.json index 90cf4da..49b8516 100644 --- a/plugins/com.sovereign-demo.iframe-local/manifest.json +++ b/plugins/com.sovereign-demo.iframe-local/manifest.json @@ -3,7 +3,12 @@ "id": "com.sovereign-demo.iframe-local", "name": "Iframe Demo", "version": "0.0.0", - "runtime": "iframe-local", + "runtime": "iframe", + "runtimeConfig": { + "engine": "*", + "host": "localhost", + "port": "8080" + }, "permissions": [], "author": "Sovereign Core Team", "license": "AGPL-3.0", @@ -11,12 +16,7 @@ "minPlatformVersion": "0.1.0" }, "launch": { - "path": "/apps/com.sovereign.iframe-demo" - }, - "runtimeConfig": { - "iframeLocal": { - "entrypoint": "iframe/index.html" - } + "path": "/apps/com.sovereign-demo.iframe-local" }, "extensionPoints": { "launcher": true, diff --git a/plugins/com.sovereign-demo.iframe-remote/manifest.json b/plugins/com.sovereign-demo.iframe-remote/manifest.json index f459fb1..3d82243 100644 --- a/plugins/com.sovereign-demo.iframe-remote/manifest.json +++ b/plugins/com.sovereign-demo.iframe-remote/manifest.json @@ -3,7 +3,13 @@ "id": "com.sovereign-demo.iframe-remote", "name": "Remote Demo", "version": "0.0.0", - "runtime": "iframe-remote", + "runtime": "iframe", + "runtimeConfig": { + "engine": "*", + "host": "example.com", + "https": true, + "uri": "/#test" + }, "permissions": [], "author": "Sovereign Core Team", "license": "AGPL-3.0", @@ -11,12 +17,7 @@ "minPlatformVersion": "0.1.0" }, "launch": { - "path": "/apps/com.sovereign.remote-demo" - }, - "runtimeConfig": { - "iframeRemote": { - "url": "https://example.com/" - } + "path": "/apps/com.sovereign-demo.iframe-remote" }, "extensionPoints": { "launcher": true, diff --git a/plugins/com.sovereign.launcher/manifest.json b/plugins/com.sovereign-demo.internal-react/manifest.json similarity index 53% rename from plugins/com.sovereign.launcher/manifest.json rename to plugins/com.sovereign-demo.internal-react/manifest.json index c830f0c..0bdef0c 100644 --- a/plugins/com.sovereign.launcher/manifest.json +++ b/plugins/com.sovereign-demo.internal-react/manifest.json @@ -1,12 +1,15 @@ { "schemaVersion": 1, - "id": "com.sovereign.launcher", + "id": "com.sovereign-demo.internal-react", "name": "Launcher", - "version": "0.0.0", - "runtime": "route-source", - "permissions": ["auth:profile"], + "version": "0.1.0", + "runtime": "internal", + "runtimeConfig": { + "engine": "react" + }, + "permissions": ["auth:read"], "launch": { - "path": "/apps/com.sovereign.launcher" + "path": "/apps/com.sovereign-demo.internal-react" }, "extensionPoints": { "launcher": true, diff --git a/plugins/com.sovereign.launcher/src/index.tsx b/plugins/com.sovereign-demo.internal-react/src/index.tsx similarity index 100% rename from plugins/com.sovereign.launcher/src/index.tsx rename to plugins/com.sovereign-demo.internal-react/src/index.tsx diff --git a/plugins/com.sovereign-demo.vite-react/manifest.json b/plugins/com.sovereign-demo.vite-react/manifest.json index 7d92f60..3ddbf69 100644 --- a/plugins/com.sovereign-demo.vite-react/manifest.json +++ b/plugins/com.sovereign-demo.vite-react/manifest.json @@ -2,9 +2,14 @@ "schemaVersion": 1, "id": "com.sovereign-demo.vite-react", "name": "Vite + React Demo", - "version": "0.0.0", - "runtime": "internal", - "permissions": ["auth:profile"], + "version": "0.1.0", + "runtime": "vite", + "runtimeConfig": { + "engine": "react", + "host": "localhost", + "port": "4000" + }, + "permissions": ["auth:read", "auth:write"], "author": "Sovereign Core Team", "license": "AGPL-3.0", "compatibility": { @@ -13,9 +18,6 @@ "launch": { "path": "/apps/com.sovereign-demo.vite-react" }, - "runtimeConfig": { - "engine": "vite:react-ts" - }, "extensionPoints": { "launcher": true, "sidebar": true diff --git a/plugins/com.sovereign-demo.vite-react/vite.config.ts b/plugins/com.sovereign-demo.vite-react/vite.config.ts index d1203cd..19c5f29 100644 --- a/plugins/com.sovereign-demo.vite-react/vite.config.ts +++ b/plugins/com.sovereign-demo.vite-react/vite.config.ts @@ -8,4 +8,7 @@ export default defineConfig({ react(), babel({ presets: [reactCompilerPreset()] }) ], + server: { + port: 4000 + } }) From 7de7ccd4d078311c2b807f17a36370d4c31cfbad Mon Sep 17 00:00:00 2001 From: kasunben Date: Sun, 17 May 2026 23:28:48 +0200 Subject: [PATCH 2/3] refactor: update manifest schema --- packages/manifest/schema/manifest.schema.json | 99 +++++++++---------- packages/manifest/src/index.ts | 27 +++-- packages/manifest/src/permissions.ts | 2 + .../[appId]/iframe/[[...assetPath]]/route.ts | 4 +- platform/src/launcher/get-installed-apps.ts | 3 +- platform/src/runtime/external-app-runtime.tsx | 32 ------ platform/src/runtime/iframe-local-runtime.tsx | 47 ++++++++- .../src/runtime/iframe-remote-runtime.tsx | 35 ------- platform/src/runtime/index.ts | 2 - platform/src/runtime/render-app-runtime.tsx | 31 +----- platform/src/sdk/create-app-sdk.ts | 15 ++- .../manifest.json | 2 +- .../manifest.json | 2 +- .../index.html | 30 ++++++ .../manifest.json | 24 +++++ .../style.css | 73 ++++++++++++++ .../manifest.json | 2 +- tools/generate-app-registry.ts | 8 +- 18 files changed, 255 insertions(+), 183 deletions(-) delete mode 100644 platform/src/runtime/external-app-runtime.tsx delete mode 100644 platform/src/runtime/iframe-remote-runtime.tsx create mode 100644 plugins/com.sovereign-demo.internal-html/index.html create mode 100644 plugins/com.sovereign-demo.internal-html/manifest.json create mode 100644 plugins/com.sovereign-demo.internal-html/style.css diff --git a/packages/manifest/schema/manifest.schema.json b/packages/manifest/schema/manifest.schema.json index 1461854..ee2660f 100644 --- a/packages/manifest/schema/manifest.schema.json +++ b/packages/manifest/schema/manifest.schema.json @@ -20,7 +20,7 @@ "version": { "type": "string" }, "runtime": { "type": "string", - "enum": ["internal", "route-source", "iframe-local", "iframe-remote", "external"] + "enum": ["internal", "vite", "iframe"] }, "permissions": { "type": "array", @@ -42,40 +42,35 @@ "properties": { "engine": { "type": "string", - "enum": ["vite:react-ts"] + "enum": ["react", "html", "*"] }, - "iframeLocal": { - "type": "object", - "required": ["entrypoint"], - "properties": { - "entrypoint": { - "type": "string", - "pattern": "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).+$" - } - }, - "additionalProperties": false + "host": { + "type": "string", + "minLength": 1 }, - "iframeRemote": { - "type": "object", - "required": ["url"], - "properties": { - "url": { + "port": { + "oneOf": [ + { "type": "string", - "pattern": "^https://.+" + "pattern": "^[0-9]+$" + }, + { + "type": "integer", + "minimum": 1, + "maximum": 65535 } - }, - "additionalProperties": false + ] }, - "external": { - "type": "object", - "required": ["url"], - "properties": { - "url": { - "type": "string", - "pattern": "^https://.+" - } - }, - "additionalProperties": false + "https": { + "type": "boolean" + }, + "uri": { + "type": "string", + "pattern": "^/" + }, + "entrypoint": { + "type": "string", + "pattern": "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$)).+$" } }, "additionalProperties": false @@ -124,7 +119,7 @@ { "if": { "properties": { - "runtime": { "const": "iframe-local" } + "runtime": { "const": "vite" } }, "required": ["runtime"] }, @@ -133,7 +128,7 @@ "properties": { "runtimeConfig": { "type": "object", - "required": ["iframeLocal"] + "required": ["engine", "host", "port"] } } } @@ -141,33 +136,29 @@ { "if": { "properties": { - "runtime": { "const": "iframe-remote" } - }, - "required": ["runtime"] - }, - "then": { - "required": ["runtimeConfig"], - "properties": { + "runtime": { "const": "iframe" }, "runtimeConfig": { "type": "object", - "required": ["iframeRemote"] + "required": ["entrypoint"] } - } - } - }, - { - "if": { - "properties": { - "runtime": { "const": "external" } }, - "required": ["runtime"] + "required": ["runtime", "runtimeConfig"] }, - "then": { - "required": ["runtimeConfig"], - "properties": { - "runtimeConfig": { - "type": "object", - "required": ["external"] + "then": true, + "else": { + "if": { + "properties": { + "runtime": { "const": "iframe" } + }, + "required": ["runtime"] + }, + "then": { + "required": ["runtimeConfig"], + "properties": { + "runtimeConfig": { + "type": "object", + "required": ["engine", "host"] + } } } } diff --git a/packages/manifest/src/index.ts b/packages/manifest/src/index.ts index 518f71b..a4b7494 100644 --- a/packages/manifest/src/index.ts +++ b/packages/manifest/src/index.ts @@ -5,10 +5,13 @@ export * from "./permissions"; export type SovereignRuntime = | "internal" - | "route-source" - | "iframe-local" - | "iframe-remote" - | "external"; + | "vite" + | "iframe"; + +export type SovereignRuntimeEngine = + | "react" + | "html" + | "*"; export interface SovereignAppManifest { schemaVersion: 1; @@ -21,16 +24,12 @@ export interface SovereignAppManifest { path: string; }; runtimeConfig?: { - engine?: "vite:react-ts"; - iframeLocal?: { - entrypoint: string; - }; - iframeRemote?: { - url: string; - }; - external?: { - url: string; - }; + engine?: SovereignRuntimeEngine; + host?: string; + port?: string | number; + https?: boolean; + uri?: string; + entrypoint?: string; }; extensionPoints?: { launcher?: boolean; diff --git a/packages/manifest/src/permissions.ts b/packages/manifest/src/permissions.ts index f07472d..ed657d2 100644 --- a/packages/manifest/src/permissions.ts +++ b/packages/manifest/src/permissions.ts @@ -1,5 +1,7 @@ export const SovereignPermissions = { AuthProfile: "auth:profile", + AuthRead: "auth:read", + AuthWrite: "auth:write", StorageReadWrite: "storage:readWrite", EventsPublish: "events:publish", NotificationsSend: "notifications:send", diff --git a/platform/app/api/apps/[appId]/iframe/[[...assetPath]]/route.ts b/platform/app/api/apps/[appId]/iframe/[[...assetPath]]/route.ts index f588946..298abe6 100644 --- a/platform/app/api/apps/[appId]/iframe/[[...assetPath]]/route.ts +++ b/platform/app/api/apps/[appId]/iframe/[[...assetPath]]/route.ts @@ -26,11 +26,11 @@ export async function GET(_request: Request, { params }: IframeAssetRouteProps) const { appId, assetPath = [] } = await params; const app = resolveApp(appId); - if (app?.runtime !== "iframe-local") { + if (!app || app.runtime !== "iframe") { notFound(); } - const entrypoint = app.runtimeConfig?.iframeLocal?.entrypoint; + const entrypoint = app.runtimeConfig?.entrypoint; if (!entrypoint || !isSafeRelativePath(entrypoint)) { notFound(); diff --git a/platform/src/launcher/get-installed-apps.ts b/platform/src/launcher/get-installed-apps.ts index 6b2ee26..fda72cf 100644 --- a/platform/src/launcher/get-installed-apps.ts +++ b/platform/src/launcher/get-installed-apps.ts @@ -1,6 +1,7 @@ import { installedApps } from "../../generated/apps.generated"; +import type { InstalledSovereignApp } from "../runtime"; -export function getInstalledApps() { +export function getInstalledApps(): readonly InstalledSovereignApp[] { return installedApps; } diff --git a/platform/src/runtime/external-app-runtime.tsx b/platform/src/runtime/external-app-runtime.tsx deleted file mode 100644 index d50558e..0000000 --- a/platform/src/runtime/external-app-runtime.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import type { InstalledSovereignApp } from "./types"; - -interface ExternalAppRuntimeProps { - app: InstalledSovereignApp; -} - -export function ExternalAppRuntime({ app }: ExternalAppRuntimeProps) { - const external = app.runtimeConfig?.external; - - if (!external || !isHttpsUrl(external.url)) { - return

External runtime URL not configured.

; - } - - return ( -
-

{app.name} runs outside the Sovereign runtime.

-

- - Open {app.name} - -

-
- ); -} - -function isHttpsUrl(input: string) { - try { - return new URL(input).protocol === "https:"; - } catch { - return false; - } -} diff --git a/platform/src/runtime/iframe-local-runtime.tsx b/platform/src/runtime/iframe-local-runtime.tsx index a066ee3..0f162fd 100644 --- a/platform/src/runtime/iframe-local-runtime.tsx +++ b/platform/src/runtime/iframe-local-runtime.tsx @@ -7,13 +7,31 @@ interface IframeLocalRuntimeProps { } export function IframeLocalRuntime({ app, appPath }: IframeLocalRuntimeProps) { - const iframeLocal = app.runtimeConfig?.iframeLocal; + const entrypoint = app.runtimeConfig?.entrypoint; - if (!iframeLocal) { - return

Iframe runtime entrypoint not configured.

; + if (!entrypoint) { + const remoteUrl = getRemoteIframeUrl(app); + + if (!remoteUrl) { + return

Iframe runtime URL not configured.

; + } + + return ( +