Something went wrong. Try again.
experiments in a post-browser web
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782/** * Generated by packages/capabilities/codegen.js from packages/capabilities/index.js. * DO NOT EDIT - regenerate with: yarn capabilities:codegen * * apps/server/ is subtree-split and deployed standalone, so it can't import * packages/ at runtime and carries its own copy instead (see the comment atop * apps/server/db.js). This file is a straight ESM->CommonJS syntax rewrite of * packages/capabilities/index.js — no capability is added, widened, narrowed * or removed by the projection. */
'use strict';/** * Tile Manifest Capability Model * * The capability vocabulary, manifest shape, detection/validation logic and * capability-resolution reduction moved out of `apps/desktop/main/tile-manifest.ts` * so both the desktop and `apps/server` can enforce the same declarations * instead of reimplementing them (see `docs/workers-capabilities.md` §3.1 and * §8.1 item 3). `apps/desktop/main/tile-manifest.ts` re-exports this package's * surface unchanged and keeps only `parseManifestFile()`, the one function * that touches the filesystem. * * Type declarations for this module live in `index.d.ts` — this file is * plain JavaScript, in the manner of `packages/sync`, because `apps/server` * is subtree-split and deployed standalone and cannot import `packages/` at * runtime; it consumes a generated CommonJS projection instead (see * `codegen.js` / `check-freshness.js`, following `packages/schema`'s * precedent for data — this one projects logic). */
// ─── Constants ───────────────────────────────────────────────────────
/** Maximum number of tiles per manifest */const MAX_TILES_PER_MANIFEST = 10;
/** Maximum number of network domains */const MAX_NETWORK_DOMAINS = 50;
/** Maximum number of datastore tables */const MAX_DATASTORE_TABLES = 20;
/** Capabilities that require explicit user approval (not auto-granted) */const PRIVILEGED_CAPABILITIES = new Set([ 'filesystem',]);
/** Capabilities that are restricted to built-in tiles only */const BUILTIN_ONLY_CAPABILITIES = new Set([ 'sync',]);
// ─── Detection ───────────────────────────────────────────────────────
/** * Detect whether a manifest JSON is legacy or a current tile manifest. * * The current tile manifest schema is version 3. Tile manifests are * identified by having: * - `manifestVersion: 3`, OR * - Both `tiles` array AND `capabilities` object present * * `manifestVersion: 2` is rejected — pre-v3 tile manifests carried the * `type` / `windowHints` compat fields that have since been removed; we * treat them as legacy and force authors to migrate (lockstep cutover). */function detectManifestVersion(json) { if (json.manifestVersion === 3) return 'tile'; if (json.manifestVersion === 2) return 'legacy'; if (Array.isArray(json.tiles) && typeof json.capabilities === 'object' && json.capabilities !== null) { return 'tile'; } return 'legacy';}
// ─── Validation ──────────────────────────────────────────────────────
/** * Validate a tile manifest * Returns array of errors (empty = valid) */function validateTileManifest(json) { const errors = [];
// Required fields if (!json.id || typeof json.id !== 'string') { errors.push({ path: 'id', message: 'id is required and must be a string' }); } if (!json.name || typeof json.name !== 'string') { errors.push({ path: 'name', message: 'name is required and must be a string' }); }
// Tiles array if (!Array.isArray(json.tiles)) { errors.push({ path: 'tiles', message: 'tiles must be an array' }); } else { if (json.tiles.length > MAX_TILES_PER_MANIFEST) { errors.push({ path: 'tiles', message: `maximum ${MAX_TILES_PER_MANIFEST} tiles per manifest` }); } const tileIds = new Set(); for (let i = 0; i < json.tiles.length; i++) { const tile = json.tiles[i]; const prefix = `tiles[${i}]`;
if (!tile.id || typeof tile.id !== 'string') { errors.push({ path: `${prefix}.id`, message: 'tile id is required' }); } else if (tileIds.has(tile.id)) { errors.push({ path: `${prefix}.id`, message: `duplicate tile id: ${tile.id}` }); } else { tileIds.add(tile.id); }
// `type` and `windowHints` were removed in manifestVersion 3 — reject // them so stale manifests fail loudly instead of silently dropping // their declared shape. if (tile.type !== undefined) { errors.push({ path: `${prefix}.type`, message: 'tile.type was removed in manifestVersion 3 — use `resident: true` instead of `type: "background"`, drop `type: "window"`/`"panel"`' }); } if (tile.windowHints !== undefined) { errors.push({ path: `${prefix}.windowHints`, message: 'tile.windowHints was removed in manifestVersion 3 — hoist its fields onto the tile entry directly' }); }
if (!tile.url || typeof tile.url !== 'string') { errors.push({ path: `${prefix}.url`, message: 'tile url is required' }); }
// Validate lazyEvents (optional array of strings) if (tile.lazyEvents !== undefined) { if (!Array.isArray(tile.lazyEvents)) { errors.push({ path: `${prefix}.lazyEvents`, message: 'lazyEvents must be an array of strings' }); } else { for (let j = 0; j < tile.lazyEvents.length; j++) { const ev = tile.lazyEvents[j]; if (typeof ev !== 'string' || ev.length === 0) { errors.push({ path: `${prefix}.lazyEvents[${j}]`, message: 'lazyEvents entries must be non-empty strings' }); } } } } } }
// Capabilities object if (typeof json.capabilities !== 'object' || json.capabilities === null) { errors.push({ path: 'capabilities', message: 'capabilities must be an object' }); } else { const caps = json.capabilities;
// Validate network domains if (caps.network && typeof caps.network === 'object') { const net = caps.network; if (Array.isArray(net.domains) && net.domains.length > MAX_NETWORK_DOMAINS) { errors.push({ path: 'capabilities.network.domains', message: `maximum ${MAX_NETWORK_DOMAINS} domains` }); } }
// Validate datastore tables if (caps.datastore && typeof caps.datastore === 'object') { const ds = caps.datastore; if (Array.isArray(ds.tables) && ds.tables.length > MAX_DATASTORE_TABLES) { errors.push({ path: 'capabilities.datastore.tables', message: `maximum ${MAX_DATASTORE_TABLES} tables` }); } }
// Validate pubsub scopes if (caps.pubsub && typeof caps.pubsub === 'object') { const ps = caps.pubsub; if (Array.isArray(ps.scopes)) { for (const scope of ps.scopes) { if (!['self', 'global', 'system'].includes(scope)) { errors.push({ path: 'capabilities.pubsub.scopes', message: `invalid scope: ${scope}` }); } } } }
// Validate shortcuts capability shape — must be boolean or { keys?: string[] } if (caps.shortcuts !== undefined) { const sc = caps.shortcuts; if (typeof sc === 'boolean') { // valid } else if (sc && typeof sc === 'object') { const scObj = sc; if (scObj.keys !== undefined) { if (!Array.isArray(scObj.keys)) { errors.push({ path: 'capabilities.shortcuts.keys', message: 'keys must be an array of strings' }); } else { for (let i = 0; i < scObj.keys.length; i++) { const k = scObj.keys[i]; if (typeof k !== 'string' || k.length === 0) { errors.push({ path: `capabilities.shortcuts.keys[${i}]`, message: 'keys entries must be non-empty strings' }); } } } } } else { errors.push({ path: 'capabilities.shortcuts', message: 'shortcuts must be boolean or { keys: string[] }' }); } }
// Validate context capability shape — must be boolean or // { read?: string[]; write?: string[]; modes?: boolean; queryWindows?: boolean } if (caps.context !== undefined) { const cc = caps.context; if (typeof cc === 'boolean') { // valid } else if (cc && typeof cc === 'object') { const ccObj = cc; for (const listKey of ['read', 'write']) { const v = ccObj[listKey]; if (v !== undefined) { if (!Array.isArray(v)) { errors.push({ path: `capabilities.context.${listKey}`, message: `${listKey} must be an array of strings` }); } else { for (let i = 0; i < v.length; i++) { const k = v[i]; if (typeof k !== 'string' || k.length === 0) { errors.push({ path: `capabilities.context.${listKey}[${i}]`, message: `${listKey} entries must be non-empty strings` }); } } } } } for (const flagKey of ['modes', 'queryWindows']) { if (ccObj[flagKey] !== undefined && typeof ccObj[flagKey] !== 'boolean') { errors.push({ path: `capabilities.context.${flagKey}`, message: `${flagKey} must be a boolean` }); } } } else { errors.push({ path: 'capabilities.context', message: 'context must be boolean or { read?, write?, modes?, queryWindows? }' }); } }
// Validate window capability shape — must be boolean or // { create?, query?, manage?: boolean; urls?: string[] } if (caps.window !== undefined) { const wc = caps.window; if (typeof wc === 'boolean') { // valid } else if (wc && typeof wc === 'object') { const wcObj = wc; for (const flagKey of ['create', 'query', 'manage']) { if (wcObj[flagKey] !== undefined && typeof wcObj[flagKey] !== 'boolean') { errors.push({ path: `capabilities.window.${flagKey}`, message: `${flagKey} must be a boolean` }); } } if (wcObj.urls !== undefined) { if (!Array.isArray(wcObj.urls)) { errors.push({ path: 'capabilities.window.urls', message: 'urls must be an array of strings' }); } else { for (let i = 0; i < wcObj.urls.length; i++) { const u = wcObj.urls[i]; if (typeof u !== 'string' || u.length === 0) { errors.push({ path: `capabilities.window.urls[${i}]`, message: 'urls entries must be non-empty strings' }); } } } } } else { errors.push({ path: 'capabilities.window', message: 'window must be boolean or { create?, query?, manage?, urls? }' }); } }
// Validate features capability shape — must be boolean or // { read?, install?, manage?, update?, dev?, publish?, devtools?, // browse?: boolean; sources?: FeatureSourceType[] } if (caps.features !== undefined) { const fc = caps.features; if (typeof fc === 'boolean') { // valid } else if (fc && typeof fc === 'object') { const fcObj = fc; for (const flagKey of [ 'read', 'install', 'manage', 'update', 'dev', 'publish', 'devtools', 'browse', ]) { if (fcObj[flagKey] !== undefined && typeof fcObj[flagKey] !== 'boolean') { errors.push({ path: `capabilities.features.${flagKey}`, message: `${flagKey} must be a boolean` }); } } if (fcObj.sources !== undefined) { if (!Array.isArray(fcObj.sources)) { errors.push({ path: 'capabilities.features.sources', message: 'sources must be an array of source type strings' }); } else { const valid = new Set(['builtin', 'local', 'atproto', 'url', 'dev']); for (let i = 0; i < fcObj.sources.length; i++) { const s = fcObj.sources[i]; if (typeof s !== 'string' || !valid.has(s)) { errors.push({ path: `capabilities.features.sources[${i}]`, message: 'sources entries must be one of: builtin, local, atproto, url, dev' }); } } } } } else { errors.push({ path: 'capabilities.features', message: 'features must be boolean or { read?, install?, manage?, update?, dev?, publish?, devtools?, browse?, sources? }' }); } }
// Validate izui capability shape — must be boolean if (caps.izui !== undefined) { if (typeof caps.izui !== 'boolean') { errors.push({ path: 'capabilities.izui', message: 'izui must be a boolean' }); } }
// Validate rules capability shape — boolean only. if (caps.rules !== undefined && typeof caps.rules !== 'boolean') { errors.push({ path: 'capabilities.rules', message: 'rules must be a boolean' }); }
// Validate settings capability shape — must be boolean or { readForeign?: string[] } if (caps.settings !== undefined) { const ss = caps.settings; if (typeof ss === 'boolean') { // valid } else if (ss && typeof ss === 'object') { const ssObj = ss; if (ssObj.readForeign !== undefined) { if (!Array.isArray(ssObj.readForeign)) { errors.push({ path: 'capabilities.settings.readForeign', message: 'readForeign must be an array of strings' }); } else { for (let i = 0; i < ssObj.readForeign.length; i++) { const k = ssObj.readForeign[i]; if (typeof k !== 'string' || k.length === 0) { errors.push({ path: `capabilities.settings.readForeign[${i}]`, message: 'readForeign entries must be non-empty strings' }); } } } } } else { errors.push({ path: 'capabilities.settings', message: 'settings must be boolean or { readForeign?: string[] }' }); } }
// Validate settingsForeign top-level fallback allowlist — optional // array of non-empty strings. Independent of settings shape so // authors using `settings: true` can still opt in to foreign reads. if (caps.settingsForeign !== undefined) { if (!Array.isArray(caps.settingsForeign)) { errors.push({ path: 'capabilities.settingsForeign', message: 'settingsForeign must be an array of strings' }); } else { for (let i = 0; i < caps.settingsForeign.length; i++) { const k = caps.settingsForeign[i]; if (typeof k !== 'string' || k.length === 0) { errors.push({ path: `capabilities.settingsForeign[${i}]`, message: 'settingsForeign entries must be non-empty strings' }); } } } }
// Validate oauth capability shape — must be boolean or { providers?: string[] } if (caps.oauth !== undefined) { const oc = caps.oauth; if (typeof oc === 'boolean') { // valid } else if (oc && typeof oc === 'object') { const ocObj = oc; if (ocObj.providers !== undefined) { if (!Array.isArray(ocObj.providers)) { errors.push({ path: 'capabilities.oauth.providers', message: 'providers must be an array of strings' }); } else { for (let i = 0; i < ocObj.providers.length; i++) { const p = ocObj.providers[i]; if (typeof p !== 'string' || p.length === 0) { errors.push({ path: `capabilities.oauth.providers[${i}]`, message: 'providers entries must be non-empty strings' }); } } } } } else { errors.push({ path: 'capabilities.oauth', message: 'oauth must be boolean or { providers?: string[] }' }); } }
// Validate session capability shape — boolean only for now. if (caps.session !== undefined && typeof caps.session !== 'boolean') { errors.push({ path: 'capabilities.session', message: 'session must be a boolean' }); }
// Validate dialogs capability shape — must be boolean or { types?: ('save'|'open')[] } if (caps.dialogs !== undefined) { const dc = caps.dialogs; if (typeof dc === 'boolean') { // valid } else if (dc && typeof dc === 'object') { const dcObj = dc; if (dcObj.types !== undefined) { if (!Array.isArray(dcObj.types)) { errors.push({ path: 'capabilities.dialogs.types', message: 'types must be an array of dialog type strings' }); } else { const valid = new Set(['save', 'open']); for (let i = 0; i < dcObj.types.length; i++) { const t = dcObj.types[i]; if (typeof t !== 'string' || !valid.has(t)) { errors.push({ path: `capabilities.dialogs.types[${i}]`, message: 'types entries must be one of: save, open' }); } } } } } else { errors.push({ path: 'capabilities.dialogs', message: 'dialogs must be boolean or { types: string[] }' }); } } }
// Rules validation — top-level array of TileRule. if (json.rules !== undefined) { if (!Array.isArray(json.rules)) { errors.push({ path: 'rules', message: 'rules must be an array' }); } else { for (let i = 0; i < json.rules.length; i++) { const r = json.rules[i]; const prefix = `rules[${i}]`; if (!r || typeof r !== 'object') { errors.push({ path: prefix, message: 'rule must be an object' }); continue; } if (!r.topic || typeof r.topic !== 'string') { errors.push({ path: `${prefix}.topic`, message: 'rule topic is required and must be a string' }); } if (!r.scriptId || typeof r.scriptId !== 'string') { errors.push({ path: `${prefix}.scriptId`, message: 'rule scriptId is required and must be a string' }); } if (r.match !== undefined && (typeof r.match !== 'object' || r.match === null || Array.isArray(r.match))) { errors.push({ path: `${prefix}.match`, message: 'match must be an object' }); } if (r.config !== undefined && (typeof r.config !== 'object' || r.config === null || Array.isArray(r.config))) { errors.push({ path: `${prefix}.config`, message: 'config must be an object' }); } if (r.display !== undefined && r.display !== null && (typeof r.display !== 'object' || Array.isArray(r.display))) { errors.push({ path: `${prefix}.display`, message: 'display must be an object or null' }); } if (r.enabled !== undefined && typeof r.enabled !== 'boolean') { errors.push({ path: `${prefix}.enabled`, message: 'enabled must be a boolean' }); } if (r.ordering !== undefined && typeof r.ordering !== 'number') { errors.push({ path: `${prefix}.ordering`, message: 'ordering must be a number' }); } } } }
// Shortcuts validation — top-level array of TileShortcut. Each entry // needs `keys` and exactly one wake target: `command` (command form) // OR `wakeTopic` (topic form). Additive: pre-existing `{keys, command}` // manifests stay valid. if (json.shortcuts !== undefined) { if (!Array.isArray(json.shortcuts)) { errors.push({ path: 'shortcuts', message: 'shortcuts must be an array' }); } else { for (let i = 0; i < json.shortcuts.length; i++) { const sc = json.shortcuts[i]; const prefix = `shortcuts[${i}]`; if (!sc || typeof sc !== 'object') { errors.push({ path: prefix, message: 'shortcut must be an object' }); continue; } if (!sc.keys || typeof sc.keys !== 'string') { errors.push({ path: `${prefix}.keys`, message: 'shortcut keys is required and must be a string' }); } const hasCommand = sc.command !== undefined; const hasTopic = sc.wakeTopic !== undefined; if (hasCommand && typeof sc.command !== 'string') { errors.push({ path: `${prefix}.command`, message: 'command must be a string' }); } if (hasTopic && typeof sc.wakeTopic !== 'string') { errors.push({ path: `${prefix}.wakeTopic`, message: 'wakeTopic must be a string' }); } if (!hasCommand && !hasTopic) { errors.push({ path: prefix, message: 'shortcut must declare a command or a wakeTopic' }); } if (hasCommand && hasTopic) { errors.push({ path: prefix, message: 'shortcut must declare exactly one of command or wakeTopic' }); } if (sc.wakeArgs !== undefined && !Array.isArray(sc.wakeArgs)) { errors.push({ path: `${prefix}.wakeArgs`, message: 'wakeArgs must be an array' }); } if (sc.global !== undefined && typeof sc.global !== 'boolean') { errors.push({ path: `${prefix}.global`, message: 'global must be a boolean' }); } } } }
// Commands validation if (json.commands !== undefined) { if (!Array.isArray(json.commands)) { errors.push({ path: 'commands', message: 'commands must be an array' }); } else { for (let i = 0; i < json.commands.length; i++) { const cmd = json.commands[i]; if (!cmd.name || typeof cmd.name !== 'string') { errors.push({ path: `commands[${i}].name`, message: 'command name is required' }); } if (!cmd.action || typeof cmd.action !== 'object') { errors.push({ path: `commands[${i}].action`, message: 'command action is required' }); } } } }
return errors;}
// ─── Parsing (non-filesystem) ────────────────────────────────────────
/** * Parse a manifest from a raw JSON object (for testing) */function parseManifestJSON(json) { const version = detectManifestVersion(json);
if (version === 'legacy') { return { version: 'legacy', raw: json }; }
const errors = validateTileManifest(json); if (errors.length > 0) { return null; }
const manifest = json; return { version: 'tile', raw: json, manifest };}
// ─── Capability Resolution ───────────────────────────────────────────
/** * Resolve capability grants for a tile * * Takes the requested capabilities from the manifest and determines * what to actually grant. Built-in tiles get broader access; external * tiles may have some capabilities denied. */function resolveCapabilities(tileId, requested, isBuiltin) { const granted = {}; const denied = [];
// PubSub: always grant self scope, grant global/system based on request if (requested.pubsub) { const grantedScopes = ['self']; for (const scope of requested.pubsub.scopes) { if (scope === 'system' && !isBuiltin) { denied.push('pubsub.system'); } else if (!grantedScopes.includes(scope)) { grantedScopes.push(scope); } } granted.pubsub = { scopes: grantedScopes, topics: requested.pubsub.topics, }; }
// Datastore: grant as requested (scoped to tile's tables) if (requested.datastore) { granted.datastore = { tables: requested.datastore.tables, createTables: requested.datastore.createTables, }; }
// Network: grant as requested (runtime enforces domain check) if (requested.network) { granted.network = { domains: requested.network.domains, }; }
// Window: preserve shape so strict IPC handlers can read the // create/query/manage flags and urls allowlist. Manifests may // declare `window: true` (treated as `{create, query, manage}` all // granted) OR an object form. if (requested.window) { if (requested.window === true) { granted.window = { create: true, query: true, manage: true }; } else if (typeof requested.window === 'object') { granted.window = { ...requested.window }; } }
// Filesystem: deny for non-builtin (privileged) if (requested.filesystem) { if (isBuiltin) { granted.filesystem = { ...requested.filesystem }; } else { denied.push('filesystem'); } }
// Commands: grant as requested if (requested.commands) { granted.commands = true; }
// Shortcuts: grant as requested. Preserve object shape so strict IPC // handlers can read the `keys` allowlist (manifests may declare // `shortcuts: true` OR `shortcuts: { keys: [...] }`). if (requested.shortcuts) { if (requested.shortcuts === true) { granted.shortcuts = true; } else if (typeof requested.shortcuts === 'object') { granted.shortcuts = { ...requested.shortcuts }; } }
// Sync: builtin only if (requested.sync) { if (isBuiltin) { granted.sync = true; } else { denied.push('sync'); } }
// Theme: grant as requested if (requested.theme) { granted.theme = true; }
// Settings: preserve object shape so the strict `tile:settings:get-foreign` // handler can read the `readForeign` allowlist. Manifests may declare // `settings: true` OR `settings: { readForeign: [...] }`. if (requested.settings) { if (requested.settings === true) { granted.settings = true; } else if (typeof requested.settings === 'object') { granted.settings = { ...requested.settings }; } }
// settingsForeign fallback allowlist — granted verbatim when present. // This piggybacks on the `settings` grant and is a no-op without it. if (Array.isArray(requested.settingsForeign) && requested.settingsForeign.length > 0) { granted.settingsForeign = [...requested.settingsForeign]; }
// OAuth: preserve object shape so the strict `tile:oauth:*` handlers // can read the `providers` allowlist. Manifests may declare // `oauth: true` OR `oauth: { providers: [...] }`. if (requested.oauth) { if (requested.oauth === true) { granted.oauth = true; } else if (typeof requested.oauth === 'object') { granted.oauth = { ...requested.oauth }; } }
// Context: preserve object shape so strict IPC handlers can read the // read/write allowlists and modes/queryWindows flags. Manifests may // declare `context: true` OR an object form. if (requested.context) { if (requested.context === true) { granted.context = true; } else if (typeof requested.context === 'object') { granted.context = { ...requested.context }; } }
// Dialogs: preserve object shape (types allowlist). Manifests may // declare `dialogs: true` OR `dialogs: { types: [...] }`. if (requested.dialogs) { if (requested.dialogs === true) { granted.dialogs = true; } else if (typeof requested.dialogs === 'object') { granted.dialogs = { ...requested.dialogs }; } }
// Features: preserve object shape (actions + sources allowlist). // Manifests may declare `features: true` OR an object form. if (requested.features) { if (requested.features === true) { granted.features = true; } else if (typeof requested.features === 'object') { granted.features = { ...requested.features }; } }
// Session: grant as requested (boolean-only for now). if (requested.session) { granted.session = true; }
// IZUI: boolean grant. Passes through 1:1. if (requested.izui) { granted.izui = true; }
// Rules: boolean grant. Lets a tile call api.rules.* (Stage 1 of // the rule engine — peek MCP plan 3d7d9239). if (requested.rules) { granted.rules = true; }
return { tileId, capabilities: granted, denied };}
// ─── Trusted Builtin Grant ──────────────────────────────────────────
/** * Build a capability grant that short-circuits every check with "allowed". * * This is used by the Electron glue that launches bundled CORE renderers * (cmd, hud, page, test fixtures) — the tile mechanics are identical to * any other tile, but capability enforcement is skipped because the * renderer ships with the app and is part of the trusted codebase. * * The returned grant carries `trustedBuiltin: true`. Every `check*Allowed` * pure-module helper AND every inline capability gate in `tile-ipc.ts` * checks this flag FIRST and returns `ok: true` when it is set — the * bypass is explicit at every site (grep for `trustedBuiltin` to audit). * * The `capabilities` object is populated with broad, permissive defaults * so that any code path that reads the shape directly (e.g. listing the * granted tables, topics) still sees reasonable values. Any allowlist * that could be non-exhaustive is omitted — when the bypass is * respected, the allowlist is never consulted. * * Core renderers (cmd, hud, page) participate in the same tile mechanics * as feature tiles, but do not require capability gating because they * are trusted bundled code. */function createTrustedBuiltinGrant(rendererId) { const capabilities = { // Pubsub — all scopes, no topic allowlist (every topic is allowed). pubsub: { scopes: ['self', 'global', 'system'] }, // Datastore — `tables: []` with no allowlist consulted at enforcement // (bypass returns ok before the allowlist is checked). Kept as empty // array to satisfy the shape. datastore: { tables: [], createTables: true }, // Network — no domain allowlist consulted. network: { domains: [] }, // Window — all actions. window: { create: true, query: true, manage: true }, // Filesystem — reserved permissive; in practice no trusted builtin // uses the tile filesystem IPC (they use Node fs directly via the // main process). Kept for shape consistency. filesystem: { read: ['/'], write: ['/'] }, commands: true, // Shortcuts — no keys allowlist consulted. shortcuts: true, sync: true, theme: true, // Settings — no readForeign allowlist (but settingsForeign: ['*'] // granted as well for defence-in-depth if foreign-read bypass is // ever reached through non-trustedBuiltin path). settings: { readForeign: ['*'] }, settingsForeign: ['*'], // Context — all actions, no key allowlists. context: { modes: true, queryWindows: true }, // Dialogs — all types. dialogs: { types: ['save', 'open'] }, // OAuth — any provider. oauth: { providers: ['*'] }, // Features — every action + every source type. features: { read: true, install: true, manage: true, update: true, dev: true, publish: true, devtools: true, browse: true, }, session: true, izui: true, rules: true, };
return { tileId: rendererId, capabilities, denied: [], trustedBuiltin: true, };}
module.exports = { detectManifestVersion, validateTileManifest, parseManifestJSON, resolveCapabilities, createTrustedBuiltinGrant };